support manta author lookup (#5561)

* support manta author lookup

* handle scenarios where queuedKeys is not of the expected type

* add catchError to imports

* should it be square?

* resolve combineLatest deprecation issue

* type safe nimbus author mappings

* precise var names

* remove reference to deprecated combineLatest

* stay simple, that's what works

* implicit type for session.validators

we don't want to change this aspect of the implementation

* clearer comments on the code paths

* explicit variable names
This commit is contained in:
rob thijssen
2023-03-28 07:51:25 +03:00
committed by GitHub
parent 745a1b7838
commit 1a267c58f3
3 changed files with 44 additions and 21 deletions
+29 -18
View File
@@ -4,7 +4,7 @@
import type { Observable } from 'rxjs';
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 { AccountId, Address, BlockNumber, Header } from '@polkadot/types/interfaces';
import type { Codec, IOption } from '@polkadot/types/types';
import type { DeriveApi } from '../types.js';
@@ -24,23 +24,36 @@ export function createBlockNumberDerive <T extends { number: Compact<BlockNumber
}
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])
);
const validators = queryAt.session
? queryAt.session.validators()
: of(null);
if (mapId) {
// 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 && queryAt.authorMapping.mappingWithDeposit) {
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
)
validators,
queryAt.authorMapping.mappingWithDeposit<IOption<{ account: AccountId } & Codec>>(loggedAuthor)
.pipe(map((opt) => opt.unwrapOr({ account: null }).account))
]);
}
// fall back to session pallet, if available (ie: manta, calamari), to map session (nimbus) key to author (collator/validator) key
if (queryAt.session && queryAt.session.queuedKeys) {
return combineLatest([
of(header),
validators,
queryAt.session.queuedKeys<[AccountId, { nimbus: Address }][]>().pipe(
map((queuedKeys) => queuedKeys.find((sessionKey) => sessionKey[1].nimbus.toHex() === loggedAuthor.toHex())),
map((sessionKey) => (sessionKey) ? sessionKey[0] : null)
)
]);
}
@@ -49,9 +62,7 @@ export function getAuthorDetails (header: Header, queryAt: QueryableStorage<'rxj
// normal operation, non-mapping
return combineLatest([
of(header),
queryAt.session
? queryAt.session.validators()
: of(null),
validators,
of(null)
]);
}
@@ -4,7 +4,7 @@
/// <reference types="@polkadot/dev-test/globals.d.ts" />
import { TypeRegistry } from '../create/index.js';
import { CID_AURA, GenericConsensusEngineId as ConsensusEngineId } from './ConsensusEngineId.js';
import { CID_AURA, CID_NMBS, GenericConsensusEngineId as ConsensusEngineId } from './ConsensusEngineId.js';
describe('ConsensusEngineId', (): void => {
const registry = new TypeRegistry();
@@ -16,4 +16,8 @@ describe('ConsensusEngineId', (): void => {
it('reverses an id to string for babe', (): void => {
expect(new ConsensusEngineId(registry, 'BABE').toString()).toEqual('BABE');
});
it('creates a valid id for nimbus', (): void => {
expect(new ConsensusEngineId(registry, 'nmbs').toU8a()).toEqual(CID_NMBS);
});
});
@@ -11,6 +11,7 @@ export const CID_AURA = stringToU8a('aura');
export const CID_BABE = stringToU8a('BABE');
export const CID_GRPA = stringToU8a('FRNK');
export const CID_POW = stringToU8a('pow_');
export const CID_NMBS = stringToU8a('nmbs');
function getAuraAuthor (registry: Registry, bytes: Bytes, sessionValidators: AccountId[]): AccountId {
return sessionValidators[
@@ -77,6 +78,13 @@ export class GenericConsensusEngineId extends U8aFixed {
return this.eq(CID_POW);
}
/**
* @description `true` is the engine matches nimbus
*/
public get isNimbus (): boolean {
return this.eq(CID_NMBS);
}
/**
* @description From the input bytes, decode into an author
*/
@@ -89,8 +97,8 @@ export class GenericConsensusEngineId extends U8aFixed {
}
}
// For pow & Moonbeam, the bytes are the actual author
if (this.isPow || bytes.length === 20) {
// For pow & Nimbus, the bytes are the actual author
if (this.isPow || this.isNimbus) {
return getBytesAsAuthor(this.registry, bytes);
}