From 82cbd9727defd6cfee47bc3578ff0b020c0080f4 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 21 Dec 2018 11:12:41 +0100 Subject: [PATCH] Track Finalised events on api-rx (as per promise) (#500) * Track Finalised events on api-rx (as per promise) * Align naming of tracking helper * Remove wtf * Warn on extrinsic not found * Fix unused imports --- .../api/src/promise/SubmittableExtrinsic.ts | 24 +++++------ packages/api/src/rx/SubmittableExtrinsic.ts | 41 ++++++++++++++----- packages/api/src/types.ts | 8 +++- packages/api/src/util/filterEvents.ts | 27 ++++++++++++ packages/api/src/util/logging.ts | 9 ++++ 5 files changed, 84 insertions(+), 25 deletions(-) create mode 100644 packages/api/src/util/filterEvents.ts create mode 100644 packages/api/src/util/logging.ts diff --git a/packages/api/src/promise/SubmittableExtrinsic.ts b/packages/api/src/promise/SubmittableExtrinsic.ts index 3a6e4623e1..e33225f1a1 100644 --- a/packages/api/src/promise/SubmittableExtrinsic.ts +++ b/packages/api/src/promise/SubmittableExtrinsic.ts @@ -4,9 +4,12 @@ import { KeyringPair } from '@polkadot/keyring/types'; import { AnyNumber, AnyU8a } from '@polkadot/types/types'; -import { ApiPromiseInterface, SubmittableSendResult } from './types'; +import { SubmittableSendResult } from '../types'; +import { ApiPromiseInterface } from './types'; -import { EventRecord, Extrinsic, ExtrinsicStatus, Hash, Method, SignedBlock, u32 } from '@polkadot/types/index'; +import { EventRecord, Extrinsic, ExtrinsicStatus, Hash, Method, SignedBlock } from '@polkadot/types/index'; + +import filterEvents from '../util/filterEvents'; export default class SubmittableExtrinsic extends Extrinsic { private _api: ApiPromiseInterface; @@ -17,24 +20,17 @@ export default class SubmittableExtrinsic extends Extrinsic { this._api = api; } - private checkStatus (statusCb: (result: SubmittableSendResult) => any): (status: ExtrinsicStatus) => Promise { + // FIXME split into graph derivation once available + private trackStatus (statusCb: (result: SubmittableSendResult) => any): (status: ExtrinsicStatus) => Promise { return async (status: ExtrinsicStatus): Promise => { let events: Array | undefined = undefined; if (status.type === 'Finalised') { const blockHash = status.value as Hash; - const { block: { extrinsics } }: SignedBlock = await this._api.rpc.chain.getBlock(blockHash); + const signedBlock: SignedBlock = await this._api.rpc.chain.getBlock(blockHash); const allEvents: Array = await this._api.query.system.events.at(blockHash) as any; - const index = extrinsics - .map((ext) => ext.hash.toHex()) - .indexOf(this.hash.toHex()); - - if (index !== -1) { - events = allEvents.filter(({ phase }) => - phase.type === 'ApplyExtrinsic' && (phase.value as u32).eqn(index) - ); - } + events = filterEvents(this.hash, signedBlock, allEvents); } statusCb({ @@ -50,7 +46,7 @@ export default class SubmittableExtrinsic extends Extrinsic { return this._api.rpc.author.submitExtrinsic(this); } - return this._api.rpc.author.submitAndWatchExtrinsic(this, this.checkStatus(statusCb)); + return this._api.rpc.author.submitAndWatchExtrinsic(this, this.trackStatus(statusCb)); } sign (signerPair: KeyringPair, nonce: AnyNumber, blockHash?: AnyU8a): SubmittableExtrinsic { diff --git a/packages/api/src/rx/SubmittableExtrinsic.ts b/packages/api/src/rx/SubmittableExtrinsic.ts index ad03de655b..d0e3ff936f 100644 --- a/packages/api/src/rx/SubmittableExtrinsic.ts +++ b/packages/api/src/rx/SubmittableExtrinsic.ts @@ -4,11 +4,14 @@ import { KeyringPair } from '@polkadot/keyring/types'; import { AnyNumber, AnyU8a } from '@polkadot/types/types'; -import { ApiRxInterface, SubmittableSendResult } from './types'; +import { SubmittableSendResult } from '../types'; +import { ApiRxInterface } from './types'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; -import { Extrinsic, ExtrinsicStatus, Method } from '@polkadot/types/index'; +import { Observable, of, combineLatest } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import { Extrinsic, ExtrinsicStatus, Hash, Method } from '@polkadot/types/index'; + +import filterEvents from '../util/filterEvents'; export default class SubmittableExtrinsic extends Extrinsic { private _api: ApiRxInterface; @@ -19,15 +22,33 @@ export default class SubmittableExtrinsic extends Extrinsic { this._api = api; } + // FIXME split into graph derivation once available + private trackStatus = (status: ExtrinsicStatus): Observable => { + if (status.type !== 'Finalised') { + return of({ + status, + type: status.type + }); + } + + const blockHash = status.value as Hash; + + return combineLatest( + this._api.rpc.chain.getBlock(blockHash), + this._api.query.system.events.at(blockHash) + ).pipe( + map(([signedBlock, allEvents]) => ({ + events: filterEvents(this.hash, signedBlock, allEvents as any), + status, + type: status.type + })) + ); + } + send (): Observable { return this._api.rpc.author .submitAndWatchExtrinsic(this) - .pipe( - map((status: ExtrinsicStatus) => ({ - status, - type: status.type - })) - ); + .pipe(switchMap(this.trackStatus)); } sign (signerPair: KeyringPair, nonce: AnyNumber, blockHash?: AnyU8a): SubmittableExtrinsic { diff --git a/packages/api/src/types.ts b/packages/api/src/types.ts index 054b9d1f24..ec6a5ebb9f 100644 --- a/packages/api/src/types.ts +++ b/packages/api/src/types.ts @@ -4,7 +4,7 @@ import { ProviderInterface } from '@polkadot/rpc-provider/types'; import { RpcRxInterface$Events } from '@polkadot/rpc-rx/types'; -import { Hash, Metadata, RuntimeVersion } from '@polkadot/types/index'; +import { EventRecord, ExtrinsicStatus, Hash, Metadata, RuntimeVersion } from '@polkadot/types/index'; import { Constructor } from '@polkadot/types/types'; export type ApiInterface$Events = RpcRxInterface$Events | 'ready'; @@ -33,3 +33,9 @@ export interface ApiOptions { */ types?: {[name: string]: Constructor}; } + +export type SubmittableSendResult = { + events?: Array, + status: ExtrinsicStatus, + type: string +}; diff --git a/packages/api/src/util/filterEvents.ts b/packages/api/src/util/filterEvents.ts new file mode 100644 index 0000000000..51dc84fa5f --- /dev/null +++ b/packages/api/src/util/filterEvents.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2018 @polkadot/api 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 { EventRecord, Hash, SignedBlock, u32 } from '@polkadot/types/index'; + +import l from './logging'; + +export default function filterEvents (extHash: Hash, { block: { extrinsics, header } }: SignedBlock, allEvents: Array): Array | undefined { + // extrinsics to hashes + const myHash = extHash.toHex(); + const allHashes = extrinsics.map((ext) => ext.hash.toHex()); + + // find the index of our extrinsic in the block + const index = allHashes.indexOf(myHash); + + // if we do get the block after Finalised, it _should_ be there + if (index === -1) { + l.warn(`block ${header.hash}: Unable to find extrinsic ${myHash} inside ${allHashes}`); + return; + } + + return allEvents.filter(({ phase }) => + // only ApplyExtrinsic has the extrinsic index + phase.type === 'ApplyExtrinsic' && (phase.value as u32).eqn(index) + ); +} diff --git a/packages/api/src/util/logging.ts b/packages/api/src/util/logging.ts new file mode 100644 index 0000000000..abc23bc723 --- /dev/null +++ b/packages/api/src/util/logging.ts @@ -0,0 +1,9 @@ +// Copyright 2017-2018 @polkadot/api 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 { logger } from '@polkadot/util'; + +const l = logger('api/util'); + +export default l;