From 092129c28e8fba64522d570eb9b6fe77e7080695 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Mon, 11 Feb 2019 12:51:41 +0100 Subject: [PATCH] Add Signer with status updates (#675) * Add Signer with status updates * Remove useless (now) updateId check * Don't send updates for non-owned extrinsics * opt -> options --- packages/api/src/SubmittableExtrinsic.ts | 56 ++++++++++++------- packages/api/src/index.ts | 1 + packages/api/src/types.ts | 12 +++- packages/api/test/util/SingleAccountSigner.js | 13 ----- packages/api/test/util/SingleAccountSigner.ts | 27 +++++++++ 5 files changed, 75 insertions(+), 34 deletions(-) delete mode 100644 packages/api/test/util/SingleAccountSigner.js create mode 100644 packages/api/test/util/SingleAccountSigner.ts diff --git a/packages/api/src/SubmittableExtrinsic.ts b/packages/api/src/SubmittableExtrinsic.ts index db1fac89ac..7609ec1920 100644 --- a/packages/api/src/SubmittableExtrinsic.ts +++ b/packages/api/src/SubmittableExtrinsic.ts @@ -5,12 +5,13 @@ import { KeyringPair } from '@polkadot/keyring/types'; import { Struct, Vector } from '@polkadot/types/codec'; import { AccountId, Address, Extrinsic, ExtrinsicStatus, EventRecord, Hash, Index, Method, RuntimeVersion, SignedBlock, Text } from '@polkadot/types/index'; +import { SignatureOptions } from '@polkadot/types/ExtrinsicSignature'; import { AnyNumber, AnyU8a, CodecCallback } from '@polkadot/types/types'; import { ApiInterface$Rx, ApiType, OnCallDefinition, Signer } from './types'; import { Observable, of, combineLatest } from 'rxjs'; -import { first, map, mergeMap, switchMap } from 'rxjs/operators'; -import { isBn, isFunction, isNumber, isUndefined, assert } from '@polkadot/util'; +import { first, map, mergeMap, switchMap, tap } from 'rxjs/operators'; +import { assert, isBn, isFunction, isNumber, isUndefined } from '@polkadot/util'; import filterEvents from './util/filterEvents'; @@ -77,6 +78,12 @@ export default class SubmittableExtrinsic exten this._onCall = onCall; } + private updateSigner (updateId: number, status: Hash | SubmittableResult): void { + if ((updateId !== -1) && this._api.signer && this._api.signer.update) { + this._api.signer.update(updateId, status); + } + } + private statusObservable (status: ExtrinsicStatus): Observable { if (status.type !== 'Finalised') { return of( @@ -103,20 +110,37 @@ export default class SubmittableExtrinsic exten ); } - private sendObservable (): Observable { - return this._api.rpc.author.submitExtrinsic(this) as Observable; + private sendObservable (updateId: number = -1): Observable { + return (this._api.rpc.author + .submitExtrinsic(this) as Observable) + .pipe( + tap((hash) => { + this.updateSigner(updateId, hash); + }) + ); } - private subscribeObservable (): Observable { + private subscribeObservable (updateId: number = -1): Observable { return (this._api.rpc.author .submitAndWatchExtrinsic(this) as Observable) .pipe( switchMap((status) => this.statusObservable(status) - ) + ), + tap((status) => { + this.updateSigner(updateId, status); + }) ); } + private expandOptions (options: SignatureOptionsPartial): SignatureOptions { + return { + blockHash: this._api.genesisHash, + version: this._api.runtimeVersion, + ...options + }; + } + send (): SumbitableResultResult; send (statusCb: (result: SubmittableResult) => any): SumbitableResultSubscription; send (statusCb?: (result: SubmittableResult) => any): SumbitableResultResult | SumbitableResultSubscription { @@ -142,11 +166,7 @@ export default class SubmittableExtrinsic exten ? { nonce: _options as any as number } : _options; - super.sign(account, { - blockHash: this._api.genesisHash, - version: this._api.runtimeVersion, - ...options - }); + super.sign(account, this.expandOptions(options)); return this; } @@ -165,6 +185,8 @@ export default class SubmittableExtrinsic exten const isSubscription = this._noStatusCb || !!statusCb; const isKeyringPair = isFunction((account as KeyringPair).address) && isFunction((account as KeyringPair).sign); const address = isKeyringPair ? (account as KeyringPair).address() : account.toString(); + let updateId: number | undefined; + return this._onCall( () => ( isUndefined(options.nonce) @@ -177,18 +199,14 @@ export default class SubmittableExtrinsic exten this.sign(account as KeyringPair, { ...options, nonce }); } else { assert(this._api.signer, 'no signer exists'); - await (this._api.signer as Signer).sign(this, address, { - blockHash: options.blockHash || this._api.genesisHash, - version: options.version || this._api.runtimeVersion, - era: options.era, - nonce - }); + + updateId = await (this._api.signer as Signer).sign(this, address, this.expandOptions({ ...options, nonce })); } }), switchMap(() => { return isSubscription - ? this.subscribeObservable() - : this.sendObservable() as any; // ??? + ? this.subscribeObservable(updateId) + : this.sendObservable(updateId) as any; // ??? }) ), [], diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index f77654e60b..7c149f0c34 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -4,3 +4,4 @@ export { default as ApiPromise } from './promise'; export { default as ApiRx } from './rx'; +export { default as SubmittableExtrinsic, SubmittableResult } from './SubmittableExtrinsic'; diff --git a/packages/api/src/types.ts b/packages/api/src/types.ts index 17639225ba..31ca24483b 100644 --- a/packages/api/src/types.ts +++ b/packages/api/src/types.ts @@ -12,7 +12,7 @@ import { MethodFunction } from '@polkadot/types/Method'; import { StorageFunction } from '@polkadot/types/StorageKey'; import { RxResult } from './rx/types'; -import SubmittableExtrinsic from './SubmittableExtrinsic'; +import SubmittableExtrinsic, { SubmittableResult } from './SubmittableExtrinsic'; import ApiBase from './Base'; export type OnCallDefinition = (method: OnCallFunction, params?: Array, callback?: CodecCallback, needsCallback?: boolean) => CodecResult | SubscriptionResult; @@ -142,5 +142,13 @@ export interface ApiBaseInterface extends Reado } export interface Signer { - sign (extrinsic: Extrinsic, address: string, opt: SignatureOptions): Promise; + /** + * @description Signs an extrinsic, returning an id (>0) that can be used to retrieve updates + */ + sign (extrinsic: Extrinsic, address: string, options: SignatureOptions): Promise; + + /** + * @description Receives an update for the extrinsic signed by a `signer.sign` + */ + update?: (id: number, status: Hash | SubmittableResult) => void; } diff --git a/packages/api/test/util/SingleAccountSigner.js b/packages/api/test/util/SingleAccountSigner.js deleted file mode 100644 index e808fc5043..0000000000 --- a/packages/api/test/util/SingleAccountSigner.js +++ /dev/null @@ -1,13 +0,0 @@ - -export default class SingleAccountSigner{ - constructor(keyringPair){ - this.keyringPair = keyringPair; - } - - async sign(extrinsic, address, opt) { - if(!this.keyringPair || String(address) !== this.keyringPair.address()){ - throw new Error('does not have the keyringPair'); - } - extrinsic.sign(this.keyringPair, opt); - } -} diff --git a/packages/api/test/util/SingleAccountSigner.ts b/packages/api/test/util/SingleAccountSigner.ts new file mode 100644 index 0000000000..379eb39ceb --- /dev/null +++ b/packages/api/test/util/SingleAccountSigner.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2019 @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 { KeyringPair } from '@polkadot/keyring/types'; +import { SignatureOptions } from '@polkadot/types/ExtrinsicSignature'; +import { Extrinsic } from '@polkadot/types/index'; + +let id = 0; + +export default class SingleAccountSigner { + private keyringPair: KeyringPair; + + constructor (keyringPair: KeyringPair) { + this.keyringPair = keyringPair; + } + + async sign (extrinsic: Extrinsic, address: string, options: SignatureOptions) { + if (!this.keyringPair || String(address) !== this.keyringPair.address()) { + throw new Error('does not have the keyringPair'); + } + + extrinsic.sign(this.keyringPair, options); + + return ++id; + } +}