Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/api/test/e2e/promise-tx.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { randomAsHex } from '@polkadot/util-crypto';
import Api from '../../src/promise';
import WsProvider from '../../../rpc-provider/src/ws';
import SingleAccountSigner from "../util/SingleAccountSigner";
import ExtrinsicEra from '../../../types/src/type/ExtrinsicEra';
Comment thread
KarishmaBothara marked this conversation as resolved.
Outdated

describe.skip('e2e transactions', () => {
const keyring = testingPairs({ type: 'ed25519' });
Expand Down Expand Up @@ -211,4 +212,21 @@ describe.skip('e2e transactions', () => {
doTwo(done)
});
});

it('makes a transfer with ERA (signAndSend)', async (done) => {
try {
const nonce = await api.query.system.accountNonce(keyring.dave.address());
const signedBlock = await api.rpc.chain.getBlock();
const exERA = new ExtrinsicEra(new Uint8Array([248, 13]), 1);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this test is happy path, you need to make sure the era you construct is always valid.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for example you should try to create a era which is valid from current to next 10 blocks. (make 10 a varible)

const eraBirth = exERA.mortalEra.birth(signedBlock.block.header.number);
const eraHash = await api.rpc.chain.getBlockHash(eraBirth);
const ex = api.tx.balances
.transfer(keyring.eve.address(), 12345);
const tx = await ex.signAndSend(keyring.dave, {blockHash: eraHash, era:exERA, nonce});
expect(tx.toHex()).toHaveLength(66);
done();
}catch (e) {
console.log(e);
}
});
});
31 changes: 31 additions & 0 deletions packages/types/src/type/ExtrinsicEra.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2017-2019 @polkadot/types 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 ExtrinsicEra, { MortalEra } from './ExtrinsicEra';
import U64 from '../primitive/U64';

describe('ExtrinsicEra', () => {

it('decodes an Extrinsic Era with immortal', () => {
const extrinsicEra = new ExtrinsicEra(new Uint8Array([0,0,0]));
Comment thread
KarishmaBothara marked this conversation as resolved.
Outdated
expect(extrinsicEra.mortalEra.period).toBeUndefined();
expect(extrinsicEra.mortalEra.phase).toBeUndefined();
});

it('decodes an Extrinsic Era from u8 as mortal', () => {
const extrinsicEra = new ExtrinsicEra(new Uint8Array([11, 125]));
expect(extrinsicEra.mortalEra.period.toNumber()).toEqual(4068);
expect(extrinsicEra.mortalEra.phase.toNumber()).toEqual(2000);
});


it.only('encode an Extrinsic Era from Object with blocknumber & period as mortal instance', () => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove only

const mortalIndex = 1;
const extrinsicEra = new ExtrinsicEra({ startBlockNumber: new U64(1400), endBlockNumber: new U64(1600) }, mortalIndex);
console.log('era', extrinsicEra.toU8a().join(','))
expect(extrinsicEra.mortalEra.period.toNumber()).toBeGreaterThan(4);
expect(extrinsicEra.mortalEra.period.toNumber()).toEqual(256);
expect(extrinsicEra.mortalEra.phase.toNumber()).toEqual(120);
});
});
153 changes: 136 additions & 17 deletions packages/types/src/type/ExtrinsicEra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,152 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.

import { AnyU8a } from '../types';
import EnumType from '../codec/EnumType';
import {isHex, isU8a, hexToU8a, isObject} from '@polkadot/util';
import Tuple from '../codec/Tuple';
import U64 from '../primitive/U64';
import U8 from '../primitive/U8';
import U32 from '../primitive/U32';
import U16 from '../primitive/U16';
import { IExtrinsicEra, AnyU8a } from '../types';
import BlockNumber from "@polkadot/types/type/BlockNumber";
import U8a from "@polkadot/types/codec/U8a";

import { u8aToU8a } from '@polkadot/util';
interface EraMethod {
startBlockNumber: BlockNumber;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they can just be number.

endBlockNumber: BlockNumber;
}

export default class ExtrinsicEra extends EnumType<ImmortalEra | MortalEra> implements IExtrinsicEra {
constructor (value?: any, index?: number) {
super({ ImmortalEra, MortalEra }, value, index);
}

/**
* @description Returns the item as a [[ImmortalEra]]
*/
get immortalEra (): ImmortalEra {
Comment thread
KarishmaBothara marked this conversation as resolved.
Outdated
return this.value as ImmortalEra;
}

/**
* @description Returns the item as a [[MortalEra]]
*/
get mortalEra (): MortalEra {
return this.value as MortalEra;
}

/**
* @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)
*/
toU8a (isBare?: boolean): Uint8Array {
if (this.index === 0 ) {
return super.toU8a()
} else {
return this.mortalEra.toU8a(isBare);
}
}
}

export class ImmortalEra extends U8a {
constructor(value?: AnyU8a) {
super(value);
Comment thread
KarishmaBothara marked this conversation as resolved.
}
}

import U8a from '../codec/U8a';
export type MortalEraValue = [U8, U8];

/**
* @name ExtrinsicEra
* @name MortalEra
* @description
* The era for an extrinsic, indicating either a mortal or immortal extrinsic
* The MortalEra for an extrinsic, indicating period and phase
*/
export default class ExtrinsicEra extends U8a {
constructor (value?: AnyU8a) {
super(
ExtrinsicEra.decodeExtrinsicEra(value)
);
export class MortalEra extends Tuple {
Comment thread
ianhe8x marked this conversation as resolved.
constructor (value?: any) {
super({
period : U8, phase : U8
}, MortalEra.decodeMortalEra(value));
}

private static decodeMortalEra (value: EraMethod | Uint8Array | string): MortalEraValue {
Comment thread
KarishmaBothara marked this conversation as resolved.
Outdated
if (isHex(value)) {
return MortalEra.decodeMortalEra(hexToU8a(value.toString()));
} else if (isU8a(value)) {
const u8a = value;
const first = u8a.subarray(0, 1);
let second = u8a.subarray(1, 2);
const encoded = new U64(new U64(first).toNumber() + (new U64(second).toNumber() << 8));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can just use them as type number. (no point wrap them with U64 but toNumber() every time use them)

const period = new U64(2 << (encoded.toNumber() % (1 << 4)));
const factor = 12;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can just use 12 in next line

const quantizeFactor = Math.max(period.toNumber() >> factor, 1);
let phase = (encoded.toNumber() >> 4) * quantizeFactor;
if (period.toNumber() >= 4 && phase < period.toNumber()) {
return [new U8(period), new U8(phase)];
}
return [new U8(), new U8()];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's a default return, so this line can be removed.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in which case the code will reach this line. maybe we should throw an error?

} else if (isObject(value)) {
const current = value.startBlockNumber;
const period = value.endBlockNumber.toNumber() - value.startBlockNumber.toNumber();
let calPeriod = Math.pow(2, Math.ceil(Math.log2(period)));
calPeriod = Math.max(calPeriod, 4);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can put max and min in one row.

calPeriod = Math.min(calPeriod, 1<< 16);
const phase = current.toNumber() % calPeriod;
const factor = 12;
const quantizeFactor = calPeriod >> factor > 1 ? calPeriod >> factor : 1;
let quantizedPhase = phase / quantizeFactor * quantizeFactor;
return [new U8(calPeriod), new U8(quantizedPhase)];
}
return [new U8(), new U8()];
}
/**
* @description The justification [[U64]]
*/
get period (): U8 {
return this[0] as U8;
}

/**
* @description The round this justification wraps as a [[U64]]
*/
get phase (): U8 {
return this[1] as U8;
}

static decodeExtrinsicEra (value?: AnyU8a): Uint8Array {
if (value) {
const u8a = u8aToU8a(value);
/**
* @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)
*/
toU8a (isBare?: boolean): Uint8Array {

// If we have a zero byte, it is immortal (1 byte in length), otherwise we have
// the era details following as another byte
return u8a.subarray(0, (u8a[0] === 0) ? 1 : 2);
const period = new U64(this.period);
Comment thread
KarishmaBothara marked this conversation as resolved.
Outdated
const phase = new U64(this.phase);
const quantize_factor = Math.max(period.toNumber() >> 12, 1);
const trailingZeros: any = this.getTrailingZeros(period);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try remove any

const encoded = Math.min(15, Math.max(1, trailingZeros - 1)) + (((phase.toNumber() / quantize_factor) << 4));
const encode = new U16(encoded);
Comment thread
KarishmaBothara marked this conversation as resolved.
Outdated
const first = encode.toNumber() >> 8;
const second = encode.toNumber() & 0xff;
return new Uint8Array([second, first]);
}

getTrailingZeros(period: U64): U32 {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why return as U32? don't see the point

let zeros = '';

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const zero: number[] = [];

let periodN = period.toNumber();
periodN = parseInt(Number(periodN).toString(2));
//periodN = periodN.toString(2)

while (periodN % 10 == 0) {
periodN = periodN /= 10;
zeros += 0;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

zeros.push(0)

}
return new U32(zeros.length);
}


return new Uint8Array([0]);
birth(current: U64) : U64{

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can just return number.

const b = Math.floor((Math.max(current.toNumber(),this.phase.toNumber()) - this.phase.toNumber()) / this.period.toNumber()) * this.period.toNumber() + this.phase.toNumber();
return new U64(b);
}

}
9 changes: 8 additions & 1 deletion packages/types/src/type/ExtrinsicSignature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@ export default class ExtrinsicSignature extends Struct implements IExtrinsicSign
return (this.get('version') as U8).toNumber();
}

/**
* @description The [[ExtrinsicEra]] (mortal or immortal) this signature applies to
*/
set era (era: ExtrinsicEra) {
this.set('era', era);
}

private injectSignature (signature: Signature, signer: Address, nonce: Nonce, era: ExtrinsicEra): ExtrinsicSignature {
this.set('era', era);
this.set('nonce', nonce);
Expand Down Expand Up @@ -145,7 +152,7 @@ export default class ExtrinsicSignature extends Struct implements IExtrinsicSign
const signingPayload = new SignaturePayload({
nonce,
method,
era: era || IMMORTAL_ERA,
era: era || this.era || IMMORTAL_ERA,
blockHash
});
const signature = new Signature(signingPayload.sign(account, version as RuntimeVersion));
Expand Down
7 changes: 7 additions & 0 deletions packages/types/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Method from './primitive/Method';
import Address from './type/Address';
import { FunctionMetadata as MetaV0 } from './Metadata/v0/Modules';
import { MetadataCall as MetaV4 } from './Metadata/v1/Calls';
import { ImmortalEra, MortalEra } from './type/ExtrinsicEra';

export type CodecArg = Codec | BN | Boolean | String | Uint8Array | boolean | number | string | undefined | CodecArgArray | CodecArgObject;

Expand Down Expand Up @@ -118,6 +119,12 @@ export interface IMethod extends Codec {

export interface IExtrinsicSignature extends Codec {
readonly isSigned: boolean;
era: IExtrinsicEra;
}

export interface IExtrinsicEra {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a proper interface for Era

immortalEra: ImmortalEra;
mortalEra: MortalEra;
}

export interface IExtrinsic extends IMethod {
Expand Down