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
This commit is contained in:
Jaco Greeff
2019-02-11 12:51:41 +01:00
committed by GitHub
parent 2ce05019ce
commit 092129c28e
5 changed files with 75 additions and 34 deletions
+37 -19
View File
@@ -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<CodecResult, SubscriptionResult> 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<SubmittableResult> {
if (status.type !== 'Finalised') {
return of(
@@ -103,20 +110,37 @@ export default class SubmittableExtrinsic<CodecResult, SubscriptionResult> exten
);
}
private sendObservable (): Observable<Hash> {
return this._api.rpc.author.submitExtrinsic(this) as Observable<Hash>;
private sendObservable (updateId: number = -1): Observable<Hash> {
return (this._api.rpc.author
.submitExtrinsic(this) as Observable<Hash>)
.pipe(
tap((hash) => {
this.updateSigner(updateId, hash);
})
);
}
private subscribeObservable (): Observable<SubmittableResult> {
private subscribeObservable (updateId: number = -1): Observable<SubmittableResult> {
return (this._api.rpc.author
.submitAndWatchExtrinsic(this) as Observable<ExtrinsicStatus>)
.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<CodecResult, SubscriptionResult>;
send (statusCb: (result: SubmittableResult) => any): SumbitableResultSubscription<CodecResult, SubscriptionResult>;
send (statusCb?: (result: SubmittableResult) => any): SumbitableResultResult<CodecResult, SubscriptionResult> | SumbitableResultSubscription<CodecResult, SubscriptionResult> {
@@ -142,11 +166,7 @@ export default class SubmittableExtrinsic<CodecResult, SubscriptionResult> 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<CodecResult, SubscriptionResult> 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<CodecResult, SubscriptionResult> 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; // ???
})
),
[],
+1
View File
@@ -4,3 +4,4 @@
export { default as ApiPromise } from './promise';
export { default as ApiRx } from './rx';
export { default as SubmittableExtrinsic, SubmittableResult } from './SubmittableExtrinsic';
+10 -2
View File
@@ -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<CodecResult, SubscriptionResult> = (method: OnCallFunction<RxResult, RxResult>, params?: Array<CodecArg>, callback?: CodecCallback, needsCallback?: boolean) => CodecResult | SubscriptionResult;
@@ -142,5 +142,13 @@ export interface ApiBaseInterface<CodecResult, SubscriptionResult> extends Reado
}
export interface Signer {
sign (extrinsic: Extrinsic, address: string, opt: SignatureOptions): Promise<void>;
/**
* @description Signs an extrinsic, returning an id (>0) that can be used to retrieve updates
*/
sign (extrinsic: Extrinsic, address: string, options: SignatureOptions): Promise<number>;
/**
* @description Receives an update for the extrinsic signed by a `signer.sign`
*/
update?: (id: number, status: Hash | SubmittableResult) => void;
}
@@ -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);
}
}
@@ -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;
}
}