mirror of
https://github.com/pezkuwichain/pezkuwi-api.git
synced 2026-04-22 09:07:56 +00:00
113 lines
4.1 KiB
TypeScript
113 lines
4.1 KiB
TypeScript
// Copyright 2017-2025 @pezkuwi/api-derive authors & contributors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import type { Observable } from 'rxjs';
|
|
import type { QueryableStorage } from '@pezkuwi/api-base/types';
|
|
import type { Compact, Vec } from '@pezkuwi/types';
|
|
import type { AccountId, BlockNumber, Header } from '@pezkuwi/types/interfaces';
|
|
import type { PezpalletImOnlineSr25519AppSr25519Public } from '@pezkuwi/types/lookup';
|
|
import type { Codec, IOption } from '@pezkuwi/types/types';
|
|
import type { DeriveApi } from '../types.js';
|
|
|
|
import { combineLatest, map, mergeMap, of, switchMap } from 'rxjs';
|
|
|
|
import { memo, unwrapBlockNumber } from '../util/index.js';
|
|
|
|
export type BlockNumberDerive = (instanceId: string, api: DeriveApi) => () => Observable<BlockNumber>;
|
|
|
|
type OptionMapping = IOption<{ account: AccountId } & Codec>;
|
|
type OptionNimbus = IOption<{ nimbus: PezpalletImOnlineSr25519AppSr25519Public } & Codec>;
|
|
|
|
export function createBlockNumberDerive <T extends { number: Compact<BlockNumber> | BlockNumber }> (fn: (api: DeriveApi) => Observable<T>): BlockNumberDerive {
|
|
return (instanceId: string, api: DeriveApi) =>
|
|
memo(instanceId, () =>
|
|
fn(api).pipe(
|
|
map(unwrapBlockNumber)
|
|
)
|
|
);
|
|
}
|
|
|
|
/** @internal */
|
|
function getAuthorDetailsWithAt (header: Header, queryAt: QueryableStorage<'rxjs'>): Observable<[Header, Vec<AccountId> | null, AccountId | null]> {
|
|
const validators = queryAt.session?.validators
|
|
? queryAt.session.validators()
|
|
: of(null);
|
|
|
|
// nimbus consensus stores the session key of the block author in header logs
|
|
const { logs: [log] } = header.digest;
|
|
const loggedAuthor = (log && (
|
|
(log.isConsensus && log.asConsensus[0].isNimbus && log.asConsensus[1]) ||
|
|
(log.isPreRuntime && log.asPreRuntime[0].isNimbus && log.asPreRuntime[1])
|
|
));
|
|
|
|
if (loggedAuthor) {
|
|
// use the author mapping pallet, if available (ie: moonbeam, moonriver), to map session (nimbus) key to author (collator/validator) key
|
|
if (queryAt['authorMapping']?.['mappingWithDeposit']) {
|
|
return combineLatest([
|
|
of(header),
|
|
validators,
|
|
queryAt['authorMapping']['mappingWithDeposit']<OptionMapping>(loggedAuthor).pipe(
|
|
map((o) =>
|
|
o.unwrapOr({ account: null }).account
|
|
)
|
|
)
|
|
]);
|
|
}
|
|
|
|
// fall back to session and teyrchain staking pallets, if available (ie: manta, calamari), to map session (nimbus) key to author (collator) key
|
|
if (queryAt['teyrchainStaking']?.['selectedCandidates'] && queryAt.session?.nextKeys) {
|
|
const loggedHex = loggedAuthor.toHex();
|
|
|
|
return combineLatest([
|
|
of(header),
|
|
validators,
|
|
queryAt['teyrchainStaking']['selectedCandidates']<Vec<AccountId>>().pipe(
|
|
mergeMap((selectedCandidates) =>
|
|
combineLatest([
|
|
of(selectedCandidates),
|
|
queryAt.session.nextKeys.multi<OptionNimbus>(selectedCandidates).pipe(
|
|
map((nextKeys) =>
|
|
nextKeys.findIndex((o) =>
|
|
o.unwrapOrDefault().nimbus.toHex() === loggedHex
|
|
)
|
|
)
|
|
)
|
|
])
|
|
),
|
|
map(([selectedCandidates, index]) =>
|
|
index === -1
|
|
? null
|
|
: selectedCandidates[index]
|
|
)
|
|
)
|
|
]);
|
|
}
|
|
}
|
|
|
|
// normal operation, non-mapping
|
|
return combineLatest([
|
|
of(header),
|
|
validators,
|
|
of(null)
|
|
]);
|
|
}
|
|
|
|
export function getAuthorDetails (api: DeriveApi, header: Header, blockHash?: Uint8Array | string): Observable<[Header, Vec<AccountId> | null, AccountId | null]> {
|
|
// For on-chain state, we need to retrieve it as per the start
|
|
// of the block being constructed, i.e. session validators would
|
|
// be at the point of the block construction, not when all operations
|
|
// has been supplied.
|
|
//
|
|
// However for the first block (no parentHash available), we would
|
|
// just use the as-is
|
|
return api.queryAt(
|
|
header.parentHash.isEmpty
|
|
? blockHash || header.hash
|
|
: header.parentHash
|
|
).pipe(
|
|
switchMap((queryAt) =>
|
|
getAuthorDetailsWithAt(header, queryAt)
|
|
)
|
|
);
|
|
}
|