From 2eee6cfea72e655c8d8a18af0f32cde3f2358a09 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Tue, 9 Jul 2019 15:56:10 +0200 Subject: [PATCH] Swap to Mortal transactions by default (#1081) * Start of mortal tx * Swap to Mortal transcations by default * Update SubmittableExtrinsic.ts * WIP (pre master merge) * Remove eraLength, allow era to specify * Oops, linting.... it happens in the best of families * WIP debugging immortals * Re-surrect with working meta, and e2e tests * Update docs/examples/promise/07_make_transfer_with_allowed_block_permissions_only/index.js Co-Authored-By: Amaury Martiny * Update docs/examples/promise/07_make_transfer_with_allowed_block_permissions_only/index.js Co-Authored-By: Amaury Martiny * Remove useless birth/death calls --- .../index.js | 16 ++-- packages/api/src/SubmittableExtrinsic.ts | 78 ++++++++++++---- packages/api/test/e2e/promise-tx.spec.ts | 92 ++++++++++--------- .../src/json/SignedBlock.004.immortal.json | 1 + .../src/json/SignedBlock.004.mortal.json | 1 + packages/types/src/rpc/SignedBlock.spec.ts | 60 +++++------- packages/types/src/type/ExtrinsicEra.ts | 35 +++++-- packages/types/src/type/SignaturePayload.ts | 4 +- packages/types/src/types.ts | 2 +- 9 files changed, 170 insertions(+), 119 deletions(-) create mode 100644 packages/types/src/json/SignedBlock.004.immortal.json create mode 100644 packages/types/src/json/SignedBlock.004.mortal.json diff --git a/docs/examples/promise/07_make_transfer_with_allowed_block_permissions_only/index.js b/docs/examples/promise/07_make_transfer_with_allowed_block_permissions_only/index.js index 3170939941..776e295ccd 100644 --- a/docs/examples/promise/07_make_transfer_with_allowed_block_permissions_only/index.js +++ b/docs/examples/promise/07_make_transfer_with_allowed_block_permissions_only/index.js @@ -22,22 +22,18 @@ async function main () { // get current block const signedBlock = await api.rpc.chain.getBlock(); - // get current block height + // get current block height and hash const currentHeight = signedBlock.block.header.number; + const blockHash = signedBlock.block.header.hash; - const exERA = new ExtrinsicEra({ current: currentHeight, period: 10 }); - - // get the beggining of the current era - const eraBirth = exERA.asMortalEra.birth(currentHeight.toNumber()); - - // get the hash of the beginning of the current era - const eraHash = await api.rpc.chain.getBlockHash(eraBirth); + // construct a mortal era + const era = new ExtrinsicEra({ current: currentHeight, period: 10 }); // Create an extrinsic, transferring 12345 units to Bob const transfer = api.tx.balances.transfer(BOB, 12345); - // Sign and send the transaction using our account with a nonce and the length of blocks the transaction is valid for - const hash = await transfer.signAndSend(alice, { blockHash: eraHash, era: exERA, nonce }); + // Sign and send the transaction using our account with a nonce and the length of blocks the transaction is valid for + const hash = await transfer.signAndSend(alice, { blockHash, era, nonce }); console.log('Transfer sent with hash', hash.toHex()); } diff --git a/packages/api/src/SubmittableExtrinsic.ts b/packages/api/src/SubmittableExtrinsic.ts index bbcb014325..9780f11e29 100644 --- a/packages/api/src/SubmittableExtrinsic.ts +++ b/packages/api/src/SubmittableExtrinsic.ts @@ -2,11 +2,11 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { AccountId, Address, ExtrinsicStatus, EventRecord, getTypeRegistry, Hash, Index, Method, SignedBlock, Vector } from '@polkadot/types'; -import { Callback, Codec, IExtrinsic, IKeyringPair, SignatureOptions } from '@polkadot/types/types'; +import { AccountId, Address, ExtrinsicStatus, EventRecord, getTypeRegistry, Hash, Header, Index, Method, SignedBlock, Vector, ExtrinsicEra } from '@polkadot/types'; +import { AnyNumber, AnyU8a, Callback, Codec, IExtrinsic, IExtrinsicEra, IKeyringPair, SignatureOptions } from '@polkadot/types/types'; import { ApiInterface$Rx, ApiTypes, Signer } from './types'; -import { Observable, of, combineLatest } from 'rxjs'; +import { Observable, combineLatest, of } from 'rxjs'; import { first, map, mergeMap, switchMap, tap } from 'rxjs/operators'; import { assert, isBn, isFunction, isNumber, isUndefined } from '@polkadot/util'; @@ -38,6 +38,15 @@ type SubmittableResultValue = { status: ExtrinsicStatus; }; +type SignerOptions = { + blockHash: AnyU8a, + era?: IExtrinsicEra | number, + nonce: AnyNumber +}; + +// pick a default - in the case of 4s blocktimes, this translates to 60 seconds +const DEFAULT_MORTAL_LENGTH = 15; + export class SubmittableResult implements ISubmittableResult { readonly events: Array; readonly status: ExtrinsicStatus; @@ -76,11 +85,11 @@ export interface SubmittableExtrinsic extends IExtrinsic { sign (account: IKeyringPair, _options: Partial): this; - signAndSend (account: IKeyringPair | string | AccountId | Address, options?: Partial): SumbitableResultResult; + signAndSend (account: IKeyringPair | string | AccountId | Address, options?: Partial): SumbitableResultResult; signAndSend (account: IKeyringPair | string | AccountId | Address, statusCb: Callback): SumbitableResultSubscription; - signAndSend (account: IKeyringPair | string | AccountId | Address, options: Partial, statusCb: Callback): SumbitableResultSubscription; + signAndSend (account: IKeyringPair | string | AccountId | Address, options: Partial, statusCb?: Callback): SumbitableResultSubscription; } export default function createSubmittableExtrinsic ( @@ -150,14 +159,37 @@ export default function createSubmittableExtrinsic ( ); } - function expandOptions (options: Partial): SignatureOptions { - return { + function expandOptions (options: Partial): SignatureOptions { + return options = { blockHash: api.genesisHash, version: api.runtimeVersion, ...options } as SignatureOptions; } + function setupEraOptions (header: Header | null, options: Partial): Partial { + if (!header) { + if (isNumber(options.era)) { + // since we have no header, it is immortal, remove any option overrides + // so we only supply the genesisHash and no era to the construction + delete options.era; + delete options.blockHash; + } + + return {}; + } + + const { blockNumber, hash } = header; + + return { + blockHash: hash, + era: new ExtrinsicEra({ + current: blockNumber, + period: options.era || DEFAULT_MORTAL_LENGTH + }) + }; + } + const signOrigin = _extrinsic.sign; Object.defineProperties( @@ -171,10 +203,10 @@ export default function createSubmittableExtrinsic ( } }, sign: { - value: function (account: IKeyringPair, _options: Partial): SubmittableExtrinsic { + value: function (account: IKeyringPair, _options: Partial): SubmittableExtrinsic { // HACK here we actually override nonce if it was specified (backwards compat for // the previous signature - don't let userspace break, but allow then time to upgrade) - const options: Partial = isBn(_options) || isNumber(_options) + const options: Partial = isBn(_options) || isNumber(_options) ? { nonce: _options as any as number } : _options; @@ -184,13 +216,13 @@ export default function createSubmittableExtrinsic ( } }, signAndSend: { - value: function (account: IKeyringPair | string | AccountId | Address, optionsOrStatus?: Partial | Callback, statusCb?: Callback): SumbitableResultResult | SumbitableResultSubscription { - let options: Partial = {}; + value: function (account: IKeyringPair | string | AccountId | Address, optionsOrStatus?: Partial | Callback, statusCb?: Callback): SumbitableResultResult | SumbitableResultSubscription { + let options: Partial = {}; if (isFunction(optionsOrStatus)) { statusCb = optionsOrStatus; } else { - options = optionsOrStatus || {}; + options = { ...optionsOrStatus }; } const isSubscription = _noStatusCb || !!statusCb; @@ -200,19 +232,29 @@ export default function createSubmittableExtrinsic ( return decorateMethod( () => (( - isUndefined(options.nonce) - ? api.query.system.accountNonce(address) - : of(new Index(options.nonce)) + combineLatest([ + // if we have a nonce already, don't retrieve the latest, use what is there + isUndefined(options.nonce) + ? api.query.system.accountNonce(address) + : of(new Index(options.nonce)), + // if we have an era provided already or eraLength is <= 0 (immortal) + // don't get the latest block, just pass null, handle in mergeMap + (isUndefined(options.era) || (isNumber(options.era) && options.era > 0)) + ? api.rpc.chain.getHeader() as Observable
+ : of(null) + ]) ).pipe( first(), - mergeMap(async (nonce) => { + mergeMap(async ([nonce, header]) => { + const eraOptions = setupEraOptions(header, options); + if (isKeyringPair) { - this.sign(account as IKeyringPair, { ...options, nonce }); + this.sign(account as IKeyringPair, { ...options, ...eraOptions, nonce }); } else { assert(api.signer, 'no signer exists'); updateId = await (api.signer as Signer).sign(_extrinsic, address, { - ...expandOptions({ ...options, nonce }), + ...expandOptions({ ...options, ...eraOptions, nonce }), genesisHash: api.genesisHash }); } diff --git a/packages/api/test/e2e/promise-tx.spec.ts b/packages/api/test/e2e/promise-tx.spec.ts index 3b2147917c..3431206c47 100644 --- a/packages/api/test/e2e/promise-tx.spec.ts +++ b/packages/api/test/e2e/promise-tx.spec.ts @@ -79,20 +79,20 @@ describe.skip('Promise e2e transactions', () => { .send(logEvents(done)); }); - it('makes a transfer (signAndSend)', async (done) => { + it('makes a transfer (signAndSend, immortal)', async (done) => { return api.tx.balances .transfer(keyring.eve.address, 12345) - .signAndSend(keyring.dave, logEvents(done)); + .signAndSend(keyring.charlie, { era: 0 }, logEvents(done)); }); it('makes a transfer (signAndSend via Signer)', async (done) => { - const signer = new SingleAccountSigner(keyring.dave) as Signer; + const signer = new SingleAccountSigner(keyring.charlie) as Signer; api.setSigner(signer); return api.tx.balances .transfer(keyring.eve.address, 12345) - .signAndSend(keyring.dave.address, logEvents(done)); + .signAndSend(keyring.charlie.address, logEvents(done)); }); it('makes a transfer (signAndSend via Signer) with undefined Signer', async () => { @@ -141,18 +141,19 @@ describe.skip('Promise e2e transactions', () => { expect(hash.toHex()).toHaveLength(66); }); - it('makes a transfer, and uses new balance to transfers to new', async (done) => { + // this one is slightly difficult with the current testnet config - CantPay + it.skip('makes a transfer, and uses new balance to transfers to new', async (done) => { const pair = new Keyring().addFromUri('testing123', {}, 'ed25519'); function doOne (cb: any) { return api.tx.balances - .transfer(pair.address, 123456) + .transfer(pair.address, 1234567) .signAndSend(keyring.dave, logEvents(cb)); } function doTwo (cb: any) { return api.tx.balances - .transfer(keyring.alice.address, 12345) + .transfer(keyring.alice.address, 1111111) .signAndSend(pair, logEvents(cb)); } @@ -162,44 +163,51 @@ describe.skip('Promise e2e transactions', () => { }); }); - it('makes a transfer with ERA (signAndSend)', async (done) => { - const nonce = await api.query.system.accountNonce(keyring.dave.address) as Index; - const signedBlock = await api.rpc.chain.getBlock(); - const currentHeight = (signedBlock as SignedBlock).block.header.number; - const exERA = new ExtrinsicEra({ current: currentHeight, period: 10 }); - const eraBirth = exERA.asMortalEra.birth(currentHeight.toNumber()); - const eraDeath = exERA.asMortalEra.death(currentHeight.toNumber()); - console.log('STARTED AT :' + eraBirth + ' EXPIRED AT :' + eraDeath); - const eraHash = await api.rpc.chain.getBlockHash(eraBirth); - const ex = api.tx.balances.transfer(keyring.eve.address, 12345); - const hash = await ex.signAndSend(keyring.dave, { blockHash: eraHash, era: exERA, nonce } as any); + describe('eras', () => { + it('makes a transfer (specified era)', async (done) => { + const signedBlock = await api.rpc.chain.getBlock() as SignedBlock; + const currentHeight = signedBlock.block.header.number; + const exERA = new ExtrinsicEra({ current: currentHeight, period: 10 }); + const ex = api.tx.balances.transfer(keyring.eve.address, 12345); + const hash = await ex.signAndSend(keyring.charlie, { + blockHash: signedBlock.block.header.hash, + era: exERA + }); - expect(hash.toHex()).toHaveLength(66); - done(); - }); + expect(hash.toHex()).toHaveLength(66); + done(); + }); - it('makes a transfer with ERA (signAndSend) with invalid time', async (done) => { - const nonce = await api.query.system.accountNonce(keyring.alice.address) as Index; - const signedBlock = await api.rpc.chain.getBlock(); - const currentHeight = (signedBlock as SignedBlock).block.header.number; - const exERA = new ExtrinsicEra({ current: currentHeight, period: 4 }); - const eraBirth = exERA.asMortalEra.birth(currentHeight.toNumber()); - const eraDeath = exERA.asMortalEra.death(currentHeight.toNumber()); - console.log('STARTED AT :' + eraBirth + ' EXPIRED AT :' + eraDeath); - const eraHash = await api.rpc.chain.getBlockHash(eraBirth); - const ex = api.tx.balances.transfer(keyring.eve.address, 12345); + it('makes a transfer with invalid time', async (done) => { + const nonce = await api.query.system.accountNonce(keyring.alice.address) as Index; + const signedBlock = await api.rpc.chain.getBlock() as SignedBlock; + const currentHeight = signedBlock.block.header.number; + const exERA = new ExtrinsicEra({ current: currentHeight, period: 4 }); + const eraDeath = exERA.asMortalEra.death(currentHeight.toNumber()); + const blockHash = signedBlock.block.header.hash; + const ex = api.tx.balances.transfer(keyring.eve.address, 12345); - return ( - api.rpc.chain.subscribeNewHead(async (header: Header) => { - if (header.blockNumber.toNumber() === eraDeath - 1) { - try { - await ex.signAndSend(keyring.alice, { blockHash: eraHash, era: exERA, nonce } as any); - } catch (error) { - expect(error.message).toMatch(/1010: Invalid Transaction \(0\)/); - done(); + return ( + api.rpc.chain.subscribeNewHead(async (header: Header) => { + if (header.blockNumber.toNumber() === eraDeath - 1) { + try { + await ex.signAndSend(keyring.alice, { blockHash, era: exERA, nonce } as any); + } catch (error) { + expect(error.message).toMatch(/1010: Invalid Transaction \(0\)/); + done(); + } } - } - }) - ); + }) + ); + }); + + it('makes a transfer with custom numeric era', async (done) => { + const hash = await api.tx.balances + .transfer(keyring.eve.address, 12345) + .signAndSend(keyring.charlie, { era: 2 }); + + expect(hash.toHex()).toHaveLength(66); + done(); + }); }); }); diff --git a/packages/types/src/json/SignedBlock.004.immortal.json b/packages/types/src/json/SignedBlock.004.immortal.json new file mode 100644 index 0000000000..6b5a026bd4 --- /dev/null +++ b/packages/types/src/json/SignedBlock.004.immortal.json @@ -0,0 +1 @@ +{"jsonrpc":"2.0","result":{"block":{"extrinsics":["0x20010100033822245d","0x14010a002534","0x250281ff90b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe2290b20be8c5929d7d4dfe3f8124bad55217fa0dc53210399448a7bddefade7a7e64489c1acbfe65eb42d235e6d54279cd88e4d6d6147862a4e5c6ba2ed44594006c000400ffe659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4ee5c0"],"header":{"digest":{"logs":["0x0661757261208e08491700000000","0x05617572610101039584bca9a9dd88d4bede8787ce022ddea809cef8ebdbf0d7b17c918e49921c86e6d6aedebc991630f7634b3f3306b6c134e87325f34301de5745df0da0240e"]},"extrinsicsRoot":"0xe52df22c1a9deb8ed460e5d80a3dd8e1090bfd51631f53eca465915089703b92","number":"0xd0a","parentHash":"0xcf5fa8ef2fe76c0d6288535231d21989829933b986d32a3ba452173c5a2074f1","stateRoot":"0x3fe7723fdb1a60938e45b72f1d84f414434b9df4ece367dbb39912ebe1a85e9f"}},"justification":null},"id":7} diff --git a/packages/types/src/json/SignedBlock.004.mortal.json b/packages/types/src/json/SignedBlock.004.mortal.json new file mode 100644 index 0000000000..5b40fb92ee --- /dev/null +++ b/packages/types/src/json/SignedBlock.004.mortal.json @@ -0,0 +1 @@ +{"jsonrpc":"2.0","result":{"block":{"extrinsics":["0x20010100036022245d","0x14010a004d34","0x290281ff90b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22f2040bbcf048e6bfb68c0722fe379c93afbc8f885ee21fc5b5679d4ade606a7d46cf151a2b8ad5bbce6ee198a41e3251302f5e9c64f457dbeacacf3e43b448087033000400ffe659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4ee5c0"],"header":{"digest":{"logs":["0x0661757261209808491700000000","0x0561757261010195888c7f16b54cf99117933d503faea1a7a8bdab4b6a8144c078b6c3d9e55d3a1114af92b2360a0694a057df016e1ad2bd750d43411464b9e7414e921cfdc00b"]},"extrinsicsRoot":"0x751fc4dfad59fefd3c4f730596f1a742b56c9936b4e3d4801015472261a0ce48","number":"0xd14","parentHash":"0x497b3b929ae079f6f0d280ba85744b87559eaab8969619be5cc7596c5a974cb9","stateRoot":"0x011c352e0f97cad72578f9e1e8ec759096365b891928d6b094d0d8d329f9bbab"}},"justification":null},"id":8} diff --git a/packages/types/src/rpc/SignedBlock.spec.ts b/packages/types/src/rpc/SignedBlock.spec.ts index bb43a90f3c..306cbd41f8 100644 --- a/packages/types/src/rpc/SignedBlock.spec.ts +++ b/packages/types/src/rpc/SignedBlock.spec.ts @@ -4,63 +4,51 @@ import extrinsics from '@polkadot/api-metadata/extrinsics/static'; -import Extrinsics from '../type/Extrinsics'; import Method from '../primitive/Method'; -import rpc from '../json/SignedBlock.001.json'; import events from '../json/SignedBlock.002.json'; +import immortalTxs from '../json/SignedBlock.004.immortal.json'; +import mortalTxs from '../json/SignedBlock.004.mortal.json'; import SignedBlock from './SignedBlock'; -describe.skip('SignedBlock', () => { - let block: SignedBlock; - +describe('SignedBlock', () => { beforeEach(() => { Method.injectMethods(extrinsics); - - block = new SignedBlock(rpc.result); }); - it('has the correct stateRoot', () => { - expect( - block.block.header.stateRoot.toString() - ).toEqual('0xbfe02f7c26e5dad1226eccb1f4ef5306312602f7e1edbb9be50e8b87d9c7840b'); - }); - - it('decodes a block with logs', () => { + it('decodes a full block', () => { const s = new SignedBlock(events.result); + expect( + s.block.header.stateRoot.toString() + ).toEqual('0x24bab7fe0fee187a57bd0029ba151e81a0bae0fea96ad9f85c579c1ba9d88948'); expect(s.block.header.digest.logs.length).toBe(1); expect(s.block.header.digest.logs[0].type).toBe('AuthoritiesChange'); expect(s.block.header.digest.logs[0].value.toJSON()).toEqual([ - '5EyrU8k6worwXWfW2LajRvS7XF6eta286tMpHeHUwMPpzQ9F', - '5Dpq7rU6KAZVdRtcegvpCoeJtZzFPzieRkRz4xajRAiMRkCf', - '5CCtQPAYtuDXt6H4WMrn5nFNk4gemkVRD3k1r1mA7YiZLPBq', - '5F2ABwFm3fUhWMZT7Zeu7UjCr1dndjJD7qBeHChJhiJC949V', - '5CA1n5DSpE46Q9Y7NaqD39T9X7S2dRtscsPvrv2dWuYhFtWr', - '5CrxmsTCLbPRzHnFPs8Q6cx8jzHweegMSQYEJBGXLjDZW4NR', - '5EQ4ZxYew2CXPkjGCBuPdTVg7Hp3bmBXiXBwn1iDmvj6CgaF' + '5EyrU8k6worwXWfW2LajRvS7XF6eta286tMpHeHUwMPpzUwT', + '5Dpq7rU6KAZVdRtcegvpCoeJtZzFPzieRkRz4xajRAiMRxCd', + '5CCtQPAYtuDXt6H4WMrn5nFNk4gemkVRD3k1r1mA7YiZLPP5', + '5F2ABwFm3fUhWMZT7Zeu7UjCr1dndjJD7qBeHChJhiJC99UH', + '5CA1n5DSpE46Q9Y7NaqD39T9X7S2dRtscsPvrv2dWuYhFg5U', + '5CrxmsTCLbPRzHnFPs8Q6cx8jzHweegMSQYEJBGXLjDZWDNv', + '5EQ4ZxYew2CXPkjGCBuPdTVg7Hp3bmBXiXBwn1iDmvj6CY3u' ]); }); describe('extrinsics', () => { - let extrinsics: Extrinsics; + it('can decode immortals', () => { + const s = new SignedBlock(immortalTxs.result); + const immortalTx = s.block.extrinsics[2]; - beforeEach(() => { - extrinsics = block.block.extrinsics; + expect(immortalTx.method.methodName).toEqual('transfer'); + expect(immortalTx.method.sectionName).toEqual('balances'); }); - it('has the correct data for the first', () => { - const x = extrinsics[0]; + it('can decode mortals', () => { + const s = new SignedBlock(mortalTxs.result); + const mortalTx = s.block.extrinsics[2]; - expect(x.callIndex).toEqual(new Uint8Array([2, 0])); - expect(x.method.args[0].toU8a()).toEqual(new Uint8Array([70, 41, 195, 91, 0, 0, 0, 0])); - }); - - it('has the correct data for the second', () => { - const x = extrinsics[1]; - - expect(x.callIndex).toEqual(new Uint8Array([1, 0])); - expect(x.method.args[0].toString()).toEqual('5CPaGq4KcmntaboAxg5nyqGXdyzaBV2hj6PvhNA3syigiRg8'); - expect(x.method.args[1].toString()).toEqual('100000000000000'); + expect(mortalTx.method.methodName).toEqual('transfer'); + expect(mortalTx.method.sectionName).toEqual('balances'); }); }); }); diff --git a/packages/types/src/type/ExtrinsicEra.ts b/packages/types/src/type/ExtrinsicEra.ts index 04efe31b40..2d812773ed 100644 --- a/packages/types/src/type/ExtrinsicEra.ts +++ b/packages/types/src/type/ExtrinsicEra.ts @@ -2,13 +2,15 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { assert, hexToU8a, isHex, isU8a, isObject, u8aToBn } from '@polkadot/util'; +import { AnyU8a, IExtrinsicEra } from '../types'; + +import BN from 'bn.js'; +import { assert, bnToBn, hexToU8a, isHex, isU8a, isObject, u8aToBn } from '@polkadot/util'; import Enum from '../codec/Enum'; import Tuple from '../codec/Tuple'; import U8a from '../codec/U8a'; import U64 from '../primitive/U64'; -import { AnyU8a, IExtrinsicEra } from '../types'; type MortalEraValue = [U64, U64]; @@ -162,6 +164,13 @@ export class MortalEra extends Tuple { throw new Error('Invalid data passed to Mortal era'); } + /** + * @description Encoded length for mortals occupy 2 bytes, different from the actual Tuple since it is encoded. This is a shortcut fro `toU8a().length` + */ + get encodedLength (): number { + return 2; + } + /** * @description The period of this Mortal wraps as a [[U64]] */ @@ -180,11 +189,11 @@ export class MortalEra extends Tuple { * @description Encodes the value as a Uint8Array as per the parity-codec specifications * @param isBare true when the value has none of the type-specific prefixes (internal) * Period and phase are encoded: - * The period of validity from the block hash found in the signing material. - * The phase in the period that this transaction's lifetime begins (and, importantly, - * implies which block hash is included in the signature material). If the `period` is - * greater than 1 << 12, then it will be a factor of the times greater than 1<<12 that - * `period` is. + * - The period of validity from the block hash found in the signing material. + * - The phase in the period that this transaction's lifetime begins (and, importantly, + * implies which block hash is included in the signature material). If the `period` is + * greater than 1 << 12, then it will be a factor of the times greater than 1<<12 that + * `period` is. */ toU8a (isBare?: boolean): Uint8Array { const period = this.period.toNumber(); @@ -201,14 +210,20 @@ export class MortalEra extends Tuple { /** * @description Get the block number of the start of the era whose properties this object describes that `current` belongs to. */ - birth (current: number) { - return Math.floor((Math.max(current,this.phase.toNumber()) - this.phase.toNumber()) / this.period.toNumber()) * this.period.toNumber() + this.phase.toNumber(); + birth (current: BN | number) { + // FIXME No toNumber() here + return Math.floor( + ( + Math.max(bnToBn(current).toNumber(), this.phase.toNumber()) - this.phase.toNumber() + ) / this.period.toNumber() + ) * this.period.toNumber() + this.phase.toNumber(); } /** * @description Get the block number of the first block at which the era has ended. */ - death (current: number) { + death (current: BN | number) { + // FIXME No toNumber() here return this.birth(current) + this.period.toNumber(); } diff --git a/packages/types/src/type/SignaturePayload.ts b/packages/types/src/type/SignaturePayload.ts index 0d87175220..c9b8ddd5cb 100644 --- a/packages/types/src/type/SignaturePayload.ts +++ b/packages/types/src/type/SignaturePayload.ts @@ -2,7 +2,7 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { AnyNumber, AnyU8a, IKeyringPair } from '../types'; +import { AnyNumber, AnyU8a, IExtrinsicEra, IKeyringPair } from '../types'; import { blake2AsU8a } from '@polkadot/util-crypto'; @@ -17,7 +17,7 @@ import Nonce from './NonceCompact'; type SignaturePayloadValue = { nonce?: AnyNumber, method?: Method, - era?: AnyU8a | ExtrinsicEra + era?: AnyU8a | IExtrinsicEra blockHash?: AnyU8a }; diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 8e00751535..3007abf38c 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -113,7 +113,7 @@ export interface RuntimeVersionInterface { export type SignatureOptions = { blockHash: AnyU8a, - era?: Uint8Array, + era?: IExtrinsicEra, nonce: AnyNumber, version?: RuntimeVersionInterface };