From 67625a0c581b4090b0e6d5cf3bec6db1f365b236 Mon Sep 17 00:00:00 2001 From: karishma Date: Wed, 10 Apr 2019 11:20:28 +1200 Subject: [PATCH 01/16] Initial changes for ERA --- packages/types/src/type/ExtrinsicEra.ts | 40 +++++++++++++++---- packages/types/src/type/ExtrinsicSignature.ts | 10 ++++- packages/types/src/types.ts | 5 +++ 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/packages/types/src/type/ExtrinsicEra.ts b/packages/types/src/type/ExtrinsicEra.ts index 5f57565c1629..c92919cf282b 100644 --- a/packages/types/src/type/ExtrinsicEra.ts +++ b/packages/types/src/type/ExtrinsicEra.ts @@ -3,32 +3,56 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { AnyU8a } from '../types'; +import EnumType from '../codec/EnumType'; +import {u8aToU8a, isHex, isObject, isU8a, hexToU8a} from '@polkadot/util'; +import {IMMORTAL_ERA} from "@polkadot/types/type/ExtrinsicSignature"; +import U8a from '../codec/U8a'; +//import {iExtrinsicEra} from "@polkadot/types/types"; +//import isHex from "@polkadot/util/is/hex"; -import { u8aToU8a } from '@polkadot/util'; -import U8a from '../codec/U8a'; +class ExtrinsicEra extends EnumType { + constructor(value) { + super({IMMORTAL_ERA, MortalEra}, value); + } +} /** * @name ExtrinsicEra * @description * The era for an extrinsic, indicating either a mortal or immortal extrinsic */ -export default class ExtrinsicEra extends U8a { - constructor (value?: AnyU8a) { +export default class MortalEra extends U8a { + constructor (value?: AnyU8a | string | {enabled: boolean, period: AnyU8a, phase: AnyU8a}) { super( - ExtrinsicEra.decodeExtrinsicEra(value) + MortalEra.decodeExtrinsicEra(value) ); } - static decodeExtrinsicEra (value?: AnyU8a): Uint8Array { - if (value) { +//{enabled: boolean, period: AnyU8a, phase: AnyU8a} + + static decodeExtrinsicEra (value?: AnyU8a | string | {'enabled': boolean, 'period': U}): Uint8Array { + if (isHex(value)) { + value = hexToU8a(value.toString()) + } + if (isU8a(value)) { const u8a = u8aToU8a(value); // 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); + } else if(isObject(value)) { + const min_period = 4; + const enabled : boolean = value.enabled; + const period = value.period; + const phase = value.phase; + if (enabled == true && period >= min_period && phase < period) { + const factor = 12; + const quantize_factor = period >> factor > 1 ? period >> factor : 1; + const quantized_phase = phase / quantize_factor * quantize_factor; + //return quantized_phase; + } } - return new Uint8Array([0]); } } diff --git a/packages/types/src/type/ExtrinsicSignature.ts b/packages/types/src/type/ExtrinsicSignature.ts index 71fef2289a12..e225863dd704 100644 --- a/packages/types/src/type/ExtrinsicSignature.ts +++ b/packages/types/src/type/ExtrinsicSignature.ts @@ -14,6 +14,7 @@ import Nonce from './NonceCompact'; import RuntimeVersion from '../rpc/RuntimeVersion'; import Signature from './Signature'; import SignaturePayload from './SignaturePayload'; +import {iExtrinsicEra} from "@polkadot/types/types"; export const IMMORTAL_ERA = new Uint8Array([0]); @@ -115,6 +116,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); @@ -145,7 +153,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)); diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 6d56c44439a1..7b3528977f1c 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -117,6 +117,11 @@ export interface IMethod extends Codec { export interface IExtrinsicSignature extends Codec { readonly isSigned: boolean; + era: IExtrinsicEra; +} + +export interface IExtrinsicEra extends U8a { + enabled: boolean; } export interface IExtrinsic extends IMethod { From 80f587da6ce2799f7eadcd1441f8182e1cbad86d Mon Sep 17 00:00:00 2001 From: karishma Date: Fri, 12 Apr 2019 10:55:40 +1200 Subject: [PATCH 02/16] WIP --- packages/types/src/type/ExtrinsicEra.ts | 205 +++++++++++++++++- packages/types/src/type/ExtrinsicSignature.ts | 1 - 2 files changed, 195 insertions(+), 11 deletions(-) diff --git a/packages/types/src/type/ExtrinsicEra.ts b/packages/types/src/type/ExtrinsicEra.ts index c92919cf282b..6414dc3bbcc4 100644 --- a/packages/types/src/type/ExtrinsicEra.ts +++ b/packages/types/src/type/ExtrinsicEra.ts @@ -5,24 +5,41 @@ import { AnyU8a } from '../types'; import EnumType from '../codec/EnumType'; import {u8aToU8a, isHex, isObject, isU8a, hexToU8a} from '@polkadot/util'; -import {IMMORTAL_ERA} from "@polkadot/types/type/ExtrinsicSignature"; import U8a from '../codec/U8a'; +import U64 from '../primitive/U64'; //import {iExtrinsicEra} from "@polkadot/types/types"; //import isHex from "@polkadot/util/is/hex"; -class ExtrinsicEra extends EnumType { - constructor(value) { - super({IMMORTAL_ERA, MortalEra}, value); +export default class ExtrinsicEra extends EnumType { + constructor(value?: any) { + super({ImmortalEra, MortalEra}, value); + } + /** + * @description Returns the item as a [[ImmortalEra]] + */ + get asImmortalEra (): ImmortalEra { + return this.value as ImmortalEra; + } + + /** + * @description Returns the item as a [[MortalEra]] + */ + get asMortalEra (): MortalEra { + return this.value as MortalEra; } } +//const a: ExtrinsicEra; + +export class ImmortalEra extends U8a { +} /** * @name ExtrinsicEra * @description * The era for an extrinsic, indicating either a mortal or immortal extrinsic */ -export default class MortalEra extends U8a { +export class MortalEra extends U8a { constructor (value?: AnyU8a | string | {enabled: boolean, period: AnyU8a, phase: AnyU8a}) { super( MortalEra.decodeExtrinsicEra(value) @@ -31,17 +48,31 @@ export default class MortalEra extends U8a { //{enabled: boolean, period: AnyU8a, phase: AnyU8a} - static decodeExtrinsicEra (value?: AnyU8a | string | {'enabled': boolean, 'period': U}): Uint8Array { + static decodeExtrinsicEra (value?: AnyU8a | string | {'enabled': boolean, 'period': AnyU8a, phase: AnyU8a}): Uint8Array { if (isHex(value)) { - value = hexToU8a(value.toString()) + return MortalEra.decodeExtrinsicEra(hexToU8a(value.toString())); } - if (isU8a(value)) { + else if (isU8a(value)) { const u8a = u8aToU8a(value); // 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); - } else if(isObject(value)) { + const eraDetails = u8a.subarray(0, (u8a[0] === 0) ? 1 : 2); + if (u8a[0] === 0) { + return MortalEra.decodeExtrinsicEra({enabled: false, period: new U8a('0'), phase: new U8a('0')}); + } else { + let first = u8a.subarray(0, 1); + let encoded = first as U64 + u8a.subarray(0, 1) + + /*let encoded = first as u64 + ((input.read_byte()? as u64) << 8); + let period = 2 << (encoded % (1 << 4)); + let quantize_factor = (period >> 12).max(1); + let phase = (encoded >> 4) * quantize_factor; + if period >= 4 && phase < period { + Some(Era::Mortal(period, phase))*/ + return MortalEra.decodeExtrinsicEra(eraDetails); + } + } else { const min_period = 4; const enabled : boolean = value.enabled; const period = value.period; @@ -56,3 +87,157 @@ export default class MortalEra extends U8a { return new Uint8Array([0]); } } + +impl Era { + /// Create a new era based on a period (which should be a power of two between 4 and 65536 inclusive) + /// and a block number on which it should start (or, for long periods, be shortly after the start). + pub fn mortal(period: u64, current: u64) -> Self { + let period = period.checked_next_power_of_two() + .unwrap_or(1 << 16) + .max(4) + .min(1 << 16); + let phase = current % period; + let quantize_factor = (period >> 12).max(1); + let quantized_phase = phase / quantize_factor * quantize_factor; + + Era::Mortal(period, quantized_phase) + } + + /// Create an "immortal" transaction. + pub fn immortal() -> Self { + Era::Immortal + } + + /// `true` if this is an immortal transaction. + pub fn is_immortal(&self) -> bool { + match self { + Era::Immortal => true, + _ => false, + } + } + + /// Get the block number of the start of the era whose properties this object + /// describes that `current` belongs to. + pub fn birth(self, current: u64) -> u64 { + match self { + Era::Immortal => 0, + Era::Mortal(period, phase) => (current.max(phase) - phase) / period * period + phase, + } + } + + /// Get the block number of the first block at which the era has ended. + pub fn death(self, current: u64) -> u64 { + match self { + Era::Immortal => u64::max_value(), + Era::Mortal(period, _) => self.birth(current) + period, + } + } +} + +impl Encode for Era { + fn encode_to(&self, output: &mut T) { + match self { + Era::Immortal => output.push_byte(0), + Era::Mortal(period, phase) => { + let quantize_factor = (*period as u64 >> 12).max(1); + let encoded = (period.trailing_zeros() - 1).max(1).min(15) as u16 | ((phase / quantize_factor) << 4) as u16; + output.push(&encoded); + } + } + } +} + +impl Decode for Era { + fn decode(input: &mut I) -> Option { + let first = input.read_byte()?; + if first == 0 { + Some(Era::Immortal) + } else { + let encoded = first as u64 + ((input.read_byte()? as u64) << 8); + let period = 2 << (encoded % (1 << 4)); + let quantize_factor = (period >> 12).max(1); + let phase = (encoded >> 4) * quantize_factor; + if period >= 4 && phase < period { + Some(Era::Mortal(period, phase)) + } else { + None + } + } +} +} + +#[cfg(test)] +mod tests { + use super::*; + +#[test] + fn immortal_works() { + let e = Era::immortal(); + assert_eq!(e.birth(0), 0); + assert_eq!(e.death(0), u64::max_value()); + assert_eq!(e.birth(1), 0); + assert_eq!(e.death(1), u64::max_value()); + assert_eq!(e.birth(u64::max_value()), 0); + assert_eq!(e.death(u64::max_value()), u64::max_value()); + assert!(e.is_immortal()); + + assert_eq!(e.encode(), vec![0u8]); + assert_eq!(e, Era::decode(&mut&[0u8][..]).unwrap()); + } + +#[test] + fn mortal_codec_works() { + let e = Era::mortal(64, 42); + assert!(!e.is_immortal()); + + let expected = vec![5 + 42 % 16 * 16, 42 / 16]; + assert_eq!(e.encode(), expected); + assert_eq!(e, Era::decode(&mut&expected[..]).unwrap()); + } + +#[test] + fn long_period_mortal_codec_works() { + let e = Era::mortal(32768, 20000); + + let expected = vec![(14 + 2500 % 16 * 16) as u8, (2500 / 16) as u8]; + assert_eq!(e.encode(), expected); + assert_eq!(e, Era::decode(&mut&expected[..]).unwrap()); + } + +#[test] + fn era_initialisation_works() { + assert_eq!(Era::mortal(64, 42), Era::Mortal(64, 42)); + assert_eq!(Era::mortal(32768, 20000), Era::Mortal(32768, 20000)); + assert_eq!(Era::mortal(200, 513), Era::Mortal(256, 1)); + assert_eq!(Era::mortal(2, 1), Era::Mortal(4, 1)); + assert_eq!(Era::mortal(4, 5), Era::Mortal(4, 1)); + } + +#[test] + fn quantised_clamped_era_initialisation_works() { + // clamp 1000000 to 65536, quantise 1000001 % 65536 to the nearest 4 + assert_eq!(Era::mortal(1000000, 1000001), Era::Mortal(65536, 1000001 % 65536 / 4 * 4)); + } + +#[test] + fn mortal_birth_death_works() { + let e = Era::mortal(4, 6); + for i in 6..10 { + assert_eq!(e.birth(i), 6); + assert_eq!(e.death(i), 10); + } + + // wrong because it's outside of the (current...current + period) range + assert_ne!(e.birth(10), 6); + assert_ne!(e.birth(5), 6); + } + +#[test] + fn current_less_than_phase() { + // should not panic + Era::mortal(4, 3).birth(1); + } +} + +* +* */ diff --git a/packages/types/src/type/ExtrinsicSignature.ts b/packages/types/src/type/ExtrinsicSignature.ts index e225863dd704..b53482d7552d 100644 --- a/packages/types/src/type/ExtrinsicSignature.ts +++ b/packages/types/src/type/ExtrinsicSignature.ts @@ -14,7 +14,6 @@ import Nonce from './NonceCompact'; import RuntimeVersion from '../rpc/RuntimeVersion'; import Signature from './Signature'; import SignaturePayload from './SignaturePayload'; -import {iExtrinsicEra} from "@polkadot/types/types"; export const IMMORTAL_ERA = new Uint8Array([0]); From 9fb26b5f1b28157f22811a2e7fd2a93c350fbdde Mon Sep 17 00:00:00 2001 From: karishma Date: Tue, 23 Apr 2019 16:10:05 +1200 Subject: [PATCH 03/16] WIP --- packages/types/src/type/ExtrinsicEra.ts | 285 +++++++----------------- 1 file changed, 84 insertions(+), 201 deletions(-) diff --git a/packages/types/src/type/ExtrinsicEra.ts b/packages/types/src/type/ExtrinsicEra.ts index 6414dc3bbcc4..30b3a254c634 100644 --- a/packages/types/src/type/ExtrinsicEra.ts +++ b/packages/types/src/type/ExtrinsicEra.ts @@ -2,18 +2,74 @@ // 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 {u8aToU8a, isHex, isObject, isU8a, hexToU8a} from '@polkadot/util'; -import U8a from '../codec/U8a'; +import {isHex, isU8a, hexToU8a, isObject} from '@polkadot/util'; +//import U8a from '../codec/U8a'; +import Null from '../primitive/Null'; +import Tuple from "@polkadot/types/codec/Tuple"; import U64 from '../primitive/U64'; //import {iExtrinsicEra} from "@polkadot/types/types"; //import isHex from "@polkadot/util/is/hex"; +interface EraMethod { + enabled: boolean; + period: U64; + phase: U64; + current: U64; // current blocknumber + fromBlockNumber: boolean +} export default class ExtrinsicEra extends EnumType { - constructor(value?: any) { - super({ImmortalEra, MortalEra}, value); + //constructor(value?: any) { + constructor (value: any) { + super({ImmortalEra, MortalEra}, ExtrinsicEra.decodeExtrinsicEra(value)); + } + + private static decodeExtrinsicEra (value: EraMethod | Uint8Array | string ): ImmortalEra | MortalEra { + if (isHex(value)) { + return ExtrinsicEra.decodeExtrinsicEra(hexToU8a(value.toString())); + } + else if (isU8a(value)) { + //const u8a = u8aToU8a(value); + const u8a = value; + // If we have a zero byte, it is immortal (1 byte in length), otherwise we have + // the era details following as another byte + //const eraDetails = u8a.subarray(0, (u8a[0] === 0) ? 1 : 2); + if (u8a[0] === 0) { + return new ImmortalEra(); + } else { + const first = u8a.subarray(0, 1); + const second = u8a.subarray(1, 2); + const encoded = new U64(first).toNumber() + (new U64(second).toNumber() << 8); + const period = 2 << (encoded % (1 << 4)); + const factor = 12; + const quantize_factor = period >> factor > 1 ? period >> factor : 1; + let phase = (encoded >> 4) * quantize_factor; + if (period >= 4 && phase < period) { + return new MortalEra([new U64(period), new U64(phase)]); + } + return new ImmortalEra(); + } + } + else if (isObject(value) && value.period) { + if (!value.enabled) { + return new ImmortalEra(); + } + if (value.phase) { + return new MortalEra([value.period, value.phase]); + } + if (value.fromBlockNumber) { + //let period = value.period.checked_next_power_of_two() + let period = Math.pow(2,Math.ceil(Math.log2(value.period.toNumber()))); + let phase = value.current.toNumber() % period; + const factor = 12; + const quantize_factor = period >> factor > 1 ? period >> factor : 1; + let quantized_phase = phase / quantize_factor * quantize_factor; + return new MortalEra([new U64(period), new U64(quantized_phase)]); + } + } + return new ImmortalEra(); + //return new Uint8Array([0]); } /** * @description Returns the item as a [[ImmortalEra]] @@ -31,213 +87,40 @@ export default class ExtrinsicEra extends EnumType { } //const a: ExtrinsicEra; -export class ImmortalEra extends U8a { - +export class ImmortalEra extends Null { } + +export type MortalEraValue = [U64, U64]; + /** * @name ExtrinsicEra * @description * The era for an extrinsic, indicating either a mortal or immortal extrinsic */ -export class MortalEra extends U8a { - constructor (value?: AnyU8a | string | {enabled: boolean, period: AnyU8a, phase: AnyU8a}) { - super( - MortalEra.decodeExtrinsicEra(value) - ); - } - -//{enabled: boolean, period: AnyU8a, phase: AnyU8a} - - static decodeExtrinsicEra (value?: AnyU8a | string | {'enabled': boolean, 'period': AnyU8a, phase: AnyU8a}): Uint8Array { - if (isHex(value)) { - return MortalEra.decodeExtrinsicEra(hexToU8a(value.toString())); - } - else if (isU8a(value)) { - const u8a = u8aToU8a(value); - - // If we have a zero byte, it is immortal (1 byte in length), otherwise we have - // the era details following as another byte - const eraDetails = u8a.subarray(0, (u8a[0] === 0) ? 1 : 2); - if (u8a[0] === 0) { - return MortalEra.decodeExtrinsicEra({enabled: false, period: new U8a('0'), phase: new U8a('0')}); - } else { - let first = u8a.subarray(0, 1); - let encoded = first as U64 + u8a.subarray(0, 1) - - /*let encoded = first as u64 + ((input.read_byte()? as u64) << 8); - let period = 2 << (encoded % (1 << 4)); - let quantize_factor = (period >> 12).max(1); - let phase = (encoded >> 4) * quantize_factor; - if period >= 4 && phase < period { - Some(Era::Mortal(period, phase))*/ - return MortalEra.decodeExtrinsicEra(eraDetails); - } - } else { - const min_period = 4; - const enabled : boolean = value.enabled; - const period = value.period; - const phase = value.phase; - if (enabled == true && period >= min_period && phase < period) { - const factor = 12; - const quantize_factor = period >> factor > 1 ? period >> factor : 1; - const quantized_phase = phase / quantize_factor * quantize_factor; - //return quantized_phase; - } - } - return new Uint8Array([0]); - } -} - -impl Era { - /// Create a new era based on a period (which should be a power of two between 4 and 65536 inclusive) - /// and a block number on which it should start (or, for long periods, be shortly after the start). - pub fn mortal(period: u64, current: u64) -> Self { - let period = period.checked_next_power_of_two() - .unwrap_or(1 << 16) - .max(4) - .min(1 << 16); - let phase = current % period; - let quantize_factor = (period >> 12).max(1); - let quantized_phase = phase / quantize_factor * quantize_factor; +export class MortalEra extends Tuple { + //private period: AnyU8a; - Era::Mortal(period, quantized_phase) + //constructor (value?: AnyU8a | string | {enabled: boolean, period: AnyU8a, phase: AnyU8a}) { + constructor(value?: MortalEraValue) { + super({ + period : U64, phase : U64 + }, value); } - /// Create an "immortal" transaction. - pub fn immortal() -> Self { - Era::Immortal - } - - /// `true` if this is an immortal transaction. - pub fn is_immortal(&self) -> bool { - match self { - Era::Immortal => true, - _ => false, - } - } - - /// Get the block number of the start of the era whose properties this object - /// describes that `current` belongs to. - pub fn birth(self, current: u64) -> u64 { - match self { - Era::Immortal => 0, - Era::Mortal(period, phase) => (current.max(phase) - phase) / period * period + phase, - } - } - - /// Get the block number of the first block at which the era has ended. - pub fn death(self, current: u64) -> u64 { - match self { - Era::Immortal => u64::max_value(), - Era::Mortal(period, _) => self.birth(current) + period, - } - } -} - -impl Encode for Era { - fn encode_to(&self, output: &mut T) { - match self { - Era::Immortal => output.push_byte(0), - Era::Mortal(period, phase) => { - let quantize_factor = (*period as u64 >> 12).max(1); - let encoded = (period.trailing_zeros() - 1).max(1).min(15) as u16 | ((phase / quantize_factor) << 4) as u16; - output.push(&encoded); - } - } - } -} - -impl Decode for Era { - fn decode(input: &mut I) -> Option { - let first = input.read_byte()?; - if first == 0 { - Some(Era::Immortal) - } else { - let encoded = first as u64 + ((input.read_byte()? as u64) << 8); - let period = 2 << (encoded % (1 << 4)); - let quantize_factor = (period >> 12).max(1); - let phase = (encoded >> 4) * quantize_factor; - if period >= 4 && phase < period { - Some(Era::Mortal(period, phase)) - } else { - None - } - } -} -} - -#[cfg(test)] -mod tests { - use super::*; - -#[test] - fn immortal_works() { - let e = Era::immortal(); - assert_eq!(e.birth(0), 0); - assert_eq!(e.death(0), u64::max_value()); - assert_eq!(e.birth(1), 0); - assert_eq!(e.death(1), u64::max_value()); - assert_eq!(e.birth(u64::max_value()), 0); - assert_eq!(e.death(u64::max_value()), u64::max_value()); - assert!(e.is_immortal()); - - assert_eq!(e.encode(), vec![0u8]); - assert_eq!(e, Era::decode(&mut&[0u8][..]).unwrap()); - } - -#[test] - fn mortal_codec_works() { - let e = Era::mortal(64, 42); - assert!(!e.is_immortal()); - - let expected = vec![5 + 42 % 16 * 16, 42 / 16]; - assert_eq!(e.encode(), expected); - assert_eq!(e, Era::decode(&mut&expected[..]).unwrap()); - } - -#[test] - fn long_period_mortal_codec_works() { - let e = Era::mortal(32768, 20000); - - let expected = vec![(14 + 2500 % 16 * 16) as u8, (2500 / 16) as u8]; - assert_eq!(e.encode(), expected); - assert_eq!(e, Era::decode(&mut&expected[..]).unwrap()); - } - -#[test] - fn era_initialisation_works() { - assert_eq!(Era::mortal(64, 42), Era::Mortal(64, 42)); - assert_eq!(Era::mortal(32768, 20000), Era::Mortal(32768, 20000)); - assert_eq!(Era::mortal(200, 513), Era::Mortal(256, 1)); - assert_eq!(Era::mortal(2, 1), Era::Mortal(4, 1)); - assert_eq!(Era::mortal(4, 5), Era::Mortal(4, 1)); - } - -#[test] - fn quantised_clamped_era_initialisation_works() { - // clamp 1000000 to 65536, quantise 1000001 % 65536 to the nearest 4 - assert_eq!(Era::mortal(1000000, 1000001), Era::Mortal(65536, 1000001 % 65536 / 4 * 4)); + /** + * @description The justification [[period]] + */ + get period() : U64 { + return this[0] as U64; + //return this.get('period'); } -#[test] - fn mortal_birth_death_works() { - let e = Era::mortal(4, 6); - for i in 6..10 { - assert_eq!(e.birth(i), 6); - assert_eq!(e.death(i), 10); - } - - // wrong because it's outside of the (current...current + period) range - assert_ne!(e.birth(10), 6); - assert_ne!(e.birth(5), 6); + /** + * @description The round this justification wraps as a [[U32]] + */ + get phase() : U64{ + return this[1] as U64; + //return this.get('phase'); } -#[test] - fn current_less_than_phase() { - // should not panic - Era::mortal(4, 3).birth(1); - } } - -* -* */ From 6f7efd4d2d4a35c24cee4decfcd532524da53a36 Mon Sep 17 00:00:00 2001 From: karishma Date: Wed, 24 Apr 2019 09:17:26 +1200 Subject: [PATCH 04/16] WIP --- packages/types/src/type/ExtrinsicEra.ts | 2 +- packages/types/src/types.ts | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/types/src/type/ExtrinsicEra.ts b/packages/types/src/type/ExtrinsicEra.ts index 30b3a254c634..1b540307369c 100644 --- a/packages/types/src/type/ExtrinsicEra.ts +++ b/packages/types/src/type/ExtrinsicEra.ts @@ -21,7 +21,7 @@ interface EraMethod { export default class ExtrinsicEra extends EnumType { //constructor(value?: any) { - constructor (value: any) { + constructor (value?: any) { super({ImmortalEra, MortalEra}, ExtrinsicEra.decodeExtrinsicEra(value)); } diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 7b3528977f1c..9998fff221a0 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -8,6 +8,8 @@ import U8a from './codec/U8a'; import Method from './primitive/Method'; import Address from './type/Address'; import { FunctionMetadata } from './Metadata/v0/Modules'; +import {ImmortalEra, MortalEra} from "@polkadot/types/type/ExtrinsicEra"; +import EnumType from "@polkadot/types/codec/EnumType"; export type CodecArg = Codec | BN | Boolean | String | Uint8Array | boolean | number | string | undefined | CodecArgArray | CodecArgObject; @@ -120,8 +122,9 @@ export interface IExtrinsicSignature extends Codec { era: IExtrinsicEra; } -export interface IExtrinsicEra extends U8a { - enabled: boolean; +// export interface IExtrinsicEra extends U8a { +export interface IExtrinsicEra extends EnumType { +enabled?: boolean; } export interface IExtrinsic extends IMethod { From c5130df7f8546af56bd29ca89b9761c9f5acfc05 Mon Sep 17 00:00:00 2001 From: karishma Date: Wed, 24 Apr 2019 14:56:37 +1200 Subject: [PATCH 05/16] Era changes --- packages/types/src/type/ExtrinsicEra.spec.ts | 45 ++++++++ packages/types/src/type/ExtrinsicEra.ts | 111 ++++++++----------- 2 files changed, 92 insertions(+), 64 deletions(-) create mode 100644 packages/types/src/type/ExtrinsicEra.spec.ts diff --git a/packages/types/src/type/ExtrinsicEra.spec.ts b/packages/types/src/type/ExtrinsicEra.spec.ts new file mode 100644 index 000000000000..6011c9e2968e --- /dev/null +++ b/packages/types/src/type/ExtrinsicEra.spec.ts @@ -0,0 +1,45 @@ +// 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 from './ExtrinsicEra'; +import U64 from "@polkadot/types/primitive/U64"; + +describe('ExtrinsicEra', () => { + + it('decodes a Extrinsic Era from hexstring as mortal', () => { + const mortalIndex = 1; + const extrinsicEra = new ExtrinsicEra('0x0401000001010b00000000',mortalIndex); + expect(extrinsicEra.asMortalEra.period.toNumber()).toBeGreaterThan(4); + expect(extrinsicEra.asMortalEra.phase.toNumber()).toBeLessThan(extrinsicEra.asMortalEra.period.toNumber()); + }); + + it('decodes a Extrinsic Era with immortal', () => { + const immortalIndex = 0; + const extrinsicEra = new ExtrinsicEra('0x0000000',immortalIndex); + //expect(extrinsicEra.asImmortalEra).toEqual(null); + expect(extrinsicEra.asMortalEra.period).toBeUndefined(); + expect(extrinsicEra.asMortalEra.phase).toBeUndefined(); + }); + + it('decodes a Extrinsic Era from u8 as mortal', () => { + const mortalIndex = 1; + const extrinsicEra = new ExtrinsicEra(new Uint8Array([4,1,0,0,1,1,11,0,0,0,0]),mortalIndex); + expect(extrinsicEra.asMortalEra.period.toNumber()).toBeGreaterThan(4); + expect(extrinsicEra.asMortalEra.phase.toNumber()).toBeLessThan(extrinsicEra.asMortalEra.period.toNumber()); + }); + + it('decodes a Extrinsic Era from Object with phase & period as mortal instance', () => { + const mortalIndex = 1; + const extrinsicEra = new ExtrinsicEra({enabled: true, period: new U64(32), phase: new U64(16)}, mortalIndex); + expect(extrinsicEra.asMortalEra.period.toNumber()).toBeGreaterThan(4); + expect(extrinsicEra.asMortalEra.phase.toNumber()).toBeLessThan(extrinsicEra.asMortalEra.period.toNumber()); + }); + + it('decodes a Extrinsic Era from Object with blocknumber & period as mortal instance', () => { + const mortalIndex = 1; + const extrinsicEra = new ExtrinsicEra({isBlockNumber: true, period: new U64(17), current: new U64(16)}, mortalIndex); + expect(extrinsicEra.asMortalEra.period.toNumber()).toBeGreaterThan(4); + expect(extrinsicEra.asMortalEra.phase.toNumber()).toBeLessThan(extrinsicEra.asMortalEra.period.toNumber()); + }); +}); diff --git a/packages/types/src/type/ExtrinsicEra.ts b/packages/types/src/type/ExtrinsicEra.ts index 1b540307369c..48ca23365682 100644 --- a/packages/types/src/type/ExtrinsicEra.ts +++ b/packages/types/src/type/ExtrinsicEra.ts @@ -4,73 +4,23 @@ import EnumType from '../codec/EnumType'; import {isHex, isU8a, hexToU8a, isObject} from '@polkadot/util'; -//import U8a from '../codec/U8a'; import Null from '../primitive/Null'; import Tuple from "@polkadot/types/codec/Tuple"; import U64 from '../primitive/U64'; -//import {iExtrinsicEra} from "@polkadot/types/types"; -//import isHex from "@polkadot/util/is/hex"; interface EraMethod { enabled: boolean; period: U64; - phase: U64; + phase?: U64; current: U64; // current blocknumber - fromBlockNumber: boolean + isBlockNumber: boolean } export default class ExtrinsicEra extends EnumType { - //constructor(value?: any) { - constructor (value?: any) { - super({ImmortalEra, MortalEra}, ExtrinsicEra.decodeExtrinsicEra(value)); + constructor (value?: any, index?: number) { + super({ImmortalEra, MortalEra}, value, index); } - private static decodeExtrinsicEra (value: EraMethod | Uint8Array | string ): ImmortalEra | MortalEra { - if (isHex(value)) { - return ExtrinsicEra.decodeExtrinsicEra(hexToU8a(value.toString())); - } - else if (isU8a(value)) { - //const u8a = u8aToU8a(value); - const u8a = value; - // If we have a zero byte, it is immortal (1 byte in length), otherwise we have - // the era details following as another byte - //const eraDetails = u8a.subarray(0, (u8a[0] === 0) ? 1 : 2); - if (u8a[0] === 0) { - return new ImmortalEra(); - } else { - const first = u8a.subarray(0, 1); - const second = u8a.subarray(1, 2); - const encoded = new U64(first).toNumber() + (new U64(second).toNumber() << 8); - const period = 2 << (encoded % (1 << 4)); - const factor = 12; - const quantize_factor = period >> factor > 1 ? period >> factor : 1; - let phase = (encoded >> 4) * quantize_factor; - if (period >= 4 && phase < period) { - return new MortalEra([new U64(period), new U64(phase)]); - } - return new ImmortalEra(); - } - } - else if (isObject(value) && value.period) { - if (!value.enabled) { - return new ImmortalEra(); - } - if (value.phase) { - return new MortalEra([value.period, value.phase]); - } - if (value.fromBlockNumber) { - //let period = value.period.checked_next_power_of_two() - let period = Math.pow(2,Math.ceil(Math.log2(value.period.toNumber()))); - let phase = value.current.toNumber() % period; - const factor = 12; - const quantize_factor = period >> factor > 1 ? period >> factor : 1; - let quantized_phase = phase / quantize_factor * quantize_factor; - return new MortalEra([new U64(period), new U64(quantized_phase)]); - } - } - return new ImmortalEra(); - //return new Uint8Array([0]); - } /** * @description Returns the item as a [[ImmortalEra]] */ @@ -85,7 +35,6 @@ export default class ExtrinsicEra extends EnumType { return this.value as MortalEra; } } -//const a: ExtrinsicEra; export class ImmortalEra extends Null { } @@ -93,34 +42,68 @@ export class ImmortalEra extends Null { export type MortalEraValue = [U64, U64]; /** - * @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 class MortalEra extends Tuple { - //private period: AnyU8a; //constructor (value?: AnyU8a | string | {enabled: boolean, period: AnyU8a, phase: AnyU8a}) { - constructor(value?: MortalEraValue) { + constructor(value?: any) { super({ period : U64, phase : U64 - }, value); + }, MortalEra.decodeMortalEra(value)); } + private static decodeMortalEra (value: EraMethod | Uint8Array | string ): MortalEraValue { + if (isHex(value)) { + return MortalEra.decodeMortalEra(hexToU8a(value.toString())); + } + else if (isU8a(value)) { + const u8a = value; + const first = u8a.subarray(0, 1); + const second = u8a.subarray(1, 2); + const encoded = new U64(first).toNumber() + (new U64(second).toNumber() << 8); + const period = 2 << (encoded % (1 << 4)); + const factor = 12; + const quantize_factor = period >> factor > 1 ? period >> factor : 1; + let phase = (encoded >> 4) * quantize_factor; + if (period >= 4 && phase < period) { + return [new U64(period), new U64(phase)]; + } + return [new U64(), new U64()]; + } + else if (isObject(value) && value.period) { + if (value.enabled === false) { + return [new U64(), new U64()]; + } + if (value.phase) { + return [value.period, value.phase as U64]; + } + else if (value.isBlockNumber) { + //let period = value.period.checked_next_power_of_two() + const period = Math.pow(2, Math.ceil(Math.log2(value.period.toNumber()))); + const phase = value.current.toNumber() % period; + const factor = 12; + const quantize_factor = period >> factor > 1 ? period >> factor : 1; + let quantized_phase = phase / quantize_factor * quantize_factor; + return [new U64(period), new U64(quantized_phase)]; + } + } + return [new U64(), new U64()]; + } /** - * @description The justification [[period]] + * @description The justification [[U64]] */ get period() : U64 { return this[0] as U64; - //return this.get('period'); } /** - * @description The round this justification wraps as a [[U32]] + * @description The round this justification wraps as a [[U64]] */ get phase() : U64{ return this[1] as U64; - //return this.get('phase'); } } From a500fd11f25b7790f0ae353983129d93dbd5a1d3 Mon Sep 17 00:00:00 2001 From: karishma Date: Fri, 26 Apr 2019 15:18:50 +1200 Subject: [PATCH 06/16] as per review comments --- packages/types/src/type/ExtrinsicEra.spec.ts | 46 +++++++---------- packages/types/src/type/ExtrinsicEra.ts | 51 ++++++++----------- packages/types/src/type/ExtrinsicSignature.ts | 2 +- packages/types/src/types.ts | 9 ++-- 4 files changed, 44 insertions(+), 64 deletions(-) diff --git a/packages/types/src/type/ExtrinsicEra.spec.ts b/packages/types/src/type/ExtrinsicEra.spec.ts index 6011c9e2968e..1b72cddb885e 100644 --- a/packages/types/src/type/ExtrinsicEra.spec.ts +++ b/packages/types/src/type/ExtrinsicEra.spec.ts @@ -2,44 +2,34 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import ExtrinsicEra from './ExtrinsicEra'; -import U64 from "@polkadot/types/primitive/U64"; +import ExtrinsicEra, { MortalEra } from './ExtrinsicEra'; +import U64 from '../primitive/U64'; describe('ExtrinsicEra', () => { - it('decodes a Extrinsic Era from hexstring as mortal', () => { - const mortalIndex = 1; - const extrinsicEra = new ExtrinsicEra('0x0401000001010b00000000',mortalIndex); - expect(extrinsicEra.asMortalEra.period.toNumber()).toBeGreaterThan(4); - expect(extrinsicEra.asMortalEra.phase.toNumber()).toBeLessThan(extrinsicEra.asMortalEra.period.toNumber()); - }); - - it('decodes a Extrinsic Era with immortal', () => { - const immortalIndex = 0; - const extrinsicEra = new ExtrinsicEra('0x0000000',immortalIndex); - //expect(extrinsicEra.asImmortalEra).toEqual(null); - expect(extrinsicEra.asMortalEra.period).toBeUndefined(); - expect(extrinsicEra.asMortalEra.phase).toBeUndefined(); + it('decodes an Extrinsic Era with immortal', () => { + const extrinsicEra = new ExtrinsicEra(new Uint8Array([0,0,0])); + expect(extrinsicEra.mortalEra.period).toBeUndefined(); + expect(extrinsicEra.mortalEra.phase).toBeUndefined(); }); - it('decodes a Extrinsic Era from u8 as mortal', () => { - const mortalIndex = 1; - const extrinsicEra = new ExtrinsicEra(new Uint8Array([4,1,0,0,1,1,11,0,0,0,0]),mortalIndex); - expect(extrinsicEra.asMortalEra.period.toNumber()).toBeGreaterThan(4); - expect(extrinsicEra.asMortalEra.phase.toNumber()).toBeLessThan(extrinsicEra.asMortalEra.period.toNumber()); + it('decodes an Extrinsic Era from u8 as mortal', () => { + const extrinsicEra = new ExtrinsicEra(new Uint8Array([1,4,1,0,0,1,1,11,0,0,0,0])); + expect(extrinsicEra.mortalEra.period.toNumber()).toBeGreaterThan(4); + expect(extrinsicEra.mortalEra.phase.toNumber()).toBeLessThan(extrinsicEra.mortalEra.period.toNumber()); }); - it('decodes a Extrinsic Era from Object with phase & period as mortal instance', () => { + it('encode an Extrinsic Era from Object with phase & period as mortal instance', () => { const mortalIndex = 1; - const extrinsicEra = new ExtrinsicEra({enabled: true, period: new U64(32), phase: new U64(16)}, mortalIndex); - expect(extrinsicEra.asMortalEra.period.toNumber()).toBeGreaterThan(4); - expect(extrinsicEra.asMortalEra.phase.toNumber()).toBeLessThan(extrinsicEra.asMortalEra.period.toNumber()); + const extrinsicEra = new ExtrinsicEra(new MortalEra({ period: new U64(32), phase: new U64(16) }), mortalIndex); + expect(extrinsicEra.mortalEra.period.toNumber()).toBeGreaterThan(4); + expect(extrinsicEra.mortalEra.phase.toNumber()).toBeLessThan(extrinsicEra.mortalEra.period.toNumber()); }); - it('decodes a Extrinsic Era from Object with blocknumber & period as mortal instance', () => { + it('encode an Extrinsic Era from Object with blocknumber & period as mortal instance', () => { const mortalIndex = 1; - const extrinsicEra = new ExtrinsicEra({isBlockNumber: true, period: new U64(17), current: new U64(16)}, mortalIndex); - expect(extrinsicEra.asMortalEra.period.toNumber()).toBeGreaterThan(4); - expect(extrinsicEra.asMortalEra.phase.toNumber()).toBeLessThan(extrinsicEra.asMortalEra.period.toNumber()); + const extrinsicEra = new ExtrinsicEra(new MortalEra({ isBlockNumber: true, period: new U64(17), current: new U64(16) }), mortalIndex); + expect(extrinsicEra.mortalEra.period.toNumber()).toBeGreaterThan(4); + expect(extrinsicEra.mortalEra.phase.toNumber()).toBeLessThan(extrinsicEra.mortalEra.period.toNumber()); }); }); diff --git a/packages/types/src/type/ExtrinsicEra.ts b/packages/types/src/type/ExtrinsicEra.ts index 48ca23365682..2e72aaf99b02 100644 --- a/packages/types/src/type/ExtrinsicEra.ts +++ b/packages/types/src/type/ExtrinsicEra.ts @@ -3,35 +3,35 @@ // of the Apache-2.0 license. See the LICENSE file for details. import EnumType from '../codec/EnumType'; -import {isHex, isU8a, hexToU8a, isObject} from '@polkadot/util'; +import { isHex, isU8a, hexToU8a, isObject } from '@polkadot/util'; import Null from '../primitive/Null'; -import Tuple from "@polkadot/types/codec/Tuple"; +import Tuple from '../codec/Tuple'; import U64 from '../primitive/U64'; +import { IExtrinsicEra } from '../types'; interface EraMethod { - enabled: boolean; period: U64; phase?: U64; current: U64; // current blocknumber - isBlockNumber: boolean + isBlockNumber: boolean; } -export default class ExtrinsicEra extends EnumType { +export default class ExtrinsicEra extends EnumType implements IExtrinsicEra { constructor (value?: any, index?: number) { - super({ImmortalEra, MortalEra}, value, index); + super({ ImmortalEra, MortalEra }, value, index); } /** * @description Returns the item as a [[ImmortalEra]] */ - get asImmortalEra (): ImmortalEra { + get immortalEra (): ImmortalEra { return this.value as ImmortalEra; } /** * @description Returns the item as a [[MortalEra]] */ - get asMortalEra (): MortalEra { + get mortalEra (): MortalEra { return this.value as MortalEra; } } @@ -47,47 +47,38 @@ export type MortalEraValue = [U64, U64]; * The MortalEra for an extrinsic, indicating period and phase */ export class MortalEra extends Tuple { - - //constructor (value?: AnyU8a | string | {enabled: boolean, period: AnyU8a, phase: AnyU8a}) { - constructor(value?: any) { + constructor (value?: any) { super({ period : U64, phase : U64 }, MortalEra.decodeMortalEra(value)); } - private static decodeMortalEra (value: EraMethod | Uint8Array | string ): MortalEraValue { + private static decodeMortalEra (value: EraMethod | Uint8Array | string): MortalEraValue { if (isHex(value)) { return MortalEra.decodeMortalEra(hexToU8a(value.toString())); - } - else if (isU8a(value)) { + } else if (isU8a(value)) { const u8a = value; const first = u8a.subarray(0, 1); const second = u8a.subarray(1, 2); const encoded = new U64(first).toNumber() + (new U64(second).toNumber() << 8); const period = 2 << (encoded % (1 << 4)); const factor = 12; - const quantize_factor = period >> factor > 1 ? period >> factor : 1; - let phase = (encoded >> 4) * quantize_factor; + const quantizeFactor = period >> factor > 1 ? period >> factor : 1; + let phase = (encoded >> 4) * quantizeFactor; if (period >= 4 && phase < period) { return [new U64(period), new U64(phase)]; } return [new U64(), new U64()]; - } - else if (isObject(value) && value.period) { - if (value.enabled === false) { - return [new U64(), new U64()]; - } + } else if (isObject(value) && value.period) { if (value.phase) { - return [value.period, value.phase as U64]; - } - else if (value.isBlockNumber) { - //let period = value.period.checked_next_power_of_two() + return [value.period, value.phase]; + } else if (value.isBlockNumber) { const period = Math.pow(2, Math.ceil(Math.log2(value.period.toNumber()))); const phase = value.current.toNumber() % period; const factor = 12; - const quantize_factor = period >> factor > 1 ? period >> factor : 1; - let quantized_phase = phase / quantize_factor * quantize_factor; - return [new U64(period), new U64(quantized_phase)]; + const quantizeFactor = period >> factor > 1 ? period >> factor : 1; + let quantizedPhase = phase / quantizeFactor * quantizeFactor; + return [new U64(period), new U64(quantizedPhase)]; } } return [new U64(), new U64()]; @@ -95,14 +86,14 @@ export class MortalEra extends Tuple { /** * @description The justification [[U64]] */ - get period() : U64 { + get period (): U64 { return this[0] as U64; } /** * @description The round this justification wraps as a [[U64]] */ - get phase() : U64{ + get phase (): U64 { return this[1] as U64; } diff --git a/packages/types/src/type/ExtrinsicSignature.ts b/packages/types/src/type/ExtrinsicSignature.ts index b53482d7552d..761d32719449 100644 --- a/packages/types/src/type/ExtrinsicSignature.ts +++ b/packages/types/src/type/ExtrinsicSignature.ts @@ -118,7 +118,7 @@ export default class ExtrinsicSignature extends Struct implements IExtrinsicSign /** * @description The [[ExtrinsicEra]] (mortal or immortal) this signature applies to */ - set era(era: ExtrinsicEra) { + set era (era: ExtrinsicEra) { this.set('era', era); } diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 9998fff221a0..75ee73230707 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -8,8 +8,7 @@ import U8a from './codec/U8a'; import Method from './primitive/Method'; import Address from './type/Address'; import { FunctionMetadata } from './Metadata/v0/Modules'; -import {ImmortalEra, MortalEra} from "@polkadot/types/type/ExtrinsicEra"; -import EnumType from "@polkadot/types/codec/EnumType"; +import { ImmortalEra, MortalEra } from './type/ExtrinsicEra'; export type CodecArg = Codec | BN | Boolean | String | Uint8Array | boolean | number | string | undefined | CodecArgArray | CodecArgObject; @@ -122,9 +121,9 @@ export interface IExtrinsicSignature extends Codec { era: IExtrinsicEra; } -// export interface IExtrinsicEra extends U8a { -export interface IExtrinsicEra extends EnumType { -enabled?: boolean; +export interface IExtrinsicEra { + immortalEra: ImmortalEra; + mortalEra: MortalEra; } export interface IExtrinsic extends IMethod { From c36cb9d3f934644b436accb8acda9260fe06a542 Mon Sep 17 00:00:00 2001 From: karishma Date: Thu, 2 May 2019 09:13:38 +1200 Subject: [PATCH 07/16] WIP --- packages/api/test/e2e/promise-tx.spec.js | 38 ++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/packages/api/test/e2e/promise-tx.spec.js b/packages/api/test/e2e/promise-tx.spec.js index 6566161a197f..0bf9ce0f17e8 100644 --- a/packages/api/test/e2e/promise-tx.spec.js +++ b/packages/api/test/e2e/promise-tx.spec.js @@ -9,8 +9,10 @@ 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, {MortalEra} from '../../../types/src/type/ExtrinsicEra' +import U64 from "../../../types/src/primitive/U64"; -describe.skip('e2e transactions', () => { +describe('e2e transactions', () => { const keyring = testingPairs({ type: 'ed25519' }); let api; @@ -29,9 +31,10 @@ describe.skip('e2e transactions', () => { jest.setTimeout(5000); }); + it('makes a transfer (sign, then send)', async (done) => { const nonce = await api.query.system.accountNonce(keyring.dave.address()); - + const ex = new ExtrinsicEra(new MortalEra({ isBlockNumber: true, period: new U64(17), current: new U64(16) }), 1); return api.tx.balances .transfer(keyring.eve.address(), 12345) .sign(keyring.dave, { nonce }) @@ -53,6 +56,37 @@ describe.skip('e2e transactions', () => { }); }); + // + // const ex = this.api.tx.cennzxSpot.addLiquidity(assetId, minLiquidity, maxAssetAmount, coreAmount); + // (ex.signature as ExtrinsicSignature).set('era', new ExtrinsicEra({enabled: true, period: 4, phase: 1})); + // return ex; + + it('makes a transfer (sign, then send)', async (done) => { + const nonce = await api.query.system.accountNonce(keyring.dave.address()); + const exERA = new ExtrinsicEra(new MortalEra({ isBlockNumber: true, period: new U64(17), current: new U64(16) }), 1); + const ex = api.tx.balances + .transfer(keyring.eve.address(), 12345); + ex.signature.set('era', exERA); + return ex + .sign(keyring.dave, { nonce }) + .send(({ events, status }) => { + console.log('Transaction status:', status.type); + + if (status.isFinalized) { + console.log('Completed at block hash', status.value.toHex()); + console.log('Events:'); + + events.forEach(({ phase, event: { data, method, section } }) => { + console.log('\t', phase.toString(), `: ${section}.${method}`, data.toString()); + }); + + if (events.length) { + done(); + } + } + }); + }); + it('makes a transfer (sign, then send - compat version)', async (done) => { const nonce = await api.query.system.accountNonce(keyring.dave.address()); From 49139f787ac3ec1883e321dafc98bda446d8c01a Mon Sep 17 00:00:00 2001 From: karishma Date: Wed, 15 May 2019 13:57:46 +1200 Subject: [PATCH 08/16] As per code review --- packages/api/test/e2e/promise-tx.spec.js | 25 ++++++++++---- packages/types/src/type/ExtrinsicEra.spec.ts | 21 ++++++------ packages/types/src/type/ExtrinsicEra.ts | 34 +++++++++++--------- packages/types/src/types.ts | 8 +---- 4 files changed, 49 insertions(+), 39 deletions(-) diff --git a/packages/api/test/e2e/promise-tx.spec.js b/packages/api/test/e2e/promise-tx.spec.js index d97ee7efa6c7..ac0edb299b53 100644 --- a/packages/api/test/e2e/promise-tx.spec.js +++ b/packages/api/test/e2e/promise-tx.spec.js @@ -10,6 +10,7 @@ import Api from '../../src/promise'; import WsProvider from '../../../rpc-provider/src/ws'; import SingleAccountSigner from "../util/SingleAccountSigner"; import ExtrinsicEra from '../../../types/src/type/ExtrinsicEra'; +import U64 from "../../../types/src/primitive/U64"; describe.skip('e2e transactions', () => { const keyring = testingPairs({ type: 'ed25519' }); @@ -214,19 +215,31 @@ describe.skip('e2e transactions', () => { }); 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); - const eraBirth = exERA.mortalEra.birth(signedBlock.block.header.number); + const currentHeight = signedBlock.block.header.number; + // const exERA = new ExtrinsicEra(new Uint8Array([248, 13]), 1); + const exERA = new ExtrinsicEra({ startBlockNumber: new U64(currentHeight+10), endBlockNumber: new U64(currentHeight+50) }, 1); + const eraBirth = exERA.asMortalEra.birth(currentHeight); 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); - } + }); + + it('makes a transfer with ERA (signAndSend) with invalid time', async (done) => { + const nonce = await api.query.system.accountNonce(keyring.alice.address()); + const signedBlock = await api.rpc.chain.getBlock(); + const currentHeight = signedBlock.block.header.number; + const exERA = new ExtrinsicEra({ startBlockNumber: new U64(currentHeight-40), endBlockNumber: new U64(currentHeight+10) }, 1); + const eraBirth = exERA.asMortalEra.birth(currentHeight); + const eraHash = await api.rpc.chain.getBlockHash(eraBirth); + const ex = api.tx.balances + .transfer(keyring.eve.address(), 12345); + const tx = await ex.signAndSend(keyring.alice, {blockHash: eraHash, era:exERA, nonce}); + expect(tx).toBeUndefined(); + done(); }); }); diff --git a/packages/types/src/type/ExtrinsicEra.spec.ts b/packages/types/src/type/ExtrinsicEra.spec.ts index 5fe3a4fdd7a9..2c7ec7fc4d63 100644 --- a/packages/types/src/type/ExtrinsicEra.spec.ts +++ b/packages/types/src/type/ExtrinsicEra.spec.ts @@ -8,24 +8,23 @@ import U64 from '../primitive/U64'; describe('ExtrinsicEra', () => { it('decodes an Extrinsic Era with immortal', () => { - const extrinsicEra = new ExtrinsicEra(new Uint8Array([0,0,0])); - expect(extrinsicEra.mortalEra.period).toBeUndefined(); - expect(extrinsicEra.mortalEra.phase).toBeUndefined(); + const extrinsicEra = new ExtrinsicEra(new Uint8Array([0])); + expect(extrinsicEra.asMortalEra).toBeUndefined(); + expect(extrinsicEra.asImmortalEra).toBeDefined(); }); 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); + const extrinsicEra = new ExtrinsicEra(new Uint8Array([1, 78, 156])); + expect((extrinsicEra.asMortalEra as MortalEra).period.toNumber()).toEqual(32768); + expect((extrinsicEra.asMortalEra as MortalEra).phase.toNumber()).toEqual(20000); }); - it.only('encode an Extrinsic Era from Object with blocknumber & period as mortal instance', () => { + it('encode an Extrinsic Era from Object with blocknumber & period as mortal instance', () => { 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); + expect((extrinsicEra.asMortalEra as MortalEra).period.toNumber()).toBeGreaterThan(4); + expect((extrinsicEra.asMortalEra as MortalEra).period.toNumber()).toEqual(256); + expect((extrinsicEra.asMortalEra as MortalEra).phase.toNumber()).toEqual(120); }); }); diff --git a/packages/types/src/type/ExtrinsicEra.ts b/packages/types/src/type/ExtrinsicEra.ts index 51b7eb8301c6..33c870ca9215 100644 --- a/packages/types/src/type/ExtrinsicEra.ts +++ b/packages/types/src/type/ExtrinsicEra.ts @@ -7,9 +7,8 @@ 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 { AnyU8a } from '../types'; import BlockNumber from "@polkadot/types/type/BlockNumber"; import U8a from "@polkadot/types/codec/U8a"; @@ -18,7 +17,7 @@ interface EraMethod { endBlockNumber: BlockNumber; } -export default class ExtrinsicEra extends EnumType implements IExtrinsicEra { +export default class ExtrinsicEra extends EnumType { constructor (value?: any, index?: number) { super({ ImmortalEra, MortalEra }, value, index); } @@ -26,15 +25,21 @@ export default class ExtrinsicEra extends EnumType impl /** * @description Returns the item as a [[ImmortalEra]] */ - get immortalEra (): ImmortalEra { - return this.value as ImmortalEra; + get asImmortalEra (): ImmortalEra | undefined{ + if (this.index === 0) { + return this.value as ImmortalEra; + } + return; } /** * @description Returns the item as a [[MortalEra]] */ - get mortalEra (): MortalEra { - return this.value as MortalEra; + get asMortalEra (): MortalEra | undefined { + if (this.index === 1) { + return this.value as MortalEra; + } + return; } /** @@ -45,7 +50,7 @@ export default class ExtrinsicEra extends EnumType impl if (this.index === 0 ) { return super.toU8a() } else { - return this.mortalEra.toU8a(isBare); + return (this.asMortalEra as MortalEra).toU8a(isBare); } } } @@ -85,13 +90,12 @@ export class MortalEra extends Tuple { if (period.toNumber() >= 4 && phase < period.toNumber()) { return [new U8(period), new U8(phase)]; } - return [new U8(), new U8()]; + throw new Error('Invalid data passed to Mortal era'); } 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); - calPeriod = Math.min(calPeriod, 1<< 16); + calPeriod = Math.min( Math.max(calPeriod, 4), 1<< 16); const phase = current.toNumber() % calPeriod; const factor = 12; const quantizeFactor = calPeriod >> factor > 1 ? calPeriod >> factor : 1; @@ -123,7 +127,7 @@ export class MortalEra extends Tuple { const period = new U64(this.period); const phase = new U64(this.phase); const quantize_factor = Math.max(period.toNumber() >> 12, 1); - const trailingZeros: any = this.getTrailingZeros(period); + const trailingZeros = this.getTrailingZeros(period); const encoded = Math.min(15, Math.max(1, trailingZeros - 1)) + (((phase.toNumber() / quantize_factor) << 4)); const encode = new U16(encoded); const first = encode.toNumber() >> 8; @@ -131,7 +135,7 @@ export class MortalEra extends Tuple { return new Uint8Array([second, first]); } - getTrailingZeros(period: U64): U32 { + getTrailingZeros(period: U64) { let zeros = ''; let periodN = period.toNumber(); periodN = parseInt(Number(periodN).toString(2)); @@ -141,11 +145,11 @@ export class MortalEra extends Tuple { periodN = periodN /= 10; zeros += 0; } - return new U32(zeros.length); + return zeros.length; } - birth(current: U64) : U64{ + birth(current: U64) { 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); } diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index b4716f30a1a6..be036a871a14 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -9,7 +9,6 @@ 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; @@ -119,12 +118,7 @@ export interface IMethod extends Codec { export interface IExtrinsicSignature extends Codec { readonly isSigned: boolean; - era: IExtrinsicEra; -} - -export interface IExtrinsicEra { - immortalEra: ImmortalEra; - mortalEra: MortalEra; + era: Codec; } export interface IExtrinsic extends IMethod { From f654f3c146d3834604499036e9ea29836af0d995 Mon Sep 17 00:00:00 2001 From: karishma Date: Wed, 15 May 2019 16:25:13 +1200 Subject: [PATCH 09/16] ERA changes --- packages/api/test/e2e/promise-tx.spec.js | 4 ++-- packages/types/src/type/ExtrinsicEra.ts | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/api/test/e2e/promise-tx.spec.js b/packages/api/test/e2e/promise-tx.spec.js index ac0edb299b53..f32071328fb1 100644 --- a/packages/api/test/e2e/promise-tx.spec.js +++ b/packages/api/test/e2e/promise-tx.spec.js @@ -219,7 +219,7 @@ describe.skip('e2e transactions', () => { const signedBlock = await api.rpc.chain.getBlock(); const currentHeight = signedBlock.block.header.number; // const exERA = new ExtrinsicEra(new Uint8Array([248, 13]), 1); - const exERA = new ExtrinsicEra({ startBlockNumber: new U64(currentHeight+10), endBlockNumber: new U64(currentHeight+50) }, 1); + const exERA = new ExtrinsicEra({ current: new U64(currentHeight+10), period: new U64(2) }, 1); const eraBirth = exERA.asMortalEra.birth(currentHeight); const eraHash = await api.rpc.chain.getBlockHash(eraBirth); const ex = api.tx.balances @@ -233,7 +233,7 @@ describe.skip('e2e transactions', () => { const nonce = await api.query.system.accountNonce(keyring.alice.address()); const signedBlock = await api.rpc.chain.getBlock(); const currentHeight = signedBlock.block.header.number; - const exERA = new ExtrinsicEra({ startBlockNumber: new U64(currentHeight-40), endBlockNumber: new U64(currentHeight+10) }, 1); + const exERA = new ExtrinsicEra({ current: new U64(currentHeight-40), period: new U64(currentHeight+10) }, 1); const eraBirth = exERA.asMortalEra.birth(currentHeight); const eraHash = await api.rpc.chain.getBlockHash(eraBirth); const ex = api.tx.balances diff --git a/packages/types/src/type/ExtrinsicEra.ts b/packages/types/src/type/ExtrinsicEra.ts index 33c870ca9215..951c06ee826b 100644 --- a/packages/types/src/type/ExtrinsicEra.ts +++ b/packages/types/src/type/ExtrinsicEra.ts @@ -13,8 +13,8 @@ import BlockNumber from "@polkadot/types/type/BlockNumber"; import U8a from "@polkadot/types/codec/U8a"; interface EraMethod { - startBlockNumber: BlockNumber; - endBlockNumber: BlockNumber; + current: BlockNumber; + period: BlockNumber; } export default class ExtrinsicEra extends EnumType { @@ -92,9 +92,9 @@ export class MortalEra extends Tuple { } throw new Error('Invalid data passed to Mortal era'); } 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))); + const {current} = value; + const {period} = value; + let calPeriod = Math.pow(2, Math.ceil(Math.log2(period.toNumber()))); calPeriod = Math.min( Math.max(calPeriod, 4), 1<< 16); const phase = current.toNumber() % calPeriod; const factor = 12; From db2b56ea888adda75df82a4a57399af938ab1169 Mon Sep 17 00:00:00 2001 From: karishma Date: Wed, 15 May 2019 16:54:02 +1200 Subject: [PATCH 10/16] as per review --- packages/types/src/type/ExtrinsicEra.spec.ts | 3 +- packages/types/src/type/ExtrinsicEra.ts | 49 ++++++++++++-------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/packages/types/src/type/ExtrinsicEra.spec.ts b/packages/types/src/type/ExtrinsicEra.spec.ts index 2c7ec7fc4d63..4d375c05a1a2 100644 --- a/packages/types/src/type/ExtrinsicEra.spec.ts +++ b/packages/types/src/type/ExtrinsicEra.spec.ts @@ -9,7 +9,6 @@ describe('ExtrinsicEra', () => { it('decodes an Extrinsic Era with immortal', () => { const extrinsicEra = new ExtrinsicEra(new Uint8Array([0])); - expect(extrinsicEra.asMortalEra).toBeUndefined(); expect(extrinsicEra.asImmortalEra).toBeDefined(); }); @@ -22,7 +21,7 @@ describe('ExtrinsicEra', () => { it('encode an Extrinsic Era from Object with blocknumber & period as mortal instance', () => { const mortalIndex = 1; - const extrinsicEra = new ExtrinsicEra({ startBlockNumber: new U64(1400), endBlockNumber: new U64(1600) }, mortalIndex); + const extrinsicEra = new ExtrinsicEra({ current: new U64(1400), period: new U64(200) }, mortalIndex); expect((extrinsicEra.asMortalEra as MortalEra).period.toNumber()).toBeGreaterThan(4); expect((extrinsicEra.asMortalEra as MortalEra).period.toNumber()).toEqual(256); expect((extrinsicEra.asMortalEra as MortalEra).phase.toNumber()).toEqual(120); diff --git a/packages/types/src/type/ExtrinsicEra.ts b/packages/types/src/type/ExtrinsicEra.ts index 951c06ee826b..48d27930970a 100644 --- a/packages/types/src/type/ExtrinsicEra.ts +++ b/packages/types/src/type/ExtrinsicEra.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import EnumType from '../codec/EnumType'; -import {isHex, isU8a, hexToU8a, isObject} from '@polkadot/util'; +import {isHex, isU8a, hexToU8a, isObject, assert} from '@polkadot/util'; import Tuple from '../codec/Tuple'; import U64 from '../primitive/U64'; import U8 from '../primitive/U8'; @@ -25,21 +25,31 @@ export default class ExtrinsicEra extends EnumType { /** * @description Returns the item as a [[ImmortalEra]] */ - get asImmortalEra (): ImmortalEra | undefined{ - if (this.index === 0) { - return this.value as ImmortalEra; - } - return; + get asImmortalEra (): ImmortalEra { + assert(this.isImmortalEra, `Cannot convert '${this.type}' via asImmortalEra`); + return this.value as ImmortalEra; } /** * @description Returns the item as a [[MortalEra]] */ - get asMortalEra (): MortalEra | undefined { - if (this.index === 1) { - return this.value as MortalEra; - } - return; + get asMortalEra (): MortalEra { + assert(this.isMortalEra, `Cannot convert '${this.type}' via asMortalEra`); + return this.value as MortalEra; + } + + /** + * @description `true` if Immortal + */ + get isImmortalEra (): boolean { + return this.type === 'ImmortalEra'; + } + + /** + * @description `true` if Mortal + */ + get isMortalEra (): boolean { + return this.type === 'MortalEra'; } /** @@ -82,11 +92,10 @@ export class MortalEra extends Tuple { 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)); - const period = new U64(2 << (encoded.toNumber() % (1 << 4))); - const factor = 12; - const quantizeFactor = Math.max(period.toNumber() >> factor, 1); - let phase = (encoded.toNumber() >> 4) * quantizeFactor; + const encoded = new U64(first).toNumber() + (new U64(second).toNumber() << 8); + const period = new U64(2 << (encoded % (1 << 4))); + const quantizeFactor = Math.max(period.toNumber() >> 12, 1); + let phase = (encoded >> 4) * quantizeFactor; if (period.toNumber() >= 4 && phase < period.toNumber()) { return [new U8(period), new U8(phase)]; } @@ -124,8 +133,8 @@ export class MortalEra extends Tuple { */ toU8a (isBare?: boolean): Uint8Array { - const period = new U64(this.period); - const phase = new U64(this.phase); + const period = this.period; + const phase = this.phase; const quantize_factor = Math.max(period.toNumber() >> 12, 1); const trailingZeros = this.getTrailingZeros(period); const encoded = Math.min(15, Math.max(1, trailingZeros - 1)) + (((phase.toNumber() / quantize_factor) << 4)); @@ -136,14 +145,14 @@ export class MortalEra extends Tuple { } getTrailingZeros(period: U64) { - let zeros = ''; + const zeros: number[] = []; let periodN = period.toNumber(); periodN = parseInt(Number(periodN).toString(2)); //periodN = periodN.toString(2) while (periodN % 10 == 0) { periodN = periodN /= 10; - zeros += 0; + zeros.push(0); } return zeros.length; } From b32ef44a6079043b62011da3631b12e4df902512 Mon Sep 17 00:00:00 2001 From: karishma Date: Wed, 15 May 2019 21:54:01 +1200 Subject: [PATCH 11/16] as per review comment --- packages/api/test/e2e/promise-tx.spec.js | 29 ++++++++++++++-------- packages/types/src/type/ExtrinsicEra.ts | 31 ++++++++++++------------ 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/packages/api/test/e2e/promise-tx.spec.js b/packages/api/test/e2e/promise-tx.spec.js index f32071328fb1..a69c019874aa 100644 --- a/packages/api/test/e2e/promise-tx.spec.js +++ b/packages/api/test/e2e/promise-tx.spec.js @@ -218,9 +218,12 @@ describe.skip('e2e transactions', () => { const nonce = await api.query.system.accountNonce(keyring.dave.address()); const signedBlock = await api.rpc.chain.getBlock(); const currentHeight = signedBlock.block.header.number; - // const exERA = new ExtrinsicEra(new Uint8Array([248, 13]), 1); - const exERA = new ExtrinsicEra({ current: new U64(currentHeight+10), period: new U64(2) }, 1); - const eraBirth = exERA.asMortalEra.birth(currentHeight); + const exERA = new ExtrinsicEra({ current: new U64(currentHeight+10), period: new U64(10) }, 1); + // eraBirth - start of ERA which is always less than current block height + // eraDeath - end of ERA validity (EXPIRY) + 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); @@ -233,13 +236,19 @@ describe.skip('e2e transactions', () => { const nonce = await api.query.system.accountNonce(keyring.alice.address()); const signedBlock = await api.rpc.chain.getBlock(); const currentHeight = signedBlock.block.header.number; - const exERA = new ExtrinsicEra({ current: new U64(currentHeight-40), period: new U64(currentHeight+10) }, 1); - const eraBirth = exERA.asMortalEra.birth(currentHeight); + const exERA = new ExtrinsicEra({ current: new U64(currentHeight+5), period: new U64(4) }, 1); + 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 tx = await ex.signAndSend(keyring.alice, {blockHash: eraHash, era:exERA, nonce}); - expect(tx).toBeUndefined(); - done(); + const ex = api.tx.balances.transfer(keyring.eve.address(), 12345); + const unsubscribe = await api.rpc.chain.subscribeNewHead(async(header) => { + console.log(`Chain is at block: #${header.blockNumber}`); + if (header.blockNumber.toNumber() === eraDeath) { + const tx = await ex.signAndSend(keyring.alice, {blockHash: eraHash, era:exERA, nonce}); + expect(tx).toBeUndefined(); + done(); + } + }); }); }); diff --git a/packages/types/src/type/ExtrinsicEra.ts b/packages/types/src/type/ExtrinsicEra.ts index 48d27930970a..89e87936bb0a 100644 --- a/packages/types/src/type/ExtrinsicEra.ts +++ b/packages/types/src/type/ExtrinsicEra.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import EnumType from '../codec/EnumType'; -import {isHex, isU8a, hexToU8a, isObject, assert} from '@polkadot/util'; +import {isHex, isU8a, hexToU8a, isObject, assert, u8aToBn} from '@polkadot/util'; import Tuple from '../codec/Tuple'; import U64 from '../primitive/U64'; import U8 from '../primitive/U8'; @@ -89,14 +89,13 @@ export class MortalEra extends Tuple { 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(first).toNumber() + (new U64(second).toNumber() << 8); - const period = new U64(2 << (encoded % (1 << 4))); - const quantizeFactor = Math.max(period.toNumber() >> 12, 1); + const first = u8aToBn(value.subarray(0, 1)).toNumber(); + let second = u8aToBn(value.subarray(1, 2)).toNumber(); + const encoded: number = first + (second << 8); + const period = 2 << (encoded % (1 << 4)); + const quantizeFactor = Math.max(period >> 12, 1); let phase = (encoded >> 4) * quantizeFactor; - if (period.toNumber() >= 4 && phase < period.toNumber()) { + if (period >= 4 && phase < period) { return [new U8(period), new U8(phase)]; } throw new Error('Invalid data passed to Mortal era'); @@ -136,7 +135,7 @@ export class MortalEra extends Tuple { const period = this.period; const phase = this.phase; const quantize_factor = Math.max(period.toNumber() >> 12, 1); - const trailingZeros = this.getTrailingZeros(period); + const trailingZeros = this.getTrailingZeros(period.toNumber()); const encoded = Math.min(15, Math.max(1, trailingZeros - 1)) + (((phase.toNumber() / quantize_factor) << 4)); const encode = new U16(encoded); const first = encode.toNumber() >> 8; @@ -144,11 +143,10 @@ export class MortalEra extends Tuple { return new Uint8Array([second, first]); } - getTrailingZeros(period: U64) { + getTrailingZeros(period: number) { const zeros: number[] = []; - let periodN = period.toNumber(); - periodN = parseInt(Number(periodN).toString(2)); - //periodN = periodN.toString(2) + let periodN = period; + periodN = parseInt(periodN.toString(2)); while (periodN % 10 == 0) { periodN = periodN /= 10; @@ -158,9 +156,12 @@ export class MortalEra extends Tuple { } - birth(current: U64) { - const b = Math.floor((Math.max(current.toNumber(),this.phase.toNumber()) - this.phase.toNumber()) / this.period.toNumber()) * this.period.toNumber() + this.phase.toNumber(); + birth(current: number) { + const b = Math.floor((Math.max(current,this.phase.toNumber()) - this.phase.toNumber()) / this.period.toNumber()) * this.period.toNumber() + this.phase.toNumber(); return new U64(b); } + death(current: number) { + return this.birth(current).toNumber() + this.period.toNumber(); + } } From 14e98256489c8f4e934559bc3c179beca0581088 Mon Sep 17 00:00:00 2001 From: karishma Date: Thu, 16 May 2019 12:02:31 +1200 Subject: [PATCH 12/16] as per review comments --- packages/api/test/e2e/promise-tx.spec.js | 9 ++- packages/types/src/type/ExtrinsicEra.spec.ts | 19 +++--- packages/types/src/type/ExtrinsicEra.ts | 67 +++++++++----------- 3 files changed, 44 insertions(+), 51 deletions(-) diff --git a/packages/api/test/e2e/promise-tx.spec.js b/packages/api/test/e2e/promise-tx.spec.js index a69c019874aa..35a5560d448b 100644 --- a/packages/api/test/e2e/promise-tx.spec.js +++ b/packages/api/test/e2e/promise-tx.spec.js @@ -9,8 +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'; -import U64 from "../../../types/src/primitive/U64"; +import {ExtrinsicEra} from '@polkadot/types/type'; describe.skip('e2e transactions', () => { const keyring = testingPairs({ type: 'ed25519' }); @@ -218,7 +217,7 @@ describe.skip('e2e transactions', () => { const nonce = await api.query.system.accountNonce(keyring.dave.address()); const signedBlock = await api.rpc.chain.getBlock(); const currentHeight = signedBlock.block.header.number; - const exERA = new ExtrinsicEra({ current: new U64(currentHeight+10), period: new U64(10) }, 1); + const exERA = new ExtrinsicEra({ current: currentHeight, period: 10 }, 1); // eraBirth - start of ERA which is always less than current block height // eraDeath - end of ERA validity (EXPIRY) const eraBirth = exERA.asMortalEra.birth(currentHeight.toNumber()); @@ -236,7 +235,7 @@ describe.skip('e2e transactions', () => { const nonce = await api.query.system.accountNonce(keyring.alice.address()); const signedBlock = await api.rpc.chain.getBlock(); const currentHeight = signedBlock.block.header.number; - const exERA = new ExtrinsicEra({ current: new U64(currentHeight+5), period: new U64(4) }, 1); + const exERA = new ExtrinsicEra({ current: currentHeight, period: 4 }, 1); const eraBirth = exERA.asMortalEra.birth(currentHeight.toNumber()); const eraDeath = exERA.asMortalEra.death(currentHeight.toNumber()); console.log('STARTED AT :'+eraBirth+' EXPIRED AT :'+eraDeath); @@ -244,7 +243,7 @@ describe.skip('e2e transactions', () => { const ex = api.tx.balances.transfer(keyring.eve.address(), 12345); const unsubscribe = await api.rpc.chain.subscribeNewHead(async(header) => { console.log(`Chain is at block: #${header.blockNumber}`); - if (header.blockNumber.toNumber() === eraDeath) { + if (header.blockNumber.toNumber() === eraDeath-1) { const tx = await ex.signAndSend(keyring.alice, {blockHash: eraHash, era:exERA, nonce}); expect(tx).toBeUndefined(); done(); diff --git a/packages/types/src/type/ExtrinsicEra.spec.ts b/packages/types/src/type/ExtrinsicEra.spec.ts index 4d375c05a1a2..6143a093c036 100644 --- a/packages/types/src/type/ExtrinsicEra.spec.ts +++ b/packages/types/src/type/ExtrinsicEra.spec.ts @@ -2,28 +2,29 @@ // 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'; +import ExtrinsicEra from './ExtrinsicEra'; describe('ExtrinsicEra', () => { it('decodes an Extrinsic Era with immortal', () => { const extrinsicEra = new ExtrinsicEra(new Uint8Array([0])); + expect(extrinsicEra.asImmortalEra).toBeDefined(); }); it('decodes an Extrinsic Era from u8 as mortal', () => { const extrinsicEra = new ExtrinsicEra(new Uint8Array([1, 78, 156])); - expect((extrinsicEra.asMortalEra as MortalEra).period.toNumber()).toEqual(32768); - expect((extrinsicEra.asMortalEra as MortalEra).phase.toNumber()).toEqual(20000); - }); + expect(extrinsicEra.asMortalEra.period.toNumber()).toEqual(32768); + expect(extrinsicEra.asMortalEra.phase.toNumber()).toEqual(20000); + }); it('encode an Extrinsic Era from Object with blocknumber & period as mortal instance', () => { const mortalIndex = 1; - const extrinsicEra = new ExtrinsicEra({ current: new U64(1400), period: new U64(200) }, mortalIndex); - expect((extrinsicEra.asMortalEra as MortalEra).period.toNumber()).toBeGreaterThan(4); - expect((extrinsicEra.asMortalEra as MortalEra).period.toNumber()).toEqual(256); - expect((extrinsicEra.asMortalEra as MortalEra).phase.toNumber()).toEqual(120); + const extrinsicEra = new ExtrinsicEra({ current: 1400, period: 200 }, mortalIndex); + + expect(extrinsicEra.asMortalEra.period.toNumber()).toBeGreaterThan(4); + expect(extrinsicEra.asMortalEra.period.toNumber()).toEqual(256); + expect(extrinsicEra.asMortalEra.phase.toNumber()).toEqual(120); }); }); diff --git a/packages/types/src/type/ExtrinsicEra.ts b/packages/types/src/type/ExtrinsicEra.ts index 89e87936bb0a..23be4b8d0646 100644 --- a/packages/types/src/type/ExtrinsicEra.ts +++ b/packages/types/src/type/ExtrinsicEra.ts @@ -3,18 +3,15 @@ // of the Apache-2.0 license. See the LICENSE file for details. import EnumType from '../codec/EnumType'; -import {isHex, isU8a, hexToU8a, isObject, assert, u8aToBn} from '@polkadot/util'; +import { isHex, isU8a, hexToU8a, isObject, assert, u8aToBn } from '@polkadot/util'; import Tuple from '../codec/Tuple'; -import U64 from '../primitive/U64'; import U8 from '../primitive/U8'; -import U16 from '../primitive/U16'; import { AnyU8a } from '../types'; -import BlockNumber from "@polkadot/types/type/BlockNumber"; -import U8a from "@polkadot/types/codec/U8a"; +import U8a from '@polkadot/types/codec/U8a'; interface EraMethod { - current: BlockNumber; - period: BlockNumber; + current: number; + period: number; } export default class ExtrinsicEra extends EnumType { @@ -57,16 +54,16 @@ export default class ExtrinsicEra extends EnumType { * @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() + if (this.index === 0) { + return super.toU8a(); } else { - return (this.asMortalEra as MortalEra).toU8a(isBare); + return this.asMortalEra.toU8a(isBare); } } } export class ImmortalEra extends U8a { - constructor(value?: AnyU8a) { + constructor (value?: AnyU8a) { super(value); } } @@ -100,15 +97,14 @@ export class MortalEra extends Tuple { } throw new Error('Invalid data passed to Mortal era'); } else if (isObject(value)) { - const {current} = value; - const {period} = value; - let calPeriod = Math.pow(2, Math.ceil(Math.log2(period.toNumber()))); - calPeriod = Math.min( Math.max(calPeriod, 4), 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)]; + const { current } = value; + const { period } = value; + let calPeriod = Math.pow(2, Math.ceil(Math.log2(period))); + calPeriod = Math.min(Math.max(calPeriod, 4), 1 << 16); + const phase = current % calPeriod; + const quantizeFactor = Math.max(calPeriod >> 12, 1); + const quantizedPhase = phase / quantizeFactor * quantizeFactor; + return [new U8(calPeriod), new U8(quantizedPhase)]; } return [new U8(), new U8()]; } @@ -132,36 +128,33 @@ export class MortalEra extends Tuple { */ toU8a (isBare?: boolean): Uint8Array { - const period = this.period; - const phase = this.phase; - const quantize_factor = Math.max(period.toNumber() >> 12, 1); - const trailingZeros = this.getTrailingZeros(period.toNumber()); - const encoded = Math.min(15, Math.max(1, trailingZeros - 1)) + (((phase.toNumber() / quantize_factor) << 4)); - const encode = new U16(encoded); - const first = encode.toNumber() >> 8; - const second = encode.toNumber() & 0xff; + const period = this.period.toNumber(); + const phase = this.phase.toNumber(); + const quantizeFactor = Math.max(period >> 12, 1); + const trailingZeros = this.getTrailingZeros(period); + const encoded = Math.min(15, Math.max(1, trailingZeros - 1)) + (((phase / quantizeFactor) << 4)); + const first = encoded >> 8; + const second = encoded & 0xff; return new Uint8Array([second, first]); } - getTrailingZeros(period: number) { + getTrailingZeros (period: number) { const zeros: number[] = []; let periodN = period; - periodN = parseInt(periodN.toString(2)); + periodN = parseInt(periodN.toString(2), 10); - while (periodN % 10 == 0) { + while (periodN % 10 === 0) { periodN = periodN /= 10; zeros.push(0); } return zeros.length; } - - birth(current: number) { - const b = Math.floor((Math.max(current,this.phase.toNumber()) - this.phase.toNumber()) / this.period.toNumber()) * this.period.toNumber() + this.phase.toNumber(); - return new U64(b); + birth (current: number) { + return Math.floor((Math.max(current,this.phase.toNumber()) - this.phase.toNumber()) / this.period.toNumber()) * this.period.toNumber() + this.phase.toNumber(); } - death(current: number) { - return this.birth(current).toNumber() + this.period.toNumber(); + death (current: number) { + return this.birth(current) + this.period.toNumber(); } } From 60d88efd557435559f924be1ee63a58c696ef721 Mon Sep 17 00:00:00 2001 From: karishma Date: Thu, 16 May 2019 14:43:34 +1200 Subject: [PATCH 13/16] as per review comment --- packages/api/test/e2e/promise-tx.spec.js | 2 ++ packages/types/src/type/ExtrinsicEra.spec.ts | 1 - packages/types/src/type/ExtrinsicEra.ts | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/api/test/e2e/promise-tx.spec.js b/packages/api/test/e2e/promise-tx.spec.js index 35a5560d448b..6bae2fbbda04 100644 --- a/packages/api/test/e2e/promise-tx.spec.js +++ b/packages/api/test/e2e/promise-tx.spec.js @@ -227,6 +227,7 @@ describe.skip('e2e transactions', () => { 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(); }); @@ -245,6 +246,7 @@ describe.skip('e2e transactions', () => { console.log(`Chain is at block: #${header.blockNumber}`); if (header.blockNumber.toNumber() === eraDeath-1) { const tx = await ex.signAndSend(keyring.alice, {blockHash: eraHash, era:exERA, nonce}); + expect(tx).toBeUndefined(); done(); } diff --git a/packages/types/src/type/ExtrinsicEra.spec.ts b/packages/types/src/type/ExtrinsicEra.spec.ts index 6143a093c036..787e71d86d2b 100644 --- a/packages/types/src/type/ExtrinsicEra.spec.ts +++ b/packages/types/src/type/ExtrinsicEra.spec.ts @@ -23,7 +23,6 @@ describe('ExtrinsicEra', () => { const mortalIndex = 1; const extrinsicEra = new ExtrinsicEra({ current: 1400, period: 200 }, mortalIndex); - expect(extrinsicEra.asMortalEra.period.toNumber()).toBeGreaterThan(4); expect(extrinsicEra.asMortalEra.period.toNumber()).toEqual(256); expect(extrinsicEra.asMortalEra.phase.toNumber()).toEqual(120); }); diff --git a/packages/types/src/type/ExtrinsicEra.ts b/packages/types/src/type/ExtrinsicEra.ts index 23be4b8d0646..0e478f4c144f 100644 --- a/packages/types/src/type/ExtrinsicEra.ts +++ b/packages/types/src/type/ExtrinsicEra.ts @@ -39,14 +39,14 @@ export default class ExtrinsicEra extends EnumType { * @description `true` if Immortal */ get isImmortalEra (): boolean { - return this.type === 'ImmortalEra'; + return this.index === 0; } /** * @description `true` if Mortal */ get isMortalEra (): boolean { - return this.type === 'MortalEra'; + return this.index === 1; } /** @@ -96,7 +96,7 @@ export class MortalEra extends Tuple { return [new U8(period), new U8(phase)]; } throw new Error('Invalid data passed to Mortal era'); - } else if (isObject(value)) { + } else if (isObject(value) && value.hasOwnProperty('period') && value.hasOwnProperty('current')) { const { current } = value; const { period } = value; let calPeriod = Math.pow(2, Math.ceil(Math.log2(period))); From 09d5c257e51c1e757088218f8125738bb99a5862 Mon Sep 17 00:00:00 2001 From: karishma Date: Thu, 16 May 2019 15:32:15 +1200 Subject: [PATCH 14/16] as per review --- packages/types/src/type/ExtrinsicEra.ts | 59 ++++++++++++++++--------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/packages/types/src/type/ExtrinsicEra.ts b/packages/types/src/type/ExtrinsicEra.ts index 0e478f4c144f..e5b0e1dfa994 100644 --- a/packages/types/src/type/ExtrinsicEra.ts +++ b/packages/types/src/type/ExtrinsicEra.ts @@ -7,13 +7,13 @@ import { isHex, isU8a, hexToU8a, isObject, assert, u8aToBn } from '@polkadot/uti import Tuple from '../codec/Tuple'; import U8 from '../primitive/U8'; import { AnyU8a } from '../types'; -import U8a from '@polkadot/types/codec/U8a'; - -interface EraMethod { - current: number; - period: number; -} +import U8a from '../codec/U8a'; +/** + * @name ExtrinsicEra + * @description + * The era for an extrinsic, indicating either a mortal or immortal extrinsic + */ export default class ExtrinsicEra extends EnumType { constructor (value?: any, index?: number) { super({ ImmortalEra, MortalEra }, value, index); @@ -62,6 +62,11 @@ export default class ExtrinsicEra extends EnumType { } } +/** + * @name ImmortalEra + * @description + * The ImmortalEra for an extrinsic + */ export class ImmortalEra extends U8a { constructor (value?: AnyU8a) { super(value); @@ -70,6 +75,11 @@ export class ImmortalEra extends U8a { export type MortalEraValue = [U8, U8]; +interface MortalMethod { + current: number; + period: number; +} + /** * @name MortalEra * @description @@ -82,7 +92,7 @@ export class MortalEra extends Tuple { }, MortalEra.decodeMortalEra(value)); } - private static decodeMortalEra (value: EraMethod | Uint8Array | string): MortalEraValue { + private static decodeMortalEra (value: MortalMethod | Uint8Array | string): MortalEraValue { if (isHex(value)) { return MortalEra.decodeMortalEra(hexToU8a(value.toString())); } else if (isU8a(value)) { @@ -96,7 +106,7 @@ export class MortalEra extends Tuple { return [new U8(period), new U8(phase)]; } throw new Error('Invalid data passed to Mortal era'); - } else if (isObject(value) && value.hasOwnProperty('period') && value.hasOwnProperty('current')) { + } else if (isObject(value)) { const { current } = value; const { period } = value; let calPeriod = Math.pow(2, Math.ceil(Math.log2(period))); @@ -106,17 +116,17 @@ export class MortalEra extends Tuple { const quantizedPhase = phase / quantizeFactor * quantizeFactor; return [new U8(calPeriod), new U8(quantizedPhase)]; } - return [new U8(), new U8()]; + throw new Error('Invalid data passed to Mortal era'); } /** - * @description The justification [[U64]] + * @description The period of this Mortal wraps as a [[U8]] */ get period (): U8 { return this[0] as U8; } /** - * @description The round this justification wraps as a [[U64]] + * @description The phase of this Mortal wraps as a [[U8]] */ get phase (): U8 { return this[1] as U8; @@ -138,7 +148,24 @@ export class MortalEra extends Tuple { return new Uint8Array([second, first]); } - getTrailingZeros (period: number) { + /** + * @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(); + } + + /** + * @description Get the block number of the first block at which the era has ended. + */ + death (current: number) { + return this.birth(current) + this.period.toNumber(); + } + + /** + * @description convert the number to binary and get the trailing zero's. + */ + private getTrailingZeros (period: number) { const zeros: number[] = []; let periodN = period; periodN = parseInt(periodN.toString(2), 10); @@ -149,12 +176,4 @@ export class MortalEra extends Tuple { } return zeros.length; } - - birth (current: number) { - return Math.floor((Math.max(current,this.phase.toNumber()) - this.phase.toNumber()) / this.period.toNumber()) * this.period.toNumber() + this.phase.toNumber(); - } - - death (current: number) { - return this.birth(current) + this.period.toNumber(); - } } From 1a3931d86a12fdca6efc376ce89787690c0316ca Mon Sep 17 00:00:00 2001 From: karishma Date: Thu, 16 May 2019 16:44:29 +1200 Subject: [PATCH 15/16] review comment --- packages/types/src/type/ExtrinsicEra.spec.ts | 2 +- packages/types/src/type/ExtrinsicEra.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/types/src/type/ExtrinsicEra.spec.ts b/packages/types/src/type/ExtrinsicEra.spec.ts index 787e71d86d2b..833e5c31f4d5 100644 --- a/packages/types/src/type/ExtrinsicEra.spec.ts +++ b/packages/types/src/type/ExtrinsicEra.spec.ts @@ -7,7 +7,7 @@ import ExtrinsicEra from './ExtrinsicEra'; describe('ExtrinsicEra', () => { it('decodes an Extrinsic Era with immortal', () => { - const extrinsicEra = new ExtrinsicEra(new Uint8Array([0])); + const extrinsicEra = new ExtrinsicEra(new Uint8Array([0, 0])); expect(extrinsicEra.asImmortalEra).toBeDefined(); }); diff --git a/packages/types/src/type/ExtrinsicEra.ts b/packages/types/src/type/ExtrinsicEra.ts index e5b0e1dfa994..a50d6f84ec0a 100644 --- a/packages/types/src/type/ExtrinsicEra.ts +++ b/packages/types/src/type/ExtrinsicEra.ts @@ -62,6 +62,7 @@ export default class ExtrinsicEra extends EnumType { } } +const VALID_IMMORTAL = new U8a([0]); /** * @name ImmortalEra * @description @@ -70,6 +71,8 @@ export default class ExtrinsicEra extends EnumType { export class ImmortalEra extends U8a { constructor (value?: AnyU8a) { super(value); + + assert(this.eq(VALID_IMMORTAL), `IMMORTAL: expected ${VALID_IMMORTAL.toHex()}, found ${this.toHex()}`); } } @@ -137,7 +140,6 @@ export class MortalEra extends Tuple { * @param isBare true when the value has none of the type-specific prefixes (internal) */ toU8a (isBare?: boolean): Uint8Array { - const period = this.period.toNumber(); const phase = this.phase.toNumber(); const quantizeFactor = Math.max(period >> 12, 1); From 48da3e8d2c997739e8293a20a23f8d5dbc80f00b Mon Sep 17 00:00:00 2001 From: karishma Date: Thu, 16 May 2019 16:59:13 +1200 Subject: [PATCH 16/16] fix --- packages/types/src/type/ExtrinsicEra.spec.ts | 2 +- packages/types/src/type/ExtrinsicEra.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/types/src/type/ExtrinsicEra.spec.ts b/packages/types/src/type/ExtrinsicEra.spec.ts index 833e5c31f4d5..787e71d86d2b 100644 --- a/packages/types/src/type/ExtrinsicEra.spec.ts +++ b/packages/types/src/type/ExtrinsicEra.spec.ts @@ -7,7 +7,7 @@ import ExtrinsicEra from './ExtrinsicEra'; describe('ExtrinsicEra', () => { it('decodes an Extrinsic Era with immortal', () => { - const extrinsicEra = new ExtrinsicEra(new Uint8Array([0, 0])); + const extrinsicEra = new ExtrinsicEra(new Uint8Array([0])); expect(extrinsicEra.asImmortalEra).toBeDefined(); }); diff --git a/packages/types/src/type/ExtrinsicEra.ts b/packages/types/src/type/ExtrinsicEra.ts index a50d6f84ec0a..f047e7a08ca6 100644 --- a/packages/types/src/type/ExtrinsicEra.ts +++ b/packages/types/src/type/ExtrinsicEra.ts @@ -62,7 +62,7 @@ export default class ExtrinsicEra extends EnumType { } } -const VALID_IMMORTAL = new U8a([0]); +const VALID_IMMORTAL = new U8a([]); /** * @name ImmortalEra * @description