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 <amaury.martiny@protonmail.com> * Update docs/examples/promise/07_make_transfer_with_allowed_block_permissions_only/index.js Co-Authored-By: Amaury Martiny <amaury.martiny@protonmail.com> * Remove useless birth/death calls
This commit is contained in:
co-authored by
Amaury Martiny
parent
be0139dfce
commit
2eee6cfea7
+6
-10
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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<EventRecord>;
|
||||
readonly status: ExtrinsicStatus;
|
||||
@@ -76,11 +85,11 @@ export interface SubmittableExtrinsic<ApiType> extends IExtrinsic {
|
||||
|
||||
sign (account: IKeyringPair, _options: Partial<SignatureOptions>): this;
|
||||
|
||||
signAndSend (account: IKeyringPair | string | AccountId | Address, options?: Partial<SignatureOptions>): SumbitableResultResult<ApiType>;
|
||||
signAndSend (account: IKeyringPair | string | AccountId | Address, options?: Partial<SignerOptions>): SumbitableResultResult<ApiType>;
|
||||
|
||||
signAndSend (account: IKeyringPair | string | AccountId | Address, statusCb: Callback<ISubmittableResult>): SumbitableResultSubscription<ApiType>;
|
||||
|
||||
signAndSend (account: IKeyringPair | string | AccountId | Address, options: Partial<SignatureOptions>, statusCb: Callback<ISubmittableResult>): SumbitableResultSubscription<ApiType>;
|
||||
signAndSend (account: IKeyringPair | string | AccountId | Address, options: Partial<SignerOptions>, statusCb?: Callback<ISubmittableResult>): SumbitableResultSubscription<ApiType>;
|
||||
}
|
||||
|
||||
export default function createSubmittableExtrinsic<ApiType> (
|
||||
@@ -150,14 +159,37 @@ export default function createSubmittableExtrinsic<ApiType> (
|
||||
);
|
||||
}
|
||||
|
||||
function expandOptions (options: Partial<SignatureOptions>): SignatureOptions {
|
||||
return {
|
||||
function expandOptions (options: Partial<SignerOptions>): SignatureOptions {
|
||||
return options = {
|
||||
blockHash: api.genesisHash,
|
||||
version: api.runtimeVersion,
|
||||
...options
|
||||
} as SignatureOptions;
|
||||
}
|
||||
|
||||
function setupEraOptions (header: Header | null, options: Partial<SignerOptions>): Partial<SignatureOptions> {
|
||||
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<ApiType> (
|
||||
}
|
||||
},
|
||||
sign: {
|
||||
value: function (account: IKeyringPair, _options: Partial<SignatureOptions>): SubmittableExtrinsic<ApiType> {
|
||||
value: function (account: IKeyringPair, _options: Partial<SignerOptions>): SubmittableExtrinsic<ApiType> {
|
||||
// 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<SignatureOptions> = isBn(_options) || isNumber(_options)
|
||||
const options: Partial<SignerOptions> = isBn(_options) || isNumber(_options)
|
||||
? { nonce: _options as any as number }
|
||||
: _options;
|
||||
|
||||
@@ -184,13 +216,13 @@ export default function createSubmittableExtrinsic<ApiType> (
|
||||
}
|
||||
},
|
||||
signAndSend: {
|
||||
value: function (account: IKeyringPair | string | AccountId | Address, optionsOrStatus?: Partial<SignatureOptions> | Callback<ISubmittableResult>, statusCb?: Callback<ISubmittableResult>): SumbitableResultResult<ApiType> | SumbitableResultSubscription<ApiType> {
|
||||
let options: Partial<SignatureOptions> = {};
|
||||
value: function (account: IKeyringPair | string | AccountId | Address, optionsOrStatus?: Partial<SignerOptions> | Callback<ISubmittableResult>, statusCb?: Callback<ISubmittableResult>): SumbitableResultResult<ApiType> | SumbitableResultSubscription<ApiType> {
|
||||
let options: Partial<SignerOptions> = {};
|
||||
|
||||
if (isFunction(optionsOrStatus)) {
|
||||
statusCb = optionsOrStatus;
|
||||
} else {
|
||||
options = optionsOrStatus || {};
|
||||
options = { ...optionsOrStatus };
|
||||
}
|
||||
|
||||
const isSubscription = _noStatusCb || !!statusCb;
|
||||
@@ -200,19 +232,29 @@ export default function createSubmittableExtrinsic<ApiType> (
|
||||
|
||||
return decorateMethod(
|
||||
() => ((
|
||||
isUndefined(options.nonce)
|
||||
? api.query.system.accountNonce<Index>(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<Index>(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<Header>
|
||||
: 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
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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}
|
||||
@@ -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}
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ export interface RuntimeVersionInterface {
|
||||
|
||||
export type SignatureOptions = {
|
||||
blockHash: AnyU8a,
|
||||
era?: Uint8Array,
|
||||
era?: IExtrinsicEra,
|
||||
nonce: AnyNumber,
|
||||
version?: RuntimeVersionInterface
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user