Support for Moonbeam author extraction in derive.chain.* (#5065)
* Support for Moonbeam author extraction in derive.chain.* * use unwrapOr * Adjust logic
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
Changes:
|
||||
|
||||
- Add `derive.chain.subscribeFinalized{Heads, Blocks}` which returns all finalized, no skips
|
||||
- Support Moonbeam author extraction on `derive.chain.*` (w/ shared logic extraction)
|
||||
|
||||
|
||||
## 8.12.2 Jul 10, 2022
|
||||
|
||||
@@ -9,6 +9,7 @@ import { combineLatest, map, of, switchMap } from 'rxjs';
|
||||
|
||||
import { createSignedBlockExtended } from '../type';
|
||||
import { memo } from '../util';
|
||||
import { getAuthorDetails } from './util';
|
||||
|
||||
/**
|
||||
* @name getBlock
|
||||
@@ -27,19 +28,17 @@ export function getBlock (instanceId: string, api: DeriveApi): (hash: Uint8Array
|
||||
return memo(instanceId, (blockHash: Uint8Array | string): Observable<SignedBlockExtended> =>
|
||||
combineLatest([
|
||||
api.rpc.chain.getBlock(blockHash),
|
||||
api.queryAt(blockHash).pipe(
|
||||
switchMap((queryAt) =>
|
||||
combineLatest([
|
||||
queryAt.system.events(),
|
||||
queryAt.session
|
||||
? queryAt.session.validators()
|
||||
: of([])
|
||||
])
|
||||
)
|
||||
)
|
||||
api.queryAt(blockHash)
|
||||
]).pipe(
|
||||
map(([signedBlock, [events, validators]]) =>
|
||||
createSignedBlockExtended(events.registry, signedBlock, events, validators)
|
||||
switchMap(([signedBlock, queryAt]) =>
|
||||
combineLatest([
|
||||
of(signedBlock),
|
||||
queryAt.system.events(),
|
||||
getAuthorDetails(signedBlock.block.header, queryAt)
|
||||
])
|
||||
),
|
||||
map(([signedBlock, events, [, validators, author]]) =>
|
||||
createSignedBlockExtended(events.registry, signedBlock, events, validators, author)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { AccountId } from '@polkadot/types/interfaces';
|
||||
import type { HeaderExtended } from '../type/types';
|
||||
import type { DeriveApi } from '../types';
|
||||
|
||||
import { combineLatest, map, of, switchMap } from 'rxjs';
|
||||
import { combineLatest, map, switchMap } from 'rxjs';
|
||||
|
||||
import { createHeaderExtended } from '../type';
|
||||
import { memo } from '../util';
|
||||
import { getAuthorDetails } from './util';
|
||||
|
||||
/**
|
||||
* @name getHeader
|
||||
@@ -29,16 +29,14 @@ export function getHeader (instanceId: string, api: DeriveApi): (blockHash: Uint
|
||||
return memo(instanceId, (blockHash: Uint8Array | string): Observable<HeaderExtended> =>
|
||||
combineLatest([
|
||||
api.rpc.chain.getHeader(blockHash),
|
||||
api.queryAt(blockHash).pipe(
|
||||
switchMap((queryAt) =>
|
||||
queryAt.session
|
||||
? queryAt.session.validators()
|
||||
: of([] as AccountId[])
|
||||
)
|
||||
)
|
||||
api.queryAt(blockHash)
|
||||
]).pipe(
|
||||
map(([header, validators]) =>
|
||||
createHeaderExtended(header.registry, header, validators)
|
||||
switchMap(([header, queryAt]) =>
|
||||
getAuthorDetails(header, queryAt)
|
||||
),
|
||||
map(([header, validators, author]) =>
|
||||
createHeaderExtended((validators || header).registry, header, validators, author)
|
||||
)
|
||||
));
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -41,8 +41,7 @@ export function subscribeFinalizedHeads (instanceId: string, api: DeriveApi): ()
|
||||
const endHash = prevHash;
|
||||
const startHash = header.parentHash;
|
||||
|
||||
prevHash = header.hash;
|
||||
header.createdAtHash = prevHash;
|
||||
prevHash = header.createdAtHash = header.hash;
|
||||
|
||||
return endHash === null || startHash.eq(endHash)
|
||||
? of(header)
|
||||
|
||||
@@ -5,9 +5,8 @@ import type { Observable } from 'rxjs';
|
||||
import type { SignedBlockExtended } from '../type/types';
|
||||
import type { DeriveApi } from '../types';
|
||||
|
||||
import { combineLatest, map, of, switchMap } from 'rxjs';
|
||||
import { switchMap } from 'rxjs';
|
||||
|
||||
import { createSignedBlockExtended } from '../type';
|
||||
import { memo } from '../util';
|
||||
|
||||
/**
|
||||
@@ -17,22 +16,8 @@ import { memo } from '../util';
|
||||
export function subscribeNewBlocks (instanceId: string, api: DeriveApi): () => Observable<SignedBlockExtended> {
|
||||
return memo(instanceId, (): Observable<SignedBlockExtended> =>
|
||||
api.derive.chain.subscribeNewHeads().pipe(
|
||||
switchMap((header) => {
|
||||
const blockHash = header.createdAtHash || header.hash;
|
||||
|
||||
// we get the block first, setting up the registry
|
||||
return combineLatest([
|
||||
of(header),
|
||||
api.rpc.chain.getBlock(blockHash),
|
||||
api.queryAt(blockHash).pipe(
|
||||
switchMap((queryAt) =>
|
||||
queryAt.system.events()
|
||||
)
|
||||
)
|
||||
]);
|
||||
}),
|
||||
map(([header, block, events]) =>
|
||||
createSignedBlockExtended(events.registry, block, events, header.validators)
|
||||
switchMap((header) =>
|
||||
api.derive.chain.getBlock(header.createdAtHash || header.hash)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -5,10 +5,11 @@ import type { Observable } from 'rxjs';
|
||||
import type { HeaderExtended } from '../type/types';
|
||||
import type { DeriveApi } from '../types';
|
||||
|
||||
import { combineLatest, map, of } from 'rxjs';
|
||||
import { combineLatest, map, of, switchMap } from 'rxjs';
|
||||
|
||||
import { createHeaderExtended } from '../type';
|
||||
import { memo } from '../util';
|
||||
import { getAuthorDetails } from './util';
|
||||
|
||||
/**
|
||||
* @name subscribeNewHeads
|
||||
@@ -25,16 +26,20 @@ import { memo } from '../util';
|
||||
*/
|
||||
export function subscribeNewHeads (instanceId: string, api: DeriveApi): () => Observable<HeaderExtended> {
|
||||
return memo(instanceId, (): Observable<HeaderExtended> =>
|
||||
combineLatest([
|
||||
api.rpc.chain.subscribeNewHeads(),
|
||||
api.query.session
|
||||
? api.query.session.validators()
|
||||
: of(undefined)
|
||||
]).pipe(
|
||||
map(([header, validators]): HeaderExtended => {
|
||||
api.rpc.chain.subscribeNewHeads().pipe(
|
||||
switchMap((header) =>
|
||||
combineLatest([
|
||||
of(header),
|
||||
api.queryAt(header.hash)
|
||||
])
|
||||
),
|
||||
switchMap(([header, queryAt]) =>
|
||||
getAuthorDetails(header, queryAt)
|
||||
),
|
||||
map(([header, validators, author]): HeaderExtended => {
|
||||
header.createdAtHash = header.hash;
|
||||
|
||||
return createHeaderExtended(header.registry, header, validators);
|
||||
return createHeaderExtended(header.registry, header, validators, author);
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { Compact } from '@polkadot/types';
|
||||
import type { BlockNumber } from '@polkadot/types/interfaces';
|
||||
import type { QueryableStorage } from '@polkadot/api-base/types';
|
||||
import type { Compact, Vec } from '@polkadot/types';
|
||||
import type { AccountId, BlockNumber, Header } from '@polkadot/types/interfaces';
|
||||
import type { Codec, IOption } from '@polkadot/types/types';
|
||||
import type { DeriveApi } from '../types';
|
||||
|
||||
import { map } from 'rxjs';
|
||||
import { combineLatest, map, of } from 'rxjs';
|
||||
|
||||
import { memo } from '../util';
|
||||
|
||||
@@ -20,3 +22,36 @@ export function unwrapBlockNumber <T extends { number: Compact<BlockNumber> }> (
|
||||
map((r) => r.number.unwrap())
|
||||
));
|
||||
}
|
||||
|
||||
export function getAuthorDetails (header: Header, queryAt: QueryableStorage<'rxjs'>): Observable<[Header, Vec<AccountId> | null, AccountId | null]> {
|
||||
// this is Moonbeam specific
|
||||
if (queryAt.authorMapping && queryAt.authorMapping.mappingWithDeposit) {
|
||||
const mapId = header.digest.logs[0] && (
|
||||
(header.digest.logs[0].isConsensus && header.digest.logs[0].asConsensus[1]) ||
|
||||
(header.digest.logs[0].isPreRuntime && header.digest.logs[0].asPreRuntime[1])
|
||||
);
|
||||
|
||||
if (mapId) {
|
||||
return combineLatest([
|
||||
of(header),
|
||||
queryAt.session
|
||||
? queryAt.session.validators()
|
||||
: of(null),
|
||||
queryAt.authorMapping.mappingWithDeposit<IOption<{ account: AccountId } & Codec>>(mapId).pipe(
|
||||
map((opt) =>
|
||||
opt.unwrapOr({ account: null }).account
|
||||
)
|
||||
)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// normal operation, non-mapping
|
||||
return combineLatest([
|
||||
of(header),
|
||||
queryAt.session
|
||||
? queryAt.session.validators()
|
||||
: of(null),
|
||||
of(null)
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -7,19 +7,17 @@ import type { HeaderExtended } from './types';
|
||||
|
||||
import { extractAuthor } from './util';
|
||||
|
||||
export function createHeaderExtended (registry: Registry, header?: Header, validators?: AccountId[]): HeaderExtended {
|
||||
export function createHeaderExtended (registry: Registry, header?: Header, validators?: AccountId[] | null, author?: AccountId | null): HeaderExtended {
|
||||
// an instance of the base extrinsic for us to extend
|
||||
const HeaderBase = registry.createClass('Header');
|
||||
|
||||
class Implementation extends HeaderBase implements HeaderExtended {
|
||||
readonly #author?: AccountId;
|
||||
readonly #validators?: AccountId[];
|
||||
|
||||
constructor (registry: Registry, header?: Header, validators?: AccountId[]) {
|
||||
constructor (registry: Registry, header?: Header, validators?: AccountId[] | null, author?: AccountId | null) {
|
||||
super(registry, header);
|
||||
|
||||
this.#author = extractAuthor(this.digest, validators);
|
||||
this.#validators = validators;
|
||||
this.#author = author || extractAuthor(this.digest, validators || []);
|
||||
this.createdAtHash = header?.createdAtHash;
|
||||
}
|
||||
|
||||
@@ -29,14 +27,7 @@ export function createHeaderExtended (registry: Registry, header?: Header, valid
|
||||
public get author (): AccountId | undefined {
|
||||
return this.#author;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Convenience method, returns the validators for the block
|
||||
*/
|
||||
public get validators (): AccountId[] | undefined {
|
||||
return this.#validators;
|
||||
}
|
||||
}
|
||||
|
||||
return new Implementation(registry, header, validators);
|
||||
return new Implementation(registry, header, validators, author);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ function mapExtrinsics (extrinsics: Extrinsic[], records: EventRecord[]): TxWith
|
||||
});
|
||||
}
|
||||
|
||||
export function createSignedBlockExtended (registry: Registry, block?: SignedBlock, events?: EventRecord[], validators?: AccountId[]): SignedBlockExtended {
|
||||
export function createSignedBlockExtended (registry: Registry, block?: SignedBlock, events?: EventRecord[], validators?: AccountId[] | null, author?: AccountId | null): SignedBlockExtended {
|
||||
// an instance of the base extrinsic for us to extend
|
||||
const SignedBlockBase = registry.createClass('SignedBlock');
|
||||
|
||||
@@ -40,10 +40,10 @@ export function createSignedBlockExtended (registry: Registry, block?: SignedBlo
|
||||
readonly #events: EventRecord[];
|
||||
readonly #extrinsics: TxWithEvent[];
|
||||
|
||||
constructor (registry: Registry, block?: SignedBlock, events?: EventRecord[], validators?: AccountId[]) {
|
||||
constructor (registry: Registry, block?: SignedBlock, events?: EventRecord[], validators?: AccountId[] | null, author?: AccountId | null) {
|
||||
super(registry, block);
|
||||
|
||||
this.#author = extractAuthor(this.block.header.digest, validators);
|
||||
this.#author = author || extractAuthor(this.block.header.digest, validators || []);
|
||||
this.#events = events || ([] as EventRecord[]);
|
||||
this.#extrinsics = mapExtrinsics(this.block.extrinsics, this.#events);
|
||||
this.createdAtHash = block?.createdAtHash;
|
||||
@@ -71,5 +71,5 @@ export function createSignedBlockExtended (registry: Registry, block?: SignedBlo
|
||||
}
|
||||
}
|
||||
|
||||
return new Implementation(registry, block, events, validators);
|
||||
return new Implementation(registry, block, events, validators, author);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import type { AccountId, DispatchError, DispatchInfo, Event, EventRecord, Extrin
|
||||
|
||||
export interface HeaderExtended extends Header {
|
||||
readonly author: AccountId | undefined;
|
||||
readonly validators: AccountId[] | undefined;
|
||||
}
|
||||
|
||||
export interface SignedBlockExtended extends SignedBlock {
|
||||
|
||||
Reference in New Issue
Block a user