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:
Jaco
2022-07-13 12:18:07 +03:00
committed by GitHub
parent 51e6e528ce
commit 551a081990
10 changed files with 86 additions and 74 deletions
+1
View File
@@ -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
+11 -12
View File
@@ -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)
)
)
);
+10 -12
View File
@@ -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);
})
)
);
+38 -3
View File
@@ -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)
]);
}
+4 -13
View File
@@ -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);
}
-1
View File
@@ -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 {