api.derive.chain.getBlock(<hash>) with author getter (#2555)

* api.derive.chain.getBlock<hash>) with author getter

* Update CHANGELOG.md
This commit is contained in:
Jaco Greeff
2020-08-31 21:12:14 +02:00
committed by GitHub
parent c0c6928394
commit 6f092a1818
6 changed files with 142 additions and 23 deletions
+44
View File
@@ -0,0 +1,44 @@
// Copyright 2017-2020 @polkadot/api-derive authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { ApiInterfaceRx } from '@polkadot/api/types';
import { Observable, combineLatest, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { SignedBlockExtended } from '../type';
import { memo } from '../util';
/**
* @name getBlock
* @param {( Uint8Array | string )} hash - A block hash as U8 array or string.
* @description Get a specific block (e.g. rpc.chain.getBlock) and extend it with the author
* @example
* <BR>
*
* ```javascript
* const { author, number } = await api.derive.chain.getHeader('0x123...456');
*
* console.log(`block #${number} was authored by ${author}`);
* ```
*/
export function getHeader (api: ApiInterfaceRx): (hash: Uint8Array | string) => Observable<SignedBlockExtended | undefined> {
return memo((hash: Uint8Array | string): Observable<SignedBlockExtended | undefined> =>
combineLatest([
api.rpc.chain.getBlock(hash),
api.query.session
? api.query.session.validators.at(hash)
: of([])
]).pipe(
map(([signedBlock, validators]): SignedBlockExtended =>
new SignedBlockExtended(api.registry, signedBlock, validators)
),
catchError((): Observable<undefined> =>
// where rpc.chain.getHeader throws, we will land here - it can happen that
// we supplied an invalid hash. (Due to defaults, storeage will have an
// empty value, so only the RPC is affected). So return undefined
of()
)
));
}
+3 -23
View File
@@ -8,6 +8,8 @@ import { AnyJson, Constructor, Registry } from '@polkadot/types/types';
import runtimeTypes from '@polkadot/types/interfaces/runtime/definitions';
import { Struct } from '@polkadot/types';
import { extractAuthor } from './util';
// We can ignore the properties, added via Struct.with
const _Header = Struct.with(runtimeTypes.types.Header as any) as Constructor<Header>;
@@ -22,29 +24,7 @@ export default class HeaderExtended extends _Header {
constructor (registry: Registry, header?: Header, sessionValidators?: AccountId[]) {
super(registry, header);
this.#author = this._extractAuthor(sessionValidators);
}
private _extractAuthor (sessionValidators: AccountId[] = []): AccountId | undefined {
const [pitem] = this.digest.logs.filter(({ type }) => type === 'PreRuntime');
// extract from the substrate 2.0 PreRuntime digest
if (pitem) {
const [engine, data] = pitem.asPreRuntime;
return engine.extractAuthor(data, sessionValidators);
} else {
const [citem] = this.digest.logs.filter(({ type }) => type === 'Consensus');
// extract author from the consensus (substrate 1.0, digest)
if (citem) {
const [engine, data] = citem.asConsensus;
return engine.extractAuthor(data, sessionValidators);
}
}
return undefined;
this.#author = extractAuthor(this.digest, sessionValidators);
}
/**
@@ -0,0 +1,60 @@
// Copyright 2017-2020 @polkadot/api-derive authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { AccountId, SignedBlock } from '@polkadot/types/interfaces';
import { AnyJson, Constructor, Registry } from '@polkadot/types/types';
import runtimeTypes from '@polkadot/types/interfaces/runtime/definitions';
import { Struct } from '@polkadot/types';
import { extractAuthor } from './util';
// We can ignore the properties, added via Struct.with
const _SignedBlock = Struct.with(runtimeTypes.types.SignedBlock as any) as Constructor<SignedBlock>;
/**
* @name SignedBlockExtended
* @description
* A [[Block]] header with an additional `author` field that indicates the block author
*/
export default class BlockExtended extends _SignedBlock {
readonly #author?: AccountId;
constructor (registry: Registry, block?: SignedBlock, sessionValidators?: AccountId[]) {
super(registry, block);
this.#author = extractAuthor(this.block.header.digest, sessionValidators);
}
/**
* @description Convenience method, returns the author for the block
*/
public get author (): AccountId | undefined {
return this.#author;
}
/**
* @description Creates a human-friendly JSON representation
*/
public toHuman (isExtended?: boolean): AnyJson {
return {
...super.toHuman(isExtended) as { [index: string]: AnyJson },
author: this.author
? this.author.toHuman()
: undefined
};
}
/**
* @description Creates the JSON representation
*/
public toJSON (): AnyJson {
return {
...super.toJSON() as { [index: string]: AnyJson },
author: this.author
? this.author.toJSON()
: undefined
};
}
}
+1
View File
@@ -3,3 +3,4 @@
// of the Apache-2.0 license. See the LICENSE file for details.
export { default as HeaderExtended } from './HeaderExtended';
export { default as SignedBlockExtended } from './SignedBlockExtended';
+27
View File
@@ -0,0 +1,27 @@
// Copyright 2017-2020 @polkadot/api-derive authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { AccountId, Digest } from '@polkadot/types/interfaces';
export function extractAuthor (digest: Digest, sessionValidators: AccountId[] = []): AccountId | undefined {
const [pitem] = digest.logs.filter(({ type }) => type === 'PreRuntime');
// extract from the substrate 2.0 PreRuntime digest
if (pitem) {
const [engine, data] = pitem.asPreRuntime;
return engine.extractAuthor(data, sessionValidators);
} else {
const [citem] = digest.logs.filter(({ type }) => type === 'Consensus');
// extract author from the consensus (substrate 1.0, digest)
if (citem) {
const [engine, data] = citem.asConsensus;
return engine.extractAuthor(data, sessionValidators);
}
}
return undefined;
}