diff --git a/packages/api-codec/src/AccountId.ts b/packages/api-codec/src/AccountId.ts index 06f811391384..914283fae856 100644 --- a/packages/api-codec/src/AccountId.ts +++ b/packages/api-codec/src/AccountId.ts @@ -9,16 +9,16 @@ import isHex from '@polkadot/util/is/hex'; import isU8a from '@polkadot/util/is/u8a'; import u8aToU8a from '@polkadot/util/u8a/toU8a'; -import CodecU8a from './codec/U8a'; -import CodecU8aFixed from './codec/U8aFixed'; +import U8a from './codec/U8a'; +import U8aFixed from './codec/U8aFixed'; // A wrapper around an AccountId/PublicKey representation. Since we are dealing with -// underlying PublicKeys (32 bytes in length), we extend from CodecU8aFixed which is +// underlying PublicKeys (32 bytes in length), we extend from U8aFixed which is // basically just a Uint8Array wrapper with a fixed length. -export default class AccountId extends CodecU8aFixed { - constructor (value: CodecU8a | string | Uint8Array = new Uint8Array()) { +export default class AccountId extends U8aFixed { + constructor (value: U8a | string | Uint8Array = new Uint8Array()) { super( - value instanceof CodecU8a + value instanceof U8a ? value.raw : AccountId.decode(value), 256 diff --git a/packages/api-codec/src/AccountIndex.ts b/packages/api-codec/src/AccountIndex.ts index 6a08d36c49b6..11ec565d1fac 100644 --- a/packages/api-codec/src/AccountIndex.ts +++ b/packages/api-codec/src/AccountIndex.ts @@ -2,21 +2,18 @@ // This software may be modified and distributed under the terms // of the ISC license. See the LICENSE file for details. -import hexToU8a from '@polkadot/util/hex/toU8a'; -import isHex from '@polkadot/util/is/hex'; -import isU8a from '@polkadot/util/is/u8a'; import u8aToHex from '@polkadot/util/u8a/toHex'; import u8aToU8a from '@polkadot/util/u8a/toU8a'; -import CodecU8a from './codec/U8a'; +import U8a from './codec/U8a'; // A wrapper around an AccountIndex, which is a shortened, variable-length encoding -// for an Account. We extends from CodecU8a which is basically +// for an Account. We extends from U8a which is basically // just a Uint8Array wrapper. -export default class AccountIndex extends CodecU8a { - constructor (value: CodecU8a | string | Uint8Array = new Uint8Array()) { +export default class AccountIndex extends U8a { + constructor (value: U8a | string | Uint8Array = new Uint8Array()) { super( - value instanceof CodecU8a + value instanceof U8a ? value.raw : AccountIndex.decode(value) ); @@ -29,13 +26,7 @@ export default class AccountIndex extends CodecU8a { } static decode (value: string | Uint8Array | Array): Uint8Array { - if (isU8a(value) || Array.isArray(value)) { - return u8aToU8a(value); - } else if (isHex(value)) { - return hexToU8a(value); - } - - throw new Error(`Unable to decode AccountIndex for [${value.toString()}]`); + return u8aToU8a(value); } fromJSON (input: any): AccountIndex { diff --git a/packages/api-codec/src/Address.ts b/packages/api-codec/src/Address.ts index cd7abd9c9a16..57494d963dcb 100644 --- a/packages/api-codec/src/Address.ts +++ b/packages/api-codec/src/Address.ts @@ -7,7 +7,7 @@ import isU8a from '@polkadot/util/is/u8a'; import u8aConcat from '@polkadot/util/u8a/concat'; import u8aToU8a from '@polkadot/util/u8a/toU8a'; -import CodecBase from './codec/Base'; +import Base from './codec/Base'; import AccountId from './AccountId'; import AccountIndex from './AccountIndex'; @@ -16,7 +16,7 @@ import AccountIndex from './AccountIndex'; // we extend from Base with an AccountId/AccountIndex wrapper. Basically the Address // is encoded as // [ , ...publicKey/...bytes ] -export default class Address extends CodecBase { +export default class Address extends Base { constructor (value: Address | AccountId | AccountIndex | string | Uint8Array = new Uint8Array()) { super( value instanceof Address diff --git a/packages/api-codec/src/Block.spec.js b/packages/api-codec/src/Block.spec.js new file mode 100644 index 000000000000..26275324f9df --- /dev/null +++ b/packages/api-codec/src/Block.spec.js @@ -0,0 +1,51 @@ +// Copyright 2017-2018 @polkadot/api-codec authors & contributors +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +import hexToU8a from '@polkadot/util/hex/toU8a'; + +import Block from './Block'; + +describe('Block', () => { + it('decodes a block properly', () => { + const block = new Block(); + + block.fromU8a(hexToU8a( + '0x' + + // parent_hash + '0900000000000000000000000000000000000000000000000000000000000009' + + // number + '4300000000000000' + + // state_root + '0800000000000000000000000000000000000000000000000000000000000008' + + // transaction_root + '0700000000000000000000000000000000000000000000000000000000000007' + + // digest + '08' + // 2 << 2 + '0401' + + '0402' + + // transactions + '04' + // 1 << 2 + // 111 bytes in compact encoding + 'bd01' + + // prefix + 'ff' + + '0000000000000000000000000000000000000000000000000000000000000000' + + '00000000' + + '0300' + + '7527000000000000' + + '0000000000000000000000000000000000000000000000000000000000000000' + + '0000000000000000000000000000000000000000000000000000000000000000' + )); + + expect( + block.header.blockNumber.toNumber() + ).toEqual(67); + expect( + block.extrinsics.length + ).toEqual(1); // eslint-disable-line + expect( + block.extrinsics.at(0).length + ).toEqual(111); // eslint-disable-line + }); +}); diff --git a/packages/api-codec/src/Block.ts b/packages/api-codec/src/Block.ts new file mode 100644 index 000000000000..92d8c0f99c22 --- /dev/null +++ b/packages/api-codec/src/Block.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2018 @polkadot/api-codec authors & contributors +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +import Struct from './codec/Struct'; +import Vector from './codec/Vector'; + +import Extrinsic from './Extrinsic'; +import Header from './Header'; + +// A block encoded with header and extrinsics +export default class Block extends Struct { + constructor (value?: any) { + super({ + header: Header, + extrinsics: Vector.with(Extrinsic) + }, value); + } + + get extrinsics (): Vector { + return this.raw.extrinsics as Vector; + } + + get header (): Header { + return this.raw.header as Header; + } +} diff --git a/packages/api-codec/src/Bool.ts b/packages/api-codec/src/Bool.ts index bbb05d8951a7..4a3b447c49a0 100644 --- a/packages/api-codec/src/Bool.ts +++ b/packages/api-codec/src/Bool.ts @@ -2,9 +2,9 @@ // This software may be modified and distributed under the terms // of the ISC license. See the LICENSE file for details. -import CodecBase from './codec/Base'; +import Base from './codec/Base'; -export default class Bool extends CodecBase { +export default class Bool extends Base { constructor (value: Bool | boolean = false) { super( value instanceof Bool diff --git a/packages/api-codec/src/Extrinsic.ts b/packages/api-codec/src/Extrinsic.ts new file mode 100644 index 000000000000..516d4e40f2e3 --- /dev/null +++ b/packages/api-codec/src/Extrinsic.ts @@ -0,0 +1,22 @@ +// Copyright 2017-2018 @polkadot/api-codec authors & contributors +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +import blake2Asu8a from '@polkadot/util-crypto/blake2/asU8a'; + +import Bytes from './codec/Bytes'; + +import Hash from './Hash'; + +// Representation of an Extrinsic in the system. +// +// NOTE At this point we are just keeping to a bare-bones Vec structure, +// this should be expanded to do the automatic decoding of an extrinsic +export default class Extrinsic extends Bytes { + // convernience, encodes the extrinsic and returns the actual hash + get hash (): Hash { + return new Hash( + blake2Asu8a(this.toU8a(), 256) + ); + } +} diff --git a/packages/api-codec/src/H256.ts b/packages/api-codec/src/H256.ts index 8c3fafe90e89..ab534091190b 100644 --- a/packages/api-codec/src/H256.ts +++ b/packages/api-codec/src/H256.ts @@ -2,13 +2,13 @@ // This software may be modified and distributed under the terms // of the ISC license. See the LICENSE file for details. -import CodecU8a from './codec/U8a'; -import CodecU8aFixed from './codec/U8aFixed'; +import U8a from './codec/U8a'; +import U8aFixed from './codec/U8aFixed'; // Hash containing 256 bits (32 bytes), typically used in blocks, extrinsics and // as a sane default for fixed-length hash representations. -export default class H256 extends CodecU8aFixed { - constructor (value?: CodecU8a | Uint8Array) { +export default class H256 extends U8aFixed { + constructor (value?: U8a | string | Uint8Array) { super(value, 256); } } diff --git a/packages/api-codec/src/H512.ts b/packages/api-codec/src/H512.ts index 2c713a3ac720..4c12b7c3d64d 100644 --- a/packages/api-codec/src/H512.ts +++ b/packages/api-codec/src/H512.ts @@ -2,12 +2,12 @@ // This software may be modified and distributed under the terms // of the ISC license. See the LICENSE file for details. -import CodecU8a from './codec/U8a'; -import CodecU8aFixed from './codec/U8aFixed'; +import U8a from './codec/U8a'; +import U8aFixed from './codec/U8aFixed'; // Hash containing 512 bits (64 bytes), typically used for signatures -export default class H512 extends CodecU8aFixed { - constructor (value?: CodecU8a | Uint8Array) { +export default class H512 extends U8aFixed { + constructor (value?: U8a | string | Uint8Array) { super(value, 512); } } diff --git a/packages/api-codec/src/Header.ts b/packages/api-codec/src/Header.ts index 3272e523d00f..1082bc59fb93 100644 --- a/packages/api-codec/src/Header.ts +++ b/packages/api-codec/src/Header.ts @@ -2,29 +2,45 @@ // This software may be modified and distributed under the terms // of the ISC license. See the LICENSE file for details. -import CodecArray from './codec/Array'; -import CodecBytes from './codec/Bytes'; -import CodecStruct from './codec/Struct'; +import BN from 'bn.js'; +import blake2Asu8a from '@polkadot/util-crypto/blake2/asU8a'; + +import Bytes from './codec/Bytes'; +import UInt from './codec/UInt'; +import Struct from './codec/Struct'; +import Vector from './codec/Vector'; import BlockNumber from './BlockNumber'; import Hash from './Hash'; +type DigestStruct = { + logs?: Array +}; + +type HeaderStruct = { + digest?: DigestStruct, + extrinsicsRoot?: Hash | Uint8Array | string, + number?: UInt | BN | number | string, + parentHash?: Hash | Uint8Array | string, + stateRoot?: Hash | Uint8Array | string +}; + // A block header digest. -export class Digest extends CodecStruct { - constructor (value?: any) { +export class Digest extends Struct { + constructor (value: DigestStruct = {}) { super({ - logs: CodecArray.with(CodecBytes) + logs: Vector.with(Bytes) }, value); } - get logs (): CodecArray { - return this.raw.logs as CodecArray; + get logs (): Vector { + return this.raw.logs as Vector; } } // A block header. -export default class Header extends CodecStruct { - constructor (value?: any) { +export default class Header extends Struct { + constructor (value: HeaderStruct = {}) { super({ parentHash: Hash, number: BlockNumber, @@ -46,6 +62,13 @@ export default class Header extends CodecStruct { return this.raw.extrinsicsRoot as Hash; } + // convernience, encodes the header and returns the actual hash + get hash (): Hash { + return new Hash( + blake2Asu8a(this.toU8a(), 256) + ); + } + get parentHash (): Hash { return this.raw.parentHash as Hash; } diff --git a/packages/api-codec/src/KeyValue.ts b/packages/api-codec/src/KeyValue.ts index 088e9efd0870..a260e3240044 100644 --- a/packages/api-codec/src/KeyValue.ts +++ b/packages/api-codec/src/KeyValue.ts @@ -2,31 +2,31 @@ // This software may be modified and distributed under the terms // of the ISC license. See the LICENSE file for details. -import CodecBytes from './codec/Bytes'; -import CodecStruct from './codec/Struct'; +import Bytes from './codec/Bytes'; +import Struct from './codec/Struct'; type KeyValueStruct = { - key: Uint8Array, - value: Uint8Array + key?: Uint8Array | string, + value?: Uint8Array | string }; // KeyValue structure. Since most of the keys and resultant values in Subtrate is -// hashed and/or encoded, this does not wrap a CodecString, but rather a CodecBytes +// hashed and/or encoded, this does not wrap a Text, but rather a Bytes // for the keys and values. (Not to be confused with the KeyValue in Metadata, that // is actually for Maps, whereas this is a representation of actaul storage values) -export default class KeyValue extends CodecStruct { - constructor (value: KeyValueStruct = {} as KeyValueStruct) { +export default class KeyValue extends Struct { + constructor (value: KeyValueStruct = {}) { super({ - key: CodecBytes, - value: CodecBytes + key: Bytes, + value: Bytes }, value); } - get key (): CodecBytes { - return this.raw.key as CodecBytes; + get key (): Bytes { + return this.raw.key as Bytes; } - get value (): CodecBytes { - return this.raw.value as CodecBytes; + get value (): Bytes { + return this.raw.value as Bytes; } } diff --git a/packages/api-codec/src/Metadata.ts b/packages/api-codec/src/Metadata.ts index f8218f81c898..eba07b54b674 100644 --- a/packages/api-codec/src/Metadata.ts +++ b/packages/api-codec/src/Metadata.ts @@ -2,13 +2,13 @@ // This software may be modified and distributed under the terms // of the ISC license. See the LICENSE file for details. -import CodecArray from './codec/Array'; -import CodecBase from './codec/Base'; -import CodecEnum from './codec/Enum'; -import CodecEnumType from './codec/EnumType'; -import CodecOption from './codec/Option'; -import CodecStruct from './codec/Struct'; -import String from './String'; +import Vector from './codec/Vector'; +import Base from './codec/Base'; +import Enum from './codec/Enum'; +import EnumType from './codec/EnumType'; +import Option from './codec/Option'; +import Struct from './codec/Struct'; +import Text from './Text'; import Type from './Type'; import U16 from './U16'; @@ -16,72 +16,72 @@ import U16 from './U16'; // file is probably best understood from the bottom-up, i.e. start reading right at the // end and work up. (Just so we don't use before definition) -class EventMetadata extends CodecStruct { +export class EventMetadata extends Struct { constructor () { super({ - name: String, - arguments: CodecArray.with(Type), - documentation: CodecArray.with(String) + name: Text, + arguments: Vector.with(Type), + documentation: Vector.with(Text) }); } - get arguments (): CodecArray { - return this.raw.arguments as CodecArray; + get arguments (): Vector { + return this.raw.arguments as Vector; } - get documentation (): CodecArray { - return this.raw.documentation as CodecArray; + get documentation (): Vector { + return this.raw.documentation as Vector; } - get name (): String { - return this.raw.name as String; + get name (): Text { + return this.raw.name as Text; } } -class OuterEventMetadataEvent extends CodecStruct { +export class OuterEventMetadataEvent extends Struct { constructor () { super({ - name: String, - events: CodecArray.with(EventMetadata) + name: Text, + events: Vector.with(EventMetadata) }); } - get events (): CodecArray { - return this.raw.events as CodecArray; + get events (): Vector { + return this.raw.events as Vector; } - get name (): String { - return this.raw.name as String; + get name (): Text { + return this.raw.name as Text; } } -class OuterEventMetadata extends CodecStruct { +export class OuterEventMetadata extends Struct { constructor () { super({ - name: String, - events: CodecArray.with(OuterEventMetadataEvent) + name: Text, + events: Vector.with(OuterEventMetadataEvent) }); } - get events (): CodecArray { - return this.raw.events as CodecArray; + get events (): Vector { + return this.raw.events as Vector; } - get name (): String { - return this.raw.name as String; + get name (): Text { + return this.raw.name as Text; } } -class FunctionArgumentMetadata extends CodecStruct { +export class FunctionArgumentMetadata extends Struct { constructor () { super({ - name: String, + name: Text, type: Type }); } - get name (): String { - return this.raw.name as String; + get name (): Text { + return this.raw.name as Text; } get type (): Type { @@ -89,54 +89,54 @@ class FunctionArgumentMetadata extends CodecStruct { } } -class FunctionMetadata extends CodecStruct { +export class FunctionMetadata extends Struct { constructor () { super({ id: U16, - name: String, - arguments: CodecArray.with(FunctionArgumentMetadata), - documentation: CodecArray.with(String) + name: Text, + arguments: Vector.with(FunctionArgumentMetadata), + documentation: Vector.with(Text) }); } - get arguments (): CodecArray { - return this.raw.arguments as CodecArray; + get arguments (): Vector { + return this.raw.arguments as Vector; } - get documentation (): CodecArray { - return this.raw.documentation as CodecArray; + get documentation (): Vector { + return this.raw.documentation as Vector; } get id (): U16 { return this.raw.id as U16; } - get name (): String { - return this.raw.name as String; + get name (): Text { + return this.raw.name as Text; } } -class CallMetadata extends CodecStruct { +export class CallMetadata extends Struct { constructor () { super({ - name: String, - functions: CodecArray.with(FunctionMetadata) + name: Text, + functions: Vector.with(FunctionMetadata) }); } - get functions (): CodecArray { - return this.raw.functions as CodecArray; + get functions (): Vector { + return this.raw.functions as Vector; } - get name (): String { - return this.raw.name as String; + get name (): Text { + return this.raw.name as Text; } } -class ModuleMetadata extends CodecStruct { +export class ModuleMetadata extends Struct { constructor () { super({ - name: String, + name: Text, call: CallMetadata }); } @@ -145,18 +145,18 @@ class ModuleMetadata extends CodecStruct { return this.raw.call as CallMetadata; } - get name (): String { - return this.raw.name as String; + get name (): Text { + return this.raw.name as Text; } } -class StorageFunctionModifier extends CodecEnum { +export class StorageFunctionModifier extends Enum { constructor () { super(['None', 'Default', 'Required']); } } -class StorageFunctionType$Map extends CodecStruct { +export class StorageFunctionType$Map extends Struct { constructor () { super({ key: Type, @@ -173,7 +173,7 @@ class StorageFunctionType$Map extends CodecStruct { } } -class StorageFunctionType extends CodecEnumType { +export class StorageFunctionType extends EnumType { constructor () { super([ Type, @@ -186,30 +186,30 @@ class StorageFunctionType extends CodecEnumType } get asMap (): StorageFunctionType$Map { - return (this.raw as CodecBase).raw; + return (this.raw as Base).raw; } get asType (): Type { - return (this.raw as CodecBase).raw; + return (this.raw as Base).raw; } } -class StorageFunctionMetadata extends CodecStruct { +export class StorageFunctionMetadata extends Struct { constructor () { super({ - name: String, + name: Text, modifier: StorageFunctionModifier, type: StorageFunctionType, - documentation: CodecArray.with(String) + documentation: Vector.with(Text) }); } - get documentation (): CodecArray { - return this.raw.documentation as CodecArray; + get documentation (): Vector { + return this.raw.documentation as Vector; } - get name (): String { - return this.raw.name as String; + get name (): Text { + return this.raw.name as Text; } get modifier (): StorageFunctionModifier { @@ -221,29 +221,29 @@ class StorageFunctionMetadata extends CodecStruct { } } -class StorageMetadata extends CodecStruct { +export class StorageMetadata extends Struct { constructor () { super({ - prefix: String, - functions: CodecArray.with(StorageFunctionMetadata) + prefix: Text, + functions: Vector.with(StorageFunctionMetadata) }); } - get functions (): CodecArray { - return this.raw.functions as CodecArray; + get functions (): Vector { + return this.raw.functions as Vector; } - get prefix (): String { - return this.raw.prefix as String; + get prefix (): Text { + return this.raw.prefix as Text; } } -class RuntimeModuleMetadata extends CodecStruct { +export class RuntimeModuleMetadata extends Struct { constructor () { super({ - prefix: String, + prefix: Text, module: ModuleMetadata, - storage: CodecOption.with(StorageMetadata) + storage: Option.with(StorageMetadata) }); } @@ -251,20 +251,20 @@ class RuntimeModuleMetadata extends CodecStruct { return this.raw.module as ModuleMetadata; } - get prefix (): String { - return this.raw.prefix as String; + get prefix (): Text { + return this.raw.prefix as Text; } get storage (): StorageMetadata | undefined { - return (this.raw.storage as CodecOption).value; + return (this.raw.storage as Option).value; } } -export default class RuntimeMetadata extends CodecStruct { +export default class RuntimeMetadata extends Struct { constructor (value?: any) { super({ outerEvent: OuterEventMetadata, - modules: CodecArray.with(RuntimeModuleMetadata) + modules: Vector.with(RuntimeModuleMetadata) }, value); } @@ -281,16 +281,16 @@ export default class RuntimeMetadata extends CodecStruct { } // FIXME Really not crazy about having to manually add all the getters. Preferably it should - // be done automagically in the actual CodecStruct - however what is really important here + // be done automagically in the actual Struct - however what is really important here // here is that we should nbot lose the autocompletion and checking that TS gives us. So if // we have to choose between the 2, manual defs it would have to be. - get events (): CodecArray { + get events (): Vector { return (this.raw.outerEvent as OuterEventMetadata).events; } - get modules (): CodecArray { - return this.raw.modules as CodecArray; + get modules (): Vector { + return this.raw.modules as Vector; } // Helper to retrieve a list of all type that are found, sorted and de-deuplicated diff --git a/packages/api-codec/src/Index.ts b/packages/api-codec/src/Nonce.ts similarity index 100% rename from packages/api-codec/src/Index.ts rename to packages/api-codec/src/Nonce.ts diff --git a/packages/api-codec/src/PropIndex.ts b/packages/api-codec/src/PropIndex.ts new file mode 100644 index 000000000000..072a6ac78d5a --- /dev/null +++ b/packages/api-codec/src/PropIndex.ts @@ -0,0 +1,9 @@ +// Copyright 2017-2018 @polkadot/api-codec authors & contributors +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +import U32 from './U32'; + +// An increasing number that represents a specific proposal in the system +export default class PropIndex extends U32 { +} diff --git a/packages/api-codec/src/RawAddress.ts b/packages/api-codec/src/RawAddress.ts new file mode 100644 index 000000000000..9c0650f9c910 --- /dev/null +++ b/packages/api-codec/src/RawAddress.ts @@ -0,0 +1,10 @@ +// Copyright 2017-2018 @polkadot/api-codec authors & contributors +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +import Address from './Address'; + +// Wrapper for a RawAddress. +// NOTE Not 100% on the differences between this and Address in the runtime +export default class RawAddress extends Address { +} diff --git a/packages/api-codec/src/String.ts b/packages/api-codec/src/Text.ts similarity index 88% rename from packages/api-codec/src/String.ts rename to packages/api-codec/src/Text.ts index 3bae4651d169..118071b05b6c 100644 --- a/packages/api-codec/src/String.ts +++ b/packages/api-codec/src/Text.ts @@ -6,7 +6,7 @@ import u8aFromUtf8 from '@polkadot/util/u8a/fromUtf8'; import u8aToUtf8 from '@polkadot/util/u8a/toUtf8'; import u8aConcat from '@polkadot/util/u8a/concat'; -import CodecBase from './codec/Base'; +import Base from './codec/Base'; import Length from './codec/Length'; // This is a string wrapper, along with the length. It is used both for strings as well @@ -17,14 +17,14 @@ import Length from './codec/Length'; // - Potentially we want a "TypeString" extension to this. Basically something that // wraps the `Balance`, `T::AccountId`, etc. The reasoning - with a "TypeString" // we can nicely strip types down like "T::AcountId" -> "AccountId" -export default class String extends CodecBase { +export default class Text extends Base { protected _length: Length; - constructor (value: String | string = '') { + constructor (value: Text | string = '') { super( - value instanceof String + value instanceof Text ? value.raw - : value + : value.trim() ); this._length = new Length(value.length); @@ -41,13 +41,13 @@ export default class String extends CodecBase { this._length.byteLength(); } - fromJSON (input: any): String { + fromJSON (input: any): Text { this.raw = `${input}`; return this; } - fromU8a (input: Uint8Array): String { + fromU8a (input: Uint8Array): Text { this._length.fromU8a(input); const length = this._length.toNumber(); diff --git a/packages/api-codec/src/Type.spec.js b/packages/api-codec/src/Type.spec.js index f3bf369303b5..7d86ff5186c7 100644 --- a/packages/api-codec/src/Type.spec.js +++ b/packages/api-codec/src/Type.spec.js @@ -2,6 +2,7 @@ // This software may be modified and distributed under the terms // of the ISC license. See the LICENSE file for details. +import Text from './Text'; import Type from './Type'; describe('Type', () => { @@ -17,15 +18,24 @@ describe('Type', () => { ).toEqual('Vec'); }); - it('handles aliasses, multiples per line', () => { - expect( - new Type().fromJSON('(PropIndex, AccountId, PropIndex)').toString() - ).toEqual('(ProposalIndex, AccountId, ProposalIndex)'); - }); + // currently no aliasses, code left as a comment, as here + // it('handles aliasses, multiples per line', () => { + // expect( + // new Type().fromJSON('(PropIndex, AccountId, PropIndex)').toString() + // ).toEqual('(ProposalIndex, AccountId, ProposalIndex)'); + // }); it('does not allow toU8a', () => { expect( () => new Type().toU8a() ).toThrow(/unimplemented/); }); + + it('has a length for the type', () => { + expect( + new Type( + new Text(' Box ') + ).length + ).toEqual('Proposal'.length); // eslint-disable-line + }); }); diff --git a/packages/api-codec/src/Type.ts b/packages/api-codec/src/Type.ts index 446743950c65..0e088e05fcf1 100644 --- a/packages/api-codec/src/Type.ts +++ b/packages/api-codec/src/Type.ts @@ -2,14 +2,20 @@ // This software may be modified and distributed under the terms // of the ISC license. See the LICENSE file for details. -import String from './String'; +import Text from './Text'; type Mapper = (value: string) => string; // This is a extended version of String, specifically to handle types. Here we rely full on // what string provides us, however we also "tweak" the types received from the runtime, i.e. // we remove the `T::` prefixes found in some types for consistency accross implementation. -export default class Type extends String { +export default class Type extends Text { + constructor (value?: Text | string) { + super(value); + + this._cleanupTypes(); + } + fromJSON (input: any): Type { super.fromJSON(input); @@ -37,11 +43,7 @@ export default class Type extends String { // remove boxing, `Box` -> `Proposal` this._unwrap('Box<'), // remove generics, `MisbehaviorReport` -> `MisbehaviorReport` - this._ungeneric(), - // convert `RawAddress` -> `Address` - this._unalias('RawAddress', 'Address'), - // convert `PropIndex` -> `ProposalIndex` - this._unalias('PropIndex', 'ProposalIndex') + this._ungeneric() ]; this.raw = mappings.reduce((result, fn) => { @@ -70,15 +72,17 @@ export default class Type extends String { throw new Error(`Unable to find closing matching <> on '${value}' (start ${start})`); } - private _unalias (src: string, dest: string): Mapper { - return (value: string): string => { - while (value.indexOf(src) !== -1) { - value = value.replace(src, dest); - } - - return value; - }; - } + // convert `RawAddress` -> `Address` + // convert `PropIndex` -> `ProposalIndex` + // private _unalias (src: string, dest: string): Mapper { + // return (value: string): string => { + // while (value.indexOf(src) !== -1) { + // value = value.replace(src, dest); + // } + + // return value; + // }; + // } private _ungeneric (): Mapper { return (value: string): string => { diff --git a/packages/api-codec/src/U128.ts b/packages/api-codec/src/U128.ts index 3aec9a7cca99..3720ff935e10 100644 --- a/packages/api-codec/src/U128.ts +++ b/packages/api-codec/src/U128.ts @@ -4,10 +4,10 @@ import BN from 'bn.js'; -import CodecNumber from './codec/Number'; +import UInt from './codec/UInt'; -export default class U128 extends CodecNumber { - constructor (value?: CodecNumber | BN | string | number) { +export default class U128 extends UInt { + constructor (value?: UInt | BN | string | number) { super(value, 128); } } diff --git a/packages/api-codec/src/U16.ts b/packages/api-codec/src/U16.ts index 6c16910280e1..a64cbdf28715 100644 --- a/packages/api-codec/src/U16.ts +++ b/packages/api-codec/src/U16.ts @@ -4,10 +4,10 @@ import BN from 'bn.js'; -import CodecNumber from './codec/Number'; +import UInt from './codec/UInt'; -export default class U16 extends CodecNumber { - constructor (value?: CodecNumber | BN | string | number) { +export default class U16 extends UInt { + constructor (value?: UInt | BN | string | number) { super(value, 16); } } diff --git a/packages/api-codec/src/U256.ts b/packages/api-codec/src/U256.ts index bbe765a38785..6edcaefac973 100644 --- a/packages/api-codec/src/U256.ts +++ b/packages/api-codec/src/U256.ts @@ -4,10 +4,10 @@ import BN from 'bn.js'; -import CodecNumber from './codec/Number'; +import UInt from './codec/UInt'; -export default class U256 extends CodecNumber { - constructor (value?: CodecNumber | BN | string | number) { +export default class U256 extends UInt { + constructor (value?: UInt | BN | string | number) { super(value, 256); } } diff --git a/packages/api-codec/src/U32.ts b/packages/api-codec/src/U32.ts index 3b99f1eff99b..2f1453b1a23e 100644 --- a/packages/api-codec/src/U32.ts +++ b/packages/api-codec/src/U32.ts @@ -4,10 +4,10 @@ import BN from 'bn.js'; -import CodecNumber from './codec/Number'; +import UInt from './codec/UInt'; -export default class U32 extends CodecNumber { - constructor (value?: CodecNumber | BN | string | number) { +export default class U32 extends UInt { + constructor (value?: UInt | BN | string | number) { super(value, 32); } } diff --git a/packages/api-codec/src/U64.ts b/packages/api-codec/src/U64.ts index 08c1e9dca844..56770ca2f620 100644 --- a/packages/api-codec/src/U64.ts +++ b/packages/api-codec/src/U64.ts @@ -4,10 +4,10 @@ import BN from 'bn.js'; -import CodecNumber from './codec/Number'; +import UInt from './codec/UInt'; -export default class U64 extends CodecNumber { - constructor (value?: CodecNumber | BN | string | number) { +export default class U64 extends UInt { + constructor (value?: UInt | BN | string | number) { super(value, 64); } } diff --git a/packages/api-codec/src/U8.ts b/packages/api-codec/src/U8.ts index 7f21c6ab17ed..0860b5e4b6f4 100644 --- a/packages/api-codec/src/U8.ts +++ b/packages/api-codec/src/U8.ts @@ -4,10 +4,10 @@ import BN from 'bn.js'; -import CodecNumber from './codec/Number'; +import UInt from './codec/UInt'; -export default class U8 extends CodecNumber { - constructor (value?: CodecNumber | BN | string | number) { +export default class U8 extends UInt { + constructor (value?: UInt | BN | string | number) { super(value, 8); } } diff --git a/packages/api-codec/src/codec/Base.ts b/packages/api-codec/src/codec/Base.ts index 90a74c36af7d..ca20d028056c 100644 --- a/packages/api-codec/src/codec/Base.ts +++ b/packages/api-codec/src/codec/Base.ts @@ -8,7 +8,7 @@ // // TODO // - This could probably be abstract, as long as we have the functions abstract as well -export default class CodecBase { +export default class Base { raw: T; constructor (value?: any) { @@ -16,26 +16,26 @@ export default class CodecBase { } byteLength (): number { - throw new Error('CodecBase::byteLength: unimplemented'); + throw new Error('Base::byteLength: unimplemented'); } - fromJSON (input: any): CodecBase { - throw new Error('CodecBase::fromJSON: unimplemented'); + fromJSON (input: any): Base { + throw new Error('Base::fromJSON: unimplemented'); } - fromU8a (input: Uint8Array): CodecBase { - throw new Error('CodecBase::fromU8a: unimplemented'); + fromU8a (input: Uint8Array): Base { + throw new Error('Base::fromU8a: unimplemented'); } toJSON (): any { - throw new Error('CodecBase::toJSON: unimplemented'); + throw new Error('Base::toJSON: unimplemented'); } toString (): string { - throw new Error('CodecBase::toString: unimplemented'); + throw new Error('Base::toString: unimplemented'); } toU8a (): Uint8Array { - throw new Error('CodecBase::toU8a: unimplemented'); + throw new Error('Base::toU8a: unimplemented'); } } diff --git a/packages/api-codec/src/codec/Bytes.ts b/packages/api-codec/src/codec/Bytes.ts index 30103b44091a..1c51554b3fb4 100644 --- a/packages/api-codec/src/codec/Bytes.ts +++ b/packages/api-codec/src/codec/Bytes.ts @@ -4,31 +4,30 @@ import u8aConcat from '@polkadot/util/u8a/concat'; -import CodecU8a from './U8a'; import Length from './Length'; +import U8a from './U8a'; -// A CodecBytes. The significant difference between this and a normal Uint8Array is that +// A Bytes. The significant difference between this and a normal Uint8Array is that // this version allows for length-encoding. (i.e. it is a variable-item codec, the same // as what is found in String and Array) -export default class CodecBytes extends CodecU8a { +export default class Bytes extends U8a { protected _length: Length; - constructor (value: CodecU8a | Uint8Array = new Uint8Array()) { - super( - value instanceof CodecU8a - ? value.raw - : value - ); + constructor (value?: U8a | string | Uint8Array) { + super(value); + + this._length = new Length(this.raw.length); + } - this._length = new Length(value.length); + get length (): number { + return this._length.toNumber(); } byteLength (): number { - return this._length.byteLength() + - this._length.toNumber(); + return this._length.byteLength() + this.length; } - fromJSON (input: any): CodecBytes { + fromJSON (input: any): Bytes { super.fromJSON(input); this._length.setValue(this.raw.length); @@ -36,7 +35,7 @@ export default class CodecBytes extends CodecU8a { return this; } - fromU8a (input: Uint8Array): CodecBytes { + fromU8a (input: Uint8Array): Bytes { this._length.fromU8a(input); const length = this._length.toNumber(); diff --git a/packages/api-codec/src/codec/Enum.ts b/packages/api-codec/src/codec/Enum.ts index 1bee0ea12f3a..03ed40350271 100644 --- a/packages/api-codec/src/codec/Enum.ts +++ b/packages/api-codec/src/codec/Enum.ts @@ -2,7 +2,7 @@ // This software may be modified and distributed under the terms // of the ISC license. See the LICENSE file for details. -import CodecBase from './Base'; +import Base from './Base'; // A codec wrapper for an enum. Enums are encoded as a single byte, where the byte // is a zero-indexed value. This class allows you to retrieve the value either @@ -11,7 +11,7 @@ import CodecBase from './Base'; // // TODO: // - It would be great if this could actually wrap actual TS enums -export default class CodecEnum extends CodecBase { +export default class Enum extends Base { private _strings: Array; constructor (strings: Array, value: number = 0) { @@ -24,13 +24,13 @@ export default class CodecEnum extends CodecBase { return 1; } - fromJSON (input: any): CodecEnum { + fromJSON (input: any): Enum { this.raw = input; return this; } - fromU8a (input: Uint8Array): CodecEnum { + fromU8a (input: Uint8Array): Enum { this.raw = input[0]; return this; diff --git a/packages/api-codec/src/codec/EnumType.ts b/packages/api-codec/src/codec/EnumType.ts index 48bd13586fda..9b5d49c53010 100644 --- a/packages/api-codec/src/codec/EnumType.ts +++ b/packages/api-codec/src/codec/EnumType.ts @@ -2,7 +2,7 @@ // This software may be modified and distributed under the terms // of the ISC license. See the LICENSE file for details. -import CodecBase from './Base'; +import Base from './Base'; // This implements an enum, that based on the value wraps a different type. It is effectively an // extension to enum where the value type is determined by the actual index. @@ -11,12 +11,12 @@ import CodecBase from './Base'; // - As per Enum, actually use TS enum // - It should rather probably extend Enum instead of copying code // - There doesn't actually seem to be a way to get to the actual determined/wrapped value -export default class CodecEnumType extends CodecBase> { - private _Type: Array<{ new(value?: any): CodecBase }>; +export default class EnumType extends Base> { + private _Type: Array<{ new(value?: any): Base }>; private _index: number; private _strings: Array; - constructor (Type: Array<{ new(value?: any): CodecBase }>, strings: Array, index: number = 0) { + constructor (Type: Array<{ new(value?: any): Base }>, strings: Array, index: number = 0) { super( new Type[index]() ); @@ -30,11 +30,11 @@ export default class CodecEnumType extends CodecBase> { return 1 + this.raw.byteLength(); } - fromJSON (input: any): CodecEnumType { - throw new Error('CodecEnumType:fromJSON: unimplemented'); + fromJSON (input: any): EnumType { + throw new Error('EnumType:fromJSON: unimplemented'); } - fromU8a (input: Uint8Array): CodecEnumType { + fromU8a (input: Uint8Array): EnumType { this._index = input[0]; this.raw = new this._Type[this._index]().fromU8a(input.subarray(1)); @@ -46,7 +46,7 @@ export default class CodecEnumType extends CodecBase> { } toU8a (): Uint8Array { - throw new Error('CodecEnumType:toU8a: unimplemented'); + throw new Error('EnumType:toU8a: unimplemented'); } toNumber (): number { diff --git a/packages/api-codec/src/codec/Length.spec.js b/packages/api-codec/src/codec/Length.spec.js index c2abeaeca01a..6dc0e134f7ad 100644 --- a/packages/api-codec/src/codec/Length.spec.js +++ b/packages/api-codec/src/codec/Length.spec.js @@ -4,7 +4,7 @@ import Length from './Length'; -describe('Compact', () => { +describe('Length', () => { it('encodes short u8', () => { expect( new Length(18).toU8a() @@ -29,14 +29,18 @@ describe('Compact', () => { ); }); + it('encodes basic ua6 (not at edge)', () => { + expect( + new Length(111).toHex() + ).toEqual('0xbd01'); + }); + it('decodes from same u16 encoded value', () => { expect( new Length() .fromU8a(new Uint8Array([0b11111101, 0b00000111])) .toNumber() - ).toEqual( - 511 - ); + ).toEqual(511); }); it('encodes basic u32 values (short)', () => { @@ -52,9 +56,7 @@ describe('Compact', () => { new Length() .fromU8a(new Uint8Array([254, 255, 3, 0])) .toNumber() - ).toEqual( - 0xffff - ); + ).toEqual(0xffff); }); it('encodes basic u32 values (full)', () => { @@ -70,8 +72,6 @@ describe('Compact', () => { new Length() .fromU8a(new Uint8Array([3, 249, 255, 255, 255])) .toNumber() - ).toEqual( - 0xfffffff9 - ); + ).toEqual(0xfffffff9); }); }); diff --git a/packages/api-codec/src/codec/Length.ts b/packages/api-codec/src/codec/Length.ts index 36ea20067853..5fad109c3d66 100644 --- a/packages/api-codec/src/codec/Length.ts +++ b/packages/api-codec/src/codec/Length.ts @@ -7,8 +7,9 @@ import bnToBn from '@polkadot/util/bn/toBn'; import bnToU8a from '@polkadot/util/bn/toU8a'; import u8aConcat from '@polkadot/util/u8a/concat'; import u8aToBn from '@polkadot/util/u8a/toBn'; +import u8aToHex from '@polkadot/util/u8a/toHex'; -import CodecBase from './Base'; +import Base from './Base'; const MAX_U8 = new BN(2).pow(new BN(8 - 2)).subn(1); const MAX_U16 = new BN(2).pow(new BN(16 - 2)).subn(1); @@ -29,10 +30,10 @@ const MAX_U32 = new BN(2).pow(new BN(32 - 2)).subn(1); // nn nn nn 11 [ / zz zz zz zz ]{4 + n} // // Note: we use *LOW BITS* of the LSB in LE encoding to encode the 2 bit key. -export default class CodecLength extends CodecBase { - constructor (value: CodecLength | BN | number = new BN(0)) { +export default class Length extends Base { + constructor (value: Length | BN | number = new BN(0)) { super( - value instanceof CodecLength + value instanceof Length ? value.raw : bnToBn(value) ); @@ -69,32 +70,32 @@ export default class CodecLength extends CodecBase { ); } - get length (): number { - return this.raw.toNumber(); - } - byteLength (): number { return this.toU8a().length; } - fromJSON (): CodecLength { - throw new Error('CodecLength::fromJSON: unimplemented'); + fromJSON (): Length { + throw new Error('Length::fromJSON: unimplemented'); } - fromNumber (value: BN | number): CodecLength { + fromNumber (value: BN | number): Length { this.raw = bnToBn(value); return this; } - fromU8a (input: Uint8Array): CodecLength { - this.raw = CodecLength.decode(input); + fromU8a (input: Uint8Array): Length { + this.raw = Length.decode(input); return this; } toJSON (): any { - throw new Error('CodecLength::toJSON: unimplemented'); + throw new Error('Length::toJSON: unimplemented'); + } + + toHex (): string { + return u8aToHex(this.toU8a()); } toNumber (): number { @@ -102,11 +103,11 @@ export default class CodecLength extends CodecBase { } toString (): string { - throw new Error('CodecLength::toString: unimplemented'); + throw new Error('Length::toString: unimplemented'); } toU8a (): Uint8Array { - return CodecLength.encode(this.raw); + return Length.encode(this.raw); } setValue (value: BN | number): void { diff --git a/packages/api-codec/src/codec/Option.ts b/packages/api-codec/src/codec/Option.ts index 1cbb8244f715..59b0255d05f7 100644 --- a/packages/api-codec/src/codec/Option.ts +++ b/packages/api-codec/src/codec/Option.ts @@ -4,16 +4,16 @@ import isUndefined from '@polkadot/util/is/undefined'; -import CodecBase from './Base'; +import Base from './Base'; // An Option is an optional field. Basically the first byte indicates that there is -// is value to follow. If the byte is `1` there is an actual value. So the CodecOption +// is value to follow. If the byte is `1` there is an actual value. So the Option // implements that - decodes, checks for optionality and wraps the required structure // with a value if/as required/found. -export default class CodecOption extends CodecBase> { +export default class Option extends Base> { private _hasValue: boolean; - constructor (Value: { new(value?: any): CodecBase }, value?: any) { + constructor (Value: { new(value?: any): Base }, value?: any) { super( new Value(value) ); @@ -21,8 +21,8 @@ export default class CodecOption extends CodecBase> { this._hasValue = !isUndefined(value); } - static with (Type: { new(value?: any): CodecBase }): { new(value?: any): CodecOption } { - return class extends CodecOption { + static with (Type: { new(value?: any): Base }): { new(value?: any): Option } { + return class extends Option { constructor (value?: any) { super(Type, value); } @@ -43,7 +43,7 @@ export default class CodecOption extends CodecBase> { return 1 + childLength; } - fromJSON (input: any): CodecOption { + fromJSON (input: any): Option { this._hasValue = !isUndefined(input); if (this._hasValue) { @@ -53,7 +53,7 @@ export default class CodecOption extends CodecBase> { return this; } - fromU8a (input: Uint8Array): CodecOption { + fromU8a (input: Uint8Array): Option { this._hasValue = input[0] === 1; if (this._hasValue) { diff --git a/packages/api-codec/src/codec/Struct.ts b/packages/api-codec/src/codec/Struct.ts index 3929afbe7bc2..95063cc24abc 100644 --- a/packages/api-codec/src/codec/Struct.ts +++ b/packages/api-codec/src/codec/Struct.ts @@ -4,29 +4,29 @@ import u8aConcat from '@polkadot/util/u8a/concat'; -import CodecBase from './Base'; +import Base from './Base'; -// A Struct defines an Object with key/values - where the values are CodecCodecBase values. It removes -// a lot of repetition from the actual coding, define a structure type, pass it the key/CodecBase +// A Struct defines an Object with key/values - where the values are CodecBase values. It removes +// a lot of repetition from the actual coding, define a structure type, pass it the key/Base // values in the constructor and it manages the decoding. It is important that the constructor // values matches 100% to the order in th Rust code, i.e. don't go crazy and make it alphabetical, // it needs to decoded in the specific defined order. // // TODO: // - Check the constructor, something is really, really wrong with the way the defs are used -export default class CodecStruct < - S = { [index: string]: { new(value?: any): CodecBase } }, - T = { [K in keyof S]: CodecBase }, +export default class Struct < + S = { [index: string]: { new(value?: any): Base } }, + T = { [K in keyof S]: Base }, V = { [K in keyof S]: any } -> extends CodecBase { - constructor (Struct: S, value: V = {} as V) { +> extends Base { + constructor (Def: S, value: V = {} as V) { super( - Object.keys(Struct).reduce((raw: T, key) => { + Object.keys(Def).reduce((raw: T, key) => { // @ts-ignore Ok, something weid is going on here or I just don't get it... it works, // so ignore the checker, although it drives me batty. (It started when the [key in keyof T] // was added, the idea is to provide better checks, which does backfire here, but works // externally.) - raw[key] = new Struct[key](value[key]); + raw[key] = new Def[key](value[key]); return raw; }, {} as T) @@ -34,11 +34,11 @@ export default class CodecStruct < } static with < - S = { [index: string]: { new(value?: any): CodecBase } } - > (Struct: S): { new(value?: any): CodecStruct } { - return class extends CodecStruct { + S = { [index: string]: { new(value?: any): Base } } + > (Def: S): { new(value?: any): Struct } { + return class extends Struct { constructor (value?: any) { - super(Struct, value); + super(Def, value); } }; } @@ -49,7 +49,7 @@ export default class CodecStruct < }, 0); } - fromJSON (input: any): CodecStruct { + fromJSON (input: any): Struct { Object.keys(this.raw).forEach((key) => { // @ts-ignore as above... this.raw[key].fromJSON(input[key]); @@ -58,7 +58,7 @@ export default class CodecStruct < return this; } - fromU8a (input: Uint8Array): CodecStruct { + fromU8a (input: Uint8Array): Struct { Object.keys(this.raw).reduce((offset, key) => { // @ts-ignore as above... this.raw[key].fromU8a(input.subarray(offset)); diff --git a/packages/api-codec/src/codec/U8a.spec.js b/packages/api-codec/src/codec/U8a.spec.js new file mode 100644 index 000000000000..bc5902de2202 --- /dev/null +++ b/packages/api-codec/src/codec/U8a.spec.js @@ -0,0 +1,23 @@ +// Copyright 2017-2018 @polkadot/api-codec authors & contributors +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +import U8a from './U8a'; + +describe('U8a', () => { + let u8a; + + beforeEach(() => { + u8a = new U8a([1, 2, 3, 4, 5]); + }); + + it('contains the length of the elements', () => { + expect(u8a.length).toEqual(5); // eslint-disable-line + }); + + it('allows wrapping of a pre-existing instance', () => { + expect( + new U8a(u8a).length + ).toEqual(5); // eslint-disable-line + }); +}); diff --git a/packages/api-codec/src/codec/U8a.ts b/packages/api-codec/src/codec/U8a.ts index dae202090429..b44ea6530da3 100644 --- a/packages/api-codec/src/codec/U8a.ts +++ b/packages/api-codec/src/codec/U8a.ts @@ -5,16 +5,16 @@ import u8aToHex from '@polkadot/util/u8a/toHex'; import toU8a from '@polkadot/util/u8a/toU8a'; -import CodecBase from './Base'; +import Base from './Base'; -// A CodecU8a. A basic wrapper around Uint8Array, with no frills and no fuss. It +// A U8a. A basic wrapper around Uint8Array, with no frills and no fuss. It // wraps a Uint8Array. It does differ from other implementations wher it will // consume the full u8a as passed to it in fromU8a. As such it is meant to be // subclassed where the wrapper takes care of the actual lengths. -export default class CodecU8a extends CodecBase { - constructor (value: CodecU8a | string | Uint8Array = new Uint8Array()) { +export default class U8a extends Base { + constructor (value: U8a | string | Uint8Array | Array = new Uint8Array()) { super( - value instanceof CodecU8a + value instanceof U8a ? value.raw : toU8a(value) ); @@ -28,13 +28,13 @@ export default class CodecU8a extends CodecBase { return this.raw.length; } - fromJSON (input: any): CodecU8a { + fromJSON (input: any): U8a { this.raw = toU8a(input); return this; } - fromU8a (input: Uint8Array): CodecU8a { + fromU8a (input: Uint8Array): U8a { this.raw = input; return this; diff --git a/packages/api-codec/src/codec/U8aFixed.spec.js b/packages/api-codec/src/codec/U8aFixed.spec.js new file mode 100644 index 000000000000..3d194f32357c --- /dev/null +++ b/packages/api-codec/src/codec/U8aFixed.spec.js @@ -0,0 +1,29 @@ +// Copyright 2017-2018 @polkadot/api-codec authors & contributors +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +import U8aFixed from './U8aFixed'; + +describe('U8aFixed', () => { + let u8a; + + beforeEach(() => { + u8a = new U8aFixed([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 32); + }); + + it('limits the length', () => { + expect(u8a.length).toEqual(4); // eslint-disable-line + }); + + it('allows wrapping of a pre-existing instance', () => { + expect( + u8a.toU8a() + ).toEqual(new Uint8Array([1, 2, 3, 4])); + }); + + it('allows empty values', () => { + expect( + new U8aFixed().toHex() + ).toEqual('0x'); + }); +}); diff --git a/packages/api-codec/src/codec/U8aFixed.ts b/packages/api-codec/src/codec/U8aFixed.ts index b8fe459ad989..78a83b8859fe 100644 --- a/packages/api-codec/src/codec/U8aFixed.ts +++ b/packages/api-codec/src/codec/U8aFixed.ts @@ -2,26 +2,40 @@ // This software may be modified and distributed under the terms // of the ISC license. See the LICENSE file for details. -import CodecU8a from './U8a'; +import U8a from './U8a'; type BitLength = 256 | 512; // A U8a that manages a a sequence of bytes up to the specified bitLength. Not meant // to be used directly, rather is should be subclassed with the specific lengths. -export default class CodecU8aFixed extends CodecU8a { +export default class U8aFixed extends U8a { protected _bitLength: number; - constructor (value: CodecU8a | string | Uint8Array = new Uint8Array(), bitLength: BitLength = 256) { + constructor (value: U8a | string | Uint8Array = new Uint8Array(), bitLength: BitLength = 256) { super(value); this._bitLength = bitLength; + + this._trimLength(); + } + + private _trimLength (): void { + this.raw = this.raw.subarray(0, this.byteLength()); } byteLength (): number { return this._bitLength / 8; } - fromU8a (input: Uint8Array): CodecU8aFixed { + fromJSON (input: any): U8aFixed { + super.fromJSON(input); + + this._trimLength(); + + return this; + } + + fromU8a (input: Uint8Array): U8aFixed { super.fromU8a(input.subarray(0, this.byteLength())); return this; diff --git a/packages/api-codec/src/codec/Number.ts b/packages/api-codec/src/codec/UInt.ts similarity index 79% rename from packages/api-codec/src/codec/Number.ts rename to packages/api-codec/src/codec/UInt.ts index 78c66271c0cf..06de236be28e 100644 --- a/packages/api-codec/src/codec/Number.ts +++ b/packages/api-codec/src/codec/UInt.ts @@ -11,7 +11,7 @@ import bnToU8a from '@polkadot/util/bn/toU8a'; import hexToBn from '@polkadot/util/hex/toBn'; import u8aToBn from '@polkadot/util/u8a/toBn'; -import CodecBase from './Base'; +import Base from './Base'; type BitLength = 8 | 16 | 32 | 64 | 128 | 256; @@ -21,19 +21,19 @@ type BitLength = 8 | 16 | 32 | 64 | 128 | 256; // // TODO: // - Apart from encoding/decoding we don't actuall keep check on the sizes, is this good enough? -export default class CodecNumber extends CodecBase { +export default class UInt extends Base { private _bitLength: BitLength; - constructor (value: CodecNumber | BN | string | number = 0, bitLength: BitLength = 64) { + constructor (value: UInt | BN | string | number = 0, bitLength: BitLength = 64) { super( - CodecNumber.decode(value) + UInt.decode(value) ); this._bitLength = bitLength; } - static decode (value: CodecNumber | BN | string | number): BN { - if (value instanceof CodecNumber) { + static decode (value: UInt | BN | string | number): BN { + if (value instanceof UInt) { return value.raw; } else if (isHex(value)) { return hexToBn(value as string); @@ -48,13 +48,13 @@ export default class CodecNumber extends CodecBase { return this._bitLength / 8; } - fromJSON (input: any): CodecNumber { - this.raw = CodecNumber.decode(input); + fromJSON (input: any): UInt { + this.raw = UInt.decode(input); return this; } - fromU8a (input: Uint8Array): CodecNumber { + fromU8a (input: Uint8Array): UInt { this.raw = u8aToBn(input.subarray(0, this.byteLength()), true); return this; diff --git a/packages/api-codec/src/codec/Vector.spec.js b/packages/api-codec/src/codec/Vector.spec.js new file mode 100644 index 000000000000..b43671653992 --- /dev/null +++ b/packages/api-codec/src/codec/Vector.spec.js @@ -0,0 +1,81 @@ +// Copyright 2017-2018 @polkadot/api-codec authors & contributors +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +import Text from '../Text'; +import Vector from './Vector'; + +describe('Vector', () => { + let array; + + beforeEach(() => { + array = new Vector(Text, [ '1', '23', '345', '4567', new Text('56789') ]); + }); + + it('wraps a sequence of values', () => { + expect(array.length).toEqual(5); // eslint-disable-line + }); + + it('has a sane representation for toString', () => { + expect(array.toString()).toEqual('[1, 23, 345, 4567, 56789]'); + }); + + it('encodes with length prefix', () => { + expect(array.toU8a()).toEqual(new Uint8Array([ + 5 << 2, + 1 << 2, 49, + 2 << 2, 50, 51, + 3 << 2, 51, 52, 53, + 4 << 2, 52, 53, 54, 55, + 5 << 2, 53, 54, 55, 56, 57 + ])); + }); + + it('allows contruction via JSON', () => { + expect( + new Vector(Text).fromJSON(['6', '7']).toString() + ).toEqual('[6, 7]'); + }); + + describe('array-like functions', () => { + it('allows retrieval of a specific item', () => { + expect( + array.at(2).toString() + ).toEqual('345'); + }); + + it('exposes a working forEach', () => { + const result = {}; + + array.forEach((e, i) => { + result[i] = e.toString(); + }); + + expect(result).toEqual({ + 0: '1', + 1: '23', + 2: '345', + 3: '4567', + 4: '56789' + }); + }); + + it('exposes a working filter', () => { + expect( + array.filter((e, i) => i >= 3).toString() + ).toEqual('4567,56789'); + }); + + it('exposes a working map', () => { + expect( + array.map((e) => e.toString().substr(0, 1)) + ).toEqual(['1', '2', '3', '4', '5']); + }); + + it('exposes a working reduce', () => { + expect( + array.reduce((r, e) => `${r}${e}`, '') + ).toEqual('123345456756789'); + }); + }); +}); diff --git a/packages/api-codec/src/codec/Array.ts b/packages/api-codec/src/codec/Vector.ts similarity index 85% rename from packages/api-codec/src/codec/Array.ts rename to packages/api-codec/src/codec/Vector.ts index 9ab23a684d53..d977beb3c84c 100644 --- a/packages/api-codec/src/codec/Array.ts +++ b/packages/api-codec/src/codec/Vector.ts @@ -4,7 +4,7 @@ import u8aConcat from '@polkadot/util/u8a/concat'; -import CodecBase from './Base'; +import Base from './Base'; import Length from './Length'; // This manages codec arrays. Intrernally it keeps track of the length (as decoded) and allows @@ -12,16 +12,18 @@ import Length from './Length'; // i.e. while it wraps an array, it provides a `length` property to get the actual length, `at(index)` // to retrieve a specific item. Additionally the helper functions `map`, `filter`, `forEach` and // `reduce` is exposed on the interface. -export default class CodecArray < - T extends CodecBase -> extends CodecBase> { +export default class Vector < + T extends Base +> extends Base> { private _length: Length; private _Type: { new(value?: any): T }; constructor (Type: { new(value?: any): T }, value: Array = [] as Array) { super( value.map((entry) => - new Type(entry) + entry instanceof Type + ? entry + : new Type(entry) ) ); @@ -29,8 +31,8 @@ export default class CodecArray < this._Type = Type; } - static with (Type: { new(value?: any): O }): { new(value?: any): CodecArray } { - return class extends CodecArray { + static with (Type: { new(value?: any): O }): { new(value?: any): Vector } { + return class extends Vector { constructor (value?: Array) { super(Type, value); } @@ -59,7 +61,7 @@ export default class CodecArray < return this.raw.forEach(fn); } - fromJSON (input: any): CodecArray { + fromJSON (input: any): Vector { this.raw = input.map((input: any) => new this._Type().fromJSON(input) ); @@ -67,7 +69,7 @@ export default class CodecArray < return this; } - fromU8a (input: Uint8Array): CodecArray { + fromU8a (input: Uint8Array): Vector { this._length.fromU8a(input); const length = this._length.toNumber(); @@ -110,8 +112,8 @@ export default class CodecArray < toString (): string { const data = this.raw.map((entry) => entry.toString() - ).join(', '); + ); - return `[${data}]`; + return `[${data.join(', ')}]`; } }