From e60ef2d677d8e5a75100a3ae7664ba81eee92d42 Mon Sep 17 00:00:00 2001 From: Jaco Date: Sun, 31 Oct 2021 05:58:14 +0100 Subject: [PATCH] Optimize internal u8a decoding (#4131) * Optimize internal u8a decoding * linting * tsc fixes & HexString usage * yet more... it passes now (well... tsc & lint) --- CHANGELOG.md | 3 +- package.json | 2 +- packages/types/src/codec/AbstractArray.ts | 3 +- packages/types/src/codec/AbstractInt.ts | 3 +- packages/types/src/codec/BTreeSet.ts | 3 +- packages/types/src/codec/Base.ts | 3 +- packages/types/src/codec/Compact.ts | 3 +- packages/types/src/codec/Date.ts | 3 +- packages/types/src/codec/DoNotConstruct.ts | 3 +- packages/types/src/codec/Enum.ts | 3 +- packages/types/src/codec/Json.ts | 3 +- packages/types/src/codec/Linkage.ts | 3 +- packages/types/src/codec/Map.ts | 3 +- packages/types/src/codec/Option.ts | 3 +- packages/types/src/codec/Raw.ts | 3 +- packages/types/src/codec/Set.ts | 3 +- packages/types/src/codec/Struct.ts | 65 ++++++++----------- packages/types/src/codec/Vec.ts | 21 ++++-- packages/types/src/codec/VecFixed.ts | 19 ++---- packages/types/src/codec/utils/decodeU8a.ts | 35 +++++----- packages/types/src/ethereum/LookupSource.ts | 3 +- packages/types/src/extrinsic/Extrinsic.ts | 5 +- .../types/src/extrinsic/ExtrinsicPayload.ts | 3 +- packages/types/src/extrinsic/SignerPayload.ts | 3 +- packages/types/src/extrinsic/v4/Extrinsic.ts | 3 +- .../src/extrinsic/v4/ExtrinsicPayload.ts | 3 +- .../src/extrinsic/v4/ExtrinsicSignature.ts | 3 +- packages/types/src/generic/LookupSource.ts | 3 +- packages/types/src/primitive/Bool.ts | 3 +- packages/types/src/primitive/Null.ts | 3 +- packages/types/src/primitive/Text.ts | 3 +- packages/types/src/types/codec.ts | 5 +- packages/types/src/types/extrinsic.ts | 7 +- yarn.lock | 43 +++++------- 34 files changed, 146 insertions(+), 131 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad205073c7..272f8f3ba6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,10 @@ Changes: - Fix construction for `palletVersion` keys with correct prefix - Add Polkadot `EthereumAddress` as a known `PortableRegistry` override - De-dupe `PortableRegistry` namespace splitting operations -- Adjust internal `AbstractArray` & `Struct` decoders with inital u8a lengths +- Optimize internal `AbstractArray` & `Struct` u8a decoding - Shortcut `Struct` construction from `Struct` instance - Use internal `lookupName` in `createClass` (allows deeply recursive lookups) +- Codec `toHex()` will now correctly return the TS `HexString` type - Update known upgrade blocks for Westend & Kusama - Bump static metadata for latest Substrate, Polkadot & Kusama diff --git a/package.json b/package.json index 87be9615a6..0ca1734405 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "@babel/core": "^7.15.8", "@babel/register": "^7.15.3", "@babel/runtime": "^7.15.4", - "@polkadot/dev": "^0.63.10", + "@polkadot/dev": "^0.63.18", "@polkadot/ts": "^0.4.12", "@polkadot/typegen": "workspace:packages/typegen", "@types/jest": "^27.0.2", diff --git a/packages/types/src/codec/AbstractArray.ts b/packages/types/src/codec/AbstractArray.ts index 2f9a428b81..19e8ea8145 100644 --- a/packages/types/src/codec/AbstractArray.ts +++ b/packages/types/src/codec/AbstractArray.ts @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { CodecHash, Hash } from '../interfaces/runtime'; import type { AnyJson, Codec, IVec, Registry } from '../types'; @@ -89,7 +90,7 @@ export abstract class AbstractArray extends Array implements /** * @description Returns a hex string representation of the value */ - public toHex (): string { + public toHex (): HexString { return u8aToHex(this.toU8a()); } diff --git a/packages/types/src/codec/AbstractInt.ts b/packages/types/src/codec/AbstractInt.ts index 34130bdb45..5a9af9858d 100644 --- a/packages/types/src/codec/AbstractInt.ts +++ b/packages/types/src/codec/AbstractInt.ts @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { CodecHash, Hash } from '../interfaces/runtime'; import type { AnyNumber, INumber, Registry } from '../types'; import type { UIntBitLength } from './types'; @@ -161,7 +162,7 @@ export abstract class AbstractInt extends BN implements INumber { /** * @description Returns a hex string representation of the value */ - public toHex (isLe = false): string { + public toHex (isLe = false): HexString { // For display/JSON, this is BE, for compare, use isLe return bnToHex(this, { bitLength: this.bitLength(), diff --git a/packages/types/src/codec/BTreeSet.ts b/packages/types/src/codec/BTreeSet.ts index 87ffa4bdd9..608173535c 100644 --- a/packages/types/src/codec/BTreeSet.ts +++ b/packages/types/src/codec/BTreeSet.ts @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { CodecHash, Hash } from '../interfaces/runtime'; import type { AnyJson, Codec, Constructor, ISet, Registry } from '../types'; @@ -148,7 +149,7 @@ export class BTreeSet extends Set implements ISet /** * @description Returns a hex string representation of the value. isLe returns a LE (number-only) representation */ - public toHex (): string { + public toHex (): HexString { return u8aToHex(this.toU8a()); } diff --git a/packages/types/src/codec/Base.ts b/packages/types/src/codec/Base.ts index af7baead2f..43c47c79c7 100644 --- a/packages/types/src/codec/Base.ts +++ b/packages/types/src/codec/Base.ts @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { CodecHash, Hash } from '../interfaces/runtime'; import type { AnyJson, BareOpts, Codec, Registry } from '../types'; @@ -61,7 +62,7 @@ export abstract class Base implements Codec { /** * @description Returns a hex string representation of the value. isLe returns a LE (number-only) representation */ - public toHex (isLe?: boolean): string { + public toHex (isLe?: boolean): HexString { return this._raw.toHex(isLe); } diff --git a/packages/types/src/codec/Compact.ts b/packages/types/src/codec/Compact.ts index c3c3be6d62..dc77b0b2f4 100644 --- a/packages/types/src/codec/Compact.ts +++ b/packages/types/src/codec/Compact.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { BN } from '@polkadot/util'; +import type { HexString } from '@polkadot/util/types'; import type { CodecHash, Hash } from '../interfaces'; import type { AnyJson, AnyNumber, Constructor, ICompact, INumber, Registry } from '../types'; @@ -122,7 +123,7 @@ export class Compact implements ICompact { /** * @description Returns a hex string representation of the value. isLe returns a LE (number-only) representation */ - public toHex (isLe?: boolean): string { + public toHex (isLe?: boolean): HexString { return this.#raw.toHex(isLe); } diff --git a/packages/types/src/codec/Date.ts b/packages/types/src/codec/Date.ts index 44f1ca8328..09758119ca 100644 --- a/packages/types/src/codec/Date.ts +++ b/packages/types/src/codec/Date.ts @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { CodecHash, Hash } from '../interfaces/runtime'; import type { AnyNumber, INumber, Registry } from '../types'; import type { UIntBitLength } from './types'; @@ -96,7 +97,7 @@ export class CodecDate extends Date implements INumber { /** * @description Returns a hex string representation of the value */ - public toHex (isLe = false): string { + public toHex (isLe = false): HexString { return bnToHex(this.toBn(), { bitLength: BITLENGTH, isLe, diff --git a/packages/types/src/codec/DoNotConstruct.ts b/packages/types/src/codec/DoNotConstruct.ts index ac0300b204..5cd60b6470 100644 --- a/packages/types/src/codec/DoNotConstruct.ts +++ b/packages/types/src/codec/DoNotConstruct.ts @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { CodecHash, Hash } from '../interfaces/runtime'; import type { AnyJson, Codec, Constructor, Registry } from '../types'; @@ -56,7 +57,7 @@ export class DoNotConstruct implements Codec { throw this.#neverError; } - toHex (): string { + toHex (): HexString { throw this.#neverError; } diff --git a/packages/types/src/codec/Enum.ts b/packages/types/src/codec/Enum.ts index a6270ab818..478925bc5e 100644 --- a/packages/types/src/codec/Enum.ts +++ b/packages/types/src/codec/Enum.ts @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { CodecHash, Hash } from '../interfaces'; import type { AnyJson, Codec, Constructor, IEnum, Registry } from '../types'; @@ -337,7 +338,7 @@ export class Enum implements IEnum { /** * @description Returns a hex string representation of the value */ - public toHex (): string { + public toHex (): HexString { return u8aToHex(this.toU8a()); } diff --git a/packages/types/src/codec/Json.ts b/packages/types/src/codec/Json.ts index 66a36c4336..a936599c25 100644 --- a/packages/types/src/codec/Json.ts +++ b/packages/types/src/codec/Json.ts @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { CodecHash, Hash } from '../interfaces/runtime'; import type { AnyJson, Codec, Registry } from '../types'; @@ -73,7 +74,7 @@ export class Json extends Map implements Codec { /** * @description Unimplemented, will throw */ - public toHex (): string { + public toHex (): HexString { throw new Error('Unimplemented'); } diff --git a/packages/types/src/codec/Linkage.ts b/packages/types/src/codec/Linkage.ts index 8724cfc77c..b7d9305a1d 100644 --- a/packages/types/src/codec/Linkage.ts +++ b/packages/types/src/codec/Linkage.ts @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { Codec, Constructor, Registry } from '../types'; import { Option } from './Option'; @@ -22,7 +23,7 @@ export class Linkage extends Struct { previous: Option.with(Type), // eslint-disable-next-line sort-keys next: Option.with(Type) - }, value as string); + }, value as HexString); } public static withKey (Type: Constructor | string): Constructor> { diff --git a/packages/types/src/codec/Map.ts b/packages/types/src/codec/Map.ts index 4ed9a5766c..6d6d27b670 100644 --- a/packages/types/src/codec/Map.ts +++ b/packages/types/src/codec/Map.ts @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { CodecHash, Hash } from '../interfaces/runtime'; import type { AnyJson, Codec, Constructor, IMap, Registry } from '../types'; @@ -162,7 +163,7 @@ export class CodecMap extends /** * @description Returns a hex string representation of the value. isLe returns a LE (number-only) representation */ - public toHex (): string { + public toHex (): HexString { return u8aToHex(this.toU8a()); } diff --git a/packages/types/src/codec/Option.ts b/packages/types/src/codec/Option.ts index 2aa8bfb667..cd4706fd1d 100644 --- a/packages/types/src/codec/Option.ts +++ b/packages/types/src/codec/Option.ts @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { CodecHash, Hash } from '../interfaces'; import type { AnyJson, Codec, Constructor, IOption, Registry } from '../types'; @@ -140,7 +141,7 @@ export class Option implements IOption { /** * @description Returns a hex string representation of the value */ - public toHex (): string { + public toHex (): HexString { // This attempts to align with the JSON encoding - actually in this case // the isSome value is correct, however the `isNone` may be problematic return this.isNone diff --git a/packages/types/src/codec/Raw.ts b/packages/types/src/codec/Raw.ts index 71f4efc133..a1ba6037cc 100644 --- a/packages/types/src/codec/Raw.ts +++ b/packages/types/src/codec/Raw.ts @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { CodecHash, Hash } from '../interfaces/runtime'; import type { AnyJson, AnyU8a, IU8a, Registry } from '../types'; @@ -121,7 +122,7 @@ export class Raw extends Uint8Array implements IU8a { /** * @description Returns a hex string representation of the value */ - public toHex (): string { + public toHex (): HexString { return u8aToHex(this); } diff --git a/packages/types/src/codec/Set.ts b/packages/types/src/codec/Set.ts index 90db5be7fc..190cff41ee 100644 --- a/packages/types/src/codec/Set.ts +++ b/packages/types/src/codec/Set.ts @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { CodecHash, Hash } from '../interfaces/runtime'; import type { Constructor, ISet, Registry } from '../types'; @@ -177,7 +178,7 @@ export class CodecSet extends Set implements ISet { /** * @description Returns a hex string representation of the value */ - public toHex (): string { + public toHex (): HexString { return u8aToHex(this.toU8a()); } diff --git a/packages/types/src/codec/Struct.ts b/packages/types/src/codec/Struct.ts index 9a66d3b8a3..2e997669c3 100644 --- a/packages/types/src/codec/Struct.ts +++ b/packages/types/src/codec/Struct.ts @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { CodecHash, Hash } from '../interfaces/runtime'; import type { AnyJson, BareOpts, Codec, Constructor, ConstructorDef, IStruct, Registry } from '../types'; @@ -10,38 +11,33 @@ import { compareMap, decodeU8a, mapToTypeMap } from './utils'; type TypesDef = Record>; -function newEntry (registry: Registry, Type: Constructor, key: string, value: unknown): [string, Codec] { - return [ - key, - value instanceof Type - ? value - : new Type(registry, value) - ]; -} - /** @internal */ function decodeStructFromObject (registry: Registry, Types: ConstructorDef, value: any, jsonMap: Map): Iterable<[string, Codec]> { let jsonObj: Record | undefined; const inputKeys = Object.keys(Types); + const typeofArray = Array.isArray(value); + const typeofMap = value instanceof Map; - assert(!Array.isArray(value) || value.length === inputKeys.length, () => `Struct: Unable to map ${stringify(value)} array to object with known keys ${inputKeys.join(', ')}`); + assert(typeofArray || typeofMap || isObject(value), () => `Struct: Cannot decode value ${stringify(value)} (typeof ${typeof value}), expected an input object, map or array`); + assert(!typeofArray || value.length === inputKeys.length, () => `Struct: Unable to map ${stringify(value)} array to object with known keys ${inputKeys.join(', ')}`); return inputKeys.reduce<[string, Codec][]>((raw, key, index) => { const jsonKey = jsonMap.get(key) || key; const Type = Types[key]; + let assign: unknown; try { - if (Array.isArray(value)) { - raw.push(newEntry(registry, Type, key, value[index])); - } else if (value instanceof Map) { - raw.push(newEntry(registry, Type, key, jsonKey && (value as Map).get(jsonKey))); - } else if (isObject(value)) { - let assign = jsonKey && value[jsonKey] as unknown; + if (typeofArray) { + assign = value[index] as unknown; + } else if (typeofMap) { + assign = jsonKey && value.get(jsonKey); + } else { + assign = jsonKey && value[jsonKey] as unknown; if (isUndefined(assign)) { if (isUndefined(jsonObj)) { - jsonObj = Object.entries(value).reduce((all: Record, [key, value]): Record => { - all[stringCamelCase(key)] = value; + jsonObj = Object.entries(value).reduce((all: Record, [k, v]): Record => { + all[stringCamelCase(k)] = v; return all; }, {}); @@ -49,11 +45,14 @@ function decodeStructFromObject (registry: Registry, Types: ConstructorDef, valu assign = jsonKey && jsonObj[jsonKey]; } - - raw.push(newEntry(registry, Type, key, assign)); - } else { - throw new Error(`Cannot decode value ${stringify(value)} (typeof ${typeof value}), expected an input object with known keys`); } + + raw.push([ + key, + assign instanceof Type + ? assign + : new Type(registry, assign) + ]); } catch (error) { let type = Type.name; @@ -90,18 +89,7 @@ function decodeStruct (registry: Registry, Types: ConstructorDef, value: unknown if (isHex(value)) { return decodeStruct(registry, Types, hexToU8a(value), jsonMap); } else if (isU8a(value)) { - const keys = Object.keys(Types); - const [values, decodedLength] = decodeU8a(registry, value, Object.values(Types), keys); - - // Transform array of values to {key: value} mapping - return [ - keys.reduce<[string, Codec][]>((raw, key, index) => { - raw.push([key, values[index]]); - - return raw; - }, []), - decodedLength - ]; + return decodeU8a(registry, value, Types, 0, (k, v) => [k, v]); } else if (value instanceof Struct) { return [value as Iterable<[string, Codec]>, 0]; } @@ -138,7 +126,7 @@ export class Struct< readonly #Types: ConstructorDef; // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - constructor (registry: Registry, Types: S, value?: V | Map | unknown[] | string | null, jsonMap: Map = new Map()) { + constructor (registry: Registry, Types: S, value?: V | Map | unknown[] | HexString | null, jsonMap = new Map()) { const [decoded, decodedLength] = decodeStruct(registry, mapToTypeMap(registry, Types), value, jsonMap); super(decoded); @@ -152,7 +140,7 @@ export class Struct< public static with (Types: S, jsonMap?: Map): Constructor> { return class extends Struct { constructor (registry: Registry, value?: unknown) { - super(registry, Types, value as string, jsonMap); + super(registry, Types, value as HexString, jsonMap); Object.keys(Types).forEach((key): void => { isUndefined(this[key as keyof this]) && @@ -186,6 +174,7 @@ export class Struct< public get isEmpty (): boolean { const items = this.toArray(); + // More code, but it _should_ be more performand that this.toArray().every((e) => e.isEmpty) for (let i = 0; i < items.length; i++) { if (!items[i].isEmpty) { return false; @@ -203,7 +192,7 @@ export class Struct< .entries(this.#Types)) .reduce((result: E, [key, Type]): E => { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - (result as any)[key] = new Type(this.registry).toRawType(); + (result as any)[key as keyof E] = new Type(this.registry).toRawType(); return result; }, {} as E); @@ -262,7 +251,7 @@ export class Struct< /** * @description Returns a hex string representation of the value */ - public toHex (): string { + public toHex (): HexString { return u8aToHex(this.toU8a()); } diff --git a/packages/types/src/codec/Vec.ts b/packages/types/src/codec/Vec.ts index 2b8ad800fc..0920a2740d 100644 --- a/packages/types/src/codec/Vec.ts +++ b/packages/types/src/codec/Vec.ts @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { Codec, Constructor, Registry } from '../types'; import { assert, compactFromU8a, logger, u8aToU8a } from '@polkadot/util'; @@ -22,7 +23,7 @@ const l = logger('Vec'); export class Vec extends AbstractArray { #Type: Constructor; - constructor (registry: Registry, Type: Constructor | string, value: Vec | Uint8Array | string | unknown[] = []) { + constructor (registry: Registry, Type: Constructor | string, value: Uint8Array | HexString | unknown[] = []) { const Clazz = typeToConstructor(registry, Type); const [values, decodedLength] = Vec.decodeVec(registry, Clazz, value); @@ -32,11 +33,10 @@ export class Vec extends AbstractArray { } /** @internal */ - public static decodeVec (registry: Registry, Type: Constructor, value: Vec | Uint8Array | string | unknown[]): [T[], number, number] { + public static decodeVec (registry: Registry, Type: Constructor, value: Uint8Array | HexString | unknown[], length = -1): [T[], number, number] { if (Array.isArray(value)) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return return [ - (value as unknown[]).map((entry: unknown, index: number): T => { + value.map((entry: unknown, index: number): T => { try { return entry instanceof Type ? entry @@ -52,11 +52,18 @@ export class Vec extends AbstractArray { } const u8a = u8aToU8a(value); - const [offset, length] = compactFromU8a(u8a); + let offset = 0; - assert(length.lten(MAX_LENGTH), () => `Vec length ${length.toString()} exceeds ${MAX_LENGTH}`); + if (length === -1) { + const [_offset, _length] = compactFromU8a(u8a); - const [decoded, decodedLength] = decodeU8a(registry, u8a.subarray(offset), new Array(length.toNumber()).fill(Type)); + assert(_length.lten(MAX_LENGTH), () => `Vec length ${_length.toString()} exceeds ${MAX_LENGTH}`); + + length = _length.toNumber(); + offset = _offset; + } + + const [decoded, decodedLength] = decodeU8a(registry, u8a.subarray(offset), Type, length); return [decoded as T[], decodedLength + offset, decodedLength]; } diff --git a/packages/types/src/codec/VecFixed.ts b/packages/types/src/codec/VecFixed.ts index 03db95bc65..4e01f36af5 100644 --- a/packages/types/src/codec/VecFixed.ts +++ b/packages/types/src/codec/VecFixed.ts @@ -1,29 +1,24 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { Codec, Constructor, Registry } from '../types'; -import { assert, compactToU8a, isHex, isU8a, u8aConcat } from '@polkadot/util'; +import { assert, u8aConcat } from '@polkadot/util'; import { AbstractArray } from './AbstractArray'; import { typeToConstructor } from './utils'; import { Vec } from './Vec'; /** @internal */ -function decodeVecFixed (registry: Registry, Type: Constructor, allocLength: number, value: VecFixed | Uint8Array | string | any[]): [T[], number] { - const [values,, decodedLengthNoOffset] = Vec.decodeVec( - registry, - Type, - isU8a(value) || isHex(value) - ? u8aConcat(compactToU8a(allocLength), value) - : value - ); +function decodeVecFixed (registry: Registry, Type: Constructor, length: number, value: Uint8Array | HexString | unknown[]): [T[], number] { + const [values,, decodedLengthNoOffset] = Vec.decodeVec(registry, Type, value, length); - while (values.length < allocLength) { + while (values.length < length) { values.push(new Type(registry)); } - assert(values.length === allocLength, () => `Expected a length of exactly ${allocLength} entries`); + assert(values.length === length, () => `Expected a length of exactly ${length} entries`); return [values, decodedLengthNoOffset]; } @@ -36,7 +31,7 @@ function decodeVecFixed (registry: Registry, Type: Constructor< export class VecFixed extends AbstractArray { #Type: Constructor; - constructor (registry: Registry, Type: Constructor | string, length: number, value: VecFixed | Uint8Array | string | any[] = [] as any[]) { + constructor (registry: Registry, Type: Constructor | string, length: number, value: Uint8Array | HexString | unknown[] = [] as unknown[]) { const Clazz = typeToConstructor(registry, Type); const [values, decodedLength] = decodeVecFixed(registry, Clazz, length, value); diff --git a/packages/types/src/codec/utils/decodeU8a.ts b/packages/types/src/codec/utils/decodeU8a.ts index 3c694bc758..ec4638f021 100644 --- a/packages/types/src/codec/utils/decodeU8a.ts +++ b/packages/types/src/codec/utils/decodeU8a.ts @@ -3,7 +3,11 @@ import type { Codec, Constructor, Registry } from '../../types'; -import { u8aToHex } from '@polkadot/util'; +import { isFunction, u8aToHex } from '@polkadot/util'; + +function identityZip (key: string, value: T): E { + return value as unknown as E; +} /** * Given an u8a, and an array of Type constructors, decode the u8a against the @@ -13,33 +17,34 @@ import { u8aToHex } from '@polkadot/util'; * @param result - The result array (will be returned with values pushed) * @param types - The array of Constructor to decode the U8a against. */ -export function decodeU8a (registry: Registry, u8a: Uint8Array, _types: Constructor[] | { [index: string]: Constructor }, _keys?: string[]): [T[], number] { - const result: T[] = []; - const [types, keys]: [Constructor[], string[]] = Array.isArray(_types) - ? [_types, _keys || []] - : [Object.values(_types), Object.keys(_types)]; +export function decodeU8a (registry: Registry, u8a: Uint8Array, types: Constructor | Constructor[] | { [index: string]: Constructor }, count?: number, zip: (key: string, value: T) => E = identityZip): [E[], number] { + const [Type, Types, keys]: [Constructor | null, Constructor[], string[]] = isFunction(types) + ? [types, [], []] + : Array.isArray(types) + ? [null, types, []] + : [null, Object.values(types), Object.keys(types)]; + + count = count || Types.length; + + const result = new Array(count); let decodedLength = 0; - for (let i = 0; i < types.length; i++) { - const Type = types[i]; - + for (let i = 0; i < count; i++) { try { - const value = new Type(registry, u8a.subarray(decodedLength)); + const value = new (Type || Types[i])(registry, u8a.subarray(decodedLength)); - result.push(value as T); - - // use the initial length if available, this bypasses an extra serialization + result[i] = zip(keys[i], value as T); decodedLength += value.initialU8aLength || value.encodedLength; } catch (error) { let rawType: string; try { - rawType = new Type(registry).toRawType(); + rawType = new (Type || Types[i])(registry).toRawType(); } catch { rawType = ''; } - throw new Error(`decodeU8a: failed at ${u8aToHex(u8a.subarray(decodedLength).slice(0, 8))}… on ${keys[i] ? `${keys[i]}` : ''}${rawType ? `: ${rawType}` : ''}:: ${(error as Error).message}`); + throw new Error(`decodeU8a: failed at ${u8aToHex(u8a.subarray(decodedLength, decodedLength + 8))}…${keys[i] ? ` on ${keys[i]}` : ''}${rawType ? `: ${rawType}` : ''}:: ${(error as Error).message}`); } } diff --git a/packages/types/src/ethereum/LookupSource.ts b/packages/types/src/ethereum/LookupSource.ts index 31ae5c3722..a45ea793fd 100644 --- a/packages/types/src/ethereum/LookupSource.ts +++ b/packages/types/src/ethereum/LookupSource.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { BN } from '@polkadot/util'; +import type { HexString } from '@polkadot/util/types'; import type { Registry } from '../types'; import { isBigInt, isBn, isHex, isNumber, isU8a, u8aConcat, u8aToBn, u8aToHex, u8aToU8a } from '@polkadot/util'; @@ -92,7 +93,7 @@ export class GenericEthereumLookupSource extends Base extends ExtrinsicBa /** * @description Injects an already-generated signature into the extrinsic */ - public addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | string, payload: ExtrinsicPayloadValue | Uint8Array | string): GenericExtrinsic { + public addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | HexString, payload: ExtrinsicPayloadValue | Uint8Array | HexString): GenericExtrinsic { (this._raw as ExtrinsicVx).addSignature(signer, signature, payload); return this; @@ -243,7 +244,7 @@ export class GenericExtrinsic extends ExtrinsicBa /** * @description Returns a hex string representation of the value */ - public override toHex (isBare?: boolean): string { + public override toHex (isBare?: boolean): HexString { return u8aToHex(this.toU8a(isBare)); } diff --git a/packages/types/src/extrinsic/ExtrinsicPayload.ts b/packages/types/src/extrinsic/ExtrinsicPayload.ts index f2238f0bb2..4105803a2b 100644 --- a/packages/types/src/extrinsic/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/ExtrinsicPayload.ts @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { ExtrinsicPayloadV4 } from '../interfaces/extrinsics'; import type { Balance, Hash, Index } from '../interfaces/runtime'; import type { AnyJson, BareOpts, ExtrinsicPayloadValue, IKeyringPair, Registry } from '../types'; @@ -119,7 +120,7 @@ export class GenericExtrinsicPayload extends Base { /** * @description Sign the payload with the keypair */ - public sign (signerPair: IKeyringPair): { signature: string } { + public sign (signerPair: IKeyringPair): { signature: HexString } { const signature = this._raw.sign(signerPair); // This is extensible, so we could quite readily extend to send back extra diff --git a/packages/types/src/extrinsic/SignerPayload.ts b/packages/types/src/extrinsic/SignerPayload.ts index f6a81684b8..e939db4c4a 100644 --- a/packages/types/src/extrinsic/SignerPayload.ts +++ b/packages/types/src/extrinsic/SignerPayload.ts @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { Address, Balance, BlockNumber, Call, ExtrinsicEra, Hash, Index, RuntimeVersion } from '../interfaces'; import type { Codec, ISignerPayload, Registry, SignerPayloadJSON, SignerPayloadRaw } from '../types'; @@ -49,7 +50,7 @@ const knownTypes: Record = { export class GenericSignerPayload extends Struct implements ISignerPayload, SignerPayloadType { private readonly _extraTypes: Record; - constructor (registry: Registry, value?: string | { [x: string]: any; } | Map | unknown[]) { + constructor (registry: Registry, value?: HexString | { [x: string]: unknown; } | Map | unknown[]) { const extensionTypes = { ...registry.getSignedExtensionTypes(), ...registry.getSignedExtensionExtra() diff --git a/packages/types/src/extrinsic/v4/Extrinsic.ts b/packages/types/src/extrinsic/v4/Extrinsic.ts index e452e4260e..c8ef604946 100644 --- a/packages/types/src/extrinsic/v4/Extrinsic.ts +++ b/packages/types/src/extrinsic/v4/Extrinsic.ts @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { ExtrinsicSignatureV4 } from '../../interfaces/extrinsics'; import type { Address, Call } from '../../interfaces/runtime'; import type { ExtrinsicPayloadValue, IExtrinsicImpl, IKeyringPair, Registry, SignatureOptions } from '../../types'; @@ -82,7 +83,7 @@ export class GenericExtrinsicV4 extends Struct implements IExtrinsicImpl { /** * @description Add an [[ExtrinsicSignatureV4]] to the extrinsic (already generated) */ - public addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | string, payload: ExtrinsicPayloadValue | Uint8Array | string): GenericExtrinsicV4 { + public addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | HexString, payload: ExtrinsicPayloadValue | Uint8Array | HexString): GenericExtrinsicV4 { this.signature.addSignature(signer, signature, payload); return this; diff --git a/packages/types/src/extrinsic/v4/ExtrinsicPayload.ts b/packages/types/src/extrinsic/v4/ExtrinsicPayload.ts index 9b2575d06c..33904d7ed4 100644 --- a/packages/types/src/extrinsic/v4/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/v4/ExtrinsicPayload.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { SignOptions } from '@polkadot/keyring/types'; +import type { HexString } from '@polkadot/util/types'; import type { ExtrinsicEra } from '../../interfaces/extrinsics'; import type { AssetId, Balance, Hash, Index } from '../../interfaces/runtime'; import type { ExtrinsicPayloadValue, IKeyringPair, Registry } from '../../types'; @@ -23,7 +24,7 @@ import { sign } from '../util'; export class GenericExtrinsicPayloadV4 extends Struct { #signOptions: SignOptions; - constructor (registry: Registry, value?: ExtrinsicPayloadValue | Uint8Array | string) { + constructor (registry: Registry, value?: ExtrinsicPayloadValue | Uint8Array | HexString) { super(registry, { method: 'Bytes', ...registry.getSignedExtensionTypes(), diff --git a/packages/types/src/extrinsic/v4/ExtrinsicSignature.ts b/packages/types/src/extrinsic/v4/ExtrinsicSignature.ts index 4916a5023a..38d8bb0e1f 100644 --- a/packages/types/src/extrinsic/v4/ExtrinsicSignature.ts +++ b/packages/types/src/extrinsic/v4/ExtrinsicSignature.ts @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { EcdsaSignature, Ed25519Signature, ExtrinsicEra, ExtrinsicSignature, Sr25519Signature } from '../../interfaces/extrinsics'; import type { Address, AssetId, Balance, Call, Index } from '../../interfaces/runtime'; import type { ExtrinsicPayloadValue, IExtrinsicSignature, IKeyringPair, Registry, SignatureOptions } from '../../types'; @@ -137,7 +138,7 @@ export class GenericExtrinsicSignatureV4 extends Struct implements IExtrinsicSig /** * @description Adds a raw signature */ - public addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | string, payload: ExtrinsicPayloadValue | Uint8Array | string): IExtrinsicSignature { + public addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | HexString, payload: ExtrinsicPayloadValue | Uint8Array | HexString): IExtrinsicSignature { return this._injectSignature( toAddress(this.registry, signer), this.registry.createType('ExtrinsicSignature', signature), diff --git a/packages/types/src/generic/LookupSource.ts b/packages/types/src/generic/LookupSource.ts index 2a5149d0ba..d1d467cad0 100644 --- a/packages/types/src/generic/LookupSource.ts +++ b/packages/types/src/generic/LookupSource.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { BN } from '@polkadot/util'; +import type { HexString } from '@polkadot/util/types'; import type { Registry } from '../types'; import { isBigInt, isBn, isHex, isNumber, isU8a, u8aConcat, u8aToBn, u8aToHex, u8aToU8a } from '@polkadot/util'; @@ -92,7 +93,7 @@ export class GenericLookupSource extends Base, when we are encoding to hex, we don't actually add // the length prefix (it is already implied by the actual string length) return u8aToHex(this.toU8a(true)); diff --git a/packages/types/src/types/codec.ts b/packages/types/src/types/codec.ts index 0f08145ea9..51a8e3a4ff 100644 --- a/packages/types/src/types/codec.ts +++ b/packages/types/src/types/codec.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { BN } from '@polkadot/util'; +import type { HexString } from '@polkadot/util/types'; import type { Hash } from '../interfaces/runtime'; import type { InterfaceTypes, Registry } from './registry'; @@ -76,7 +77,7 @@ export interface Codec { /** * @description Returns a hex string representation of the value. isLe returns a LE (number-only) representation */ - toHex (isLe?: boolean): string; + toHex (isLe?: boolean): HexString; /** * @description Converts the Object to to a human-friendly JSON, with additional fields, expansion and formatting of information @@ -116,6 +117,6 @@ export interface Constructor { new(registry: Registry, ...args: any[]): T; } -export type ConstructorDef = Record | Constructor>; +export type ConstructorDef = Record>; export type ArgsDef = Record; diff --git a/packages/types/src/types/extrinsic.ts b/packages/types/src/types/extrinsic.ts index acbd3ddd74..862ec4652c 100644 --- a/packages/types/src/types/extrinsic.ts +++ b/packages/types/src/types/extrinsic.ts @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { HexString } from '@polkadot/util/types'; import type { ExtrinsicStatus } from '../interfaces/author'; import type { EcdsaSignature, Ed25519Signature, Sr25519Signature } from '../interfaces/extrinsics'; import type { Address, Balance, Call, H256, Index } from '../interfaces/runtime'; @@ -125,7 +126,7 @@ export interface SignerResult { /** * @description The resulting signature in hex */ - signature: string; + signature: HexString; } export interface Signer { @@ -184,13 +185,13 @@ export interface ExtrinsicPayloadValue { } export interface IExtrinsicSignature extends ExtrinsicSignatureBase, Codec { - addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | string, payload: Uint8Array | string): IExtrinsicSignature; + addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | HexString, payload: Uint8Array | HexString): IExtrinsicSignature; sign (method: Call, account: IKeyringPair, options: SignatureOptions): IExtrinsicSignature; signFake (method: Call, address: Address | Uint8Array | string, options: SignatureOptions): IExtrinsicSignature; } interface IExtrinsicSignable { - addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | string, payload: ExtrinsicPayloadValue | Uint8Array | string): T; + addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | HexString, payload: ExtrinsicPayloadValue | Uint8Array | HexString): T; sign (account: IKeyringPair, options: SignatureOptions): T; signFake (address: Address | Uint8Array | string, options: SignatureOptions): T; } diff --git a/yarn.lock b/yarn.lock index 12ccae2b8f..426a5f7d31 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1874,9 +1874,9 @@ __metadata: languageName: unknown linkType: soft -"@polkadot/dev@npm:^0.63.10": - version: 0.63.10 - resolution: "@polkadot/dev@npm:0.63.10" +"@polkadot/dev@npm:^0.63.18": + version: 0.63.18 + resolution: "@polkadot/dev@npm:0.63.18" dependencies: "@babel/cli": ^7.15.7 "@babel/core": ^7.15.8 @@ -1902,10 +1902,10 @@ __metadata: "@rollup/plugin-inject": ^4.0.3 "@rollup/plugin-json": ^4.1.0 "@rollup/plugin-node-resolve": ^13.0.6 - "@rushstack/eslint-patch": ^1.0.8 + "@rushstack/eslint-patch": ^1.0.9 "@typescript-eslint/eslint-plugin": 5.2.0 "@typescript-eslint/parser": 5.2.0 - "@vue/component-compiler-utils": ^3.2.2 + "@vue/component-compiler-utils": ^3.3.0 babel-jest: ^27.3.1 babel-plugin-module-extension-resolver: ^1.0.0-rc.2 babel-plugin-module-resolver: ^4.1.0 @@ -1964,7 +1964,7 @@ __metadata: polkadot-exec-rollup: scripts/polkadot-exec-rollup.cjs polkadot-exec-tsc: scripts/polkadot-exec-tsc.cjs polkadot-exec-webpack: scripts/polkadot-exec-webpack.cjs - checksum: a47ef588d69c35d67fb96ffc8a386de1d5c722dd9817068cc063e0d634c4e9f356e937d572eb46bfdcbd28a4e9822c9469cb6cab8ec2eda024e0053b7d31e63a + checksum: 76c73b040bb02e9b1d7b050718e1e9b8bad4a20850b33e9597d0ccc07981e79c18f19ff3d1b4ab2827b86fe7fede8ee11f83b3ecf3518de7e294164de4b1c130 languageName: node linkType: hard @@ -2310,10 +2310,10 @@ __metadata: languageName: node linkType: hard -"@rushstack/eslint-patch@npm:^1.0.8": - version: 1.0.8 - resolution: "@rushstack/eslint-patch@npm:1.0.8" - checksum: e1c4e2d8317db712a4f24510eacb98c13fd18632eaffae0096a359ab02021ce44c35f614d489741e57f4b0f2d8b11b4b95cdc00aa57753d840d0ba512a5c609e +"@rushstack/eslint-patch@npm:^1.0.9": + version: 1.0.9 + resolution: "@rushstack/eslint-patch@npm:1.0.9" + checksum: 01425d38261013955990e4e923bc57dd1ff258a09740a5cbf5645b6a61cd3c85255edd11801277c07022fabbdfdb39d1d324f6acb1bc0b1795d2a0a187b9569f languageName: node linkType: hard @@ -2740,9 +2740,9 @@ __metadata: languageName: node linkType: hard -"@vue/component-compiler-utils@npm:^3.2.2": - version: 3.2.2 - resolution: "@vue/component-compiler-utils@npm:3.2.2" +"@vue/component-compiler-utils@npm:^3.3.0": + version: 3.3.0 + resolution: "@vue/component-compiler-utils@npm:3.3.0" dependencies: consolidate: ^0.15.1 hash-sum: ^1.0.2 @@ -2750,13 +2750,13 @@ __metadata: merge-source-map: ^1.1.0 postcss: ^7.0.36 postcss-selector-parser: ^6.0.2 - prettier: ^1.18.2 + prettier: ^1.18.2 || ^2.0.0 source-map: ~0.6.1 vue-template-es2015-compiler: ^1.9.0 dependenciesMeta: prettier: optional: true - checksum: ae2d08b4c1907c1bdb2c35e0d67e58afd3afa053bbc5f9cdb6de8915b051124d034cd6b14808aca5c93776289dd832e5514847e4fb925571857aa237262d753a + checksum: 70fee2289a4f54ec1be4d46136ee9b9893e31bf5622cead5be06c3dfb83449c3dbe6f8c03404625ccf302d0628ff9e2ea1debfae609d1bfe1d065d8f57c5dba8 languageName: node linkType: hard @@ -8390,16 +8390,7 @@ fsevents@~2.1.2: languageName: node linkType: hard -"prettier@npm:^1.18.2": - version: 1.19.1 - resolution: "prettier@npm:1.19.1" - bin: - prettier: ./bin-prettier.js - checksum: bc78219e0f8173a808f4c6c8e0a137dd8ebd4fbe013e63fe1a37a82b48612f17b8ae8e18a992adf802ee2cf7428f14f084e7c2846ca5759cf4013c6e54810e1f - languageName: node - linkType: hard - -"prettier@npm:^2.4.1": +"prettier@npm:^1.18.2 || ^2.0.0, prettier@npm:^2.4.1": version: 2.4.1 resolution: "prettier@npm:2.4.1" bin: @@ -8952,7 +8943,7 @@ resolve@^2.0.0-next.3: "@babel/core": ^7.15.8 "@babel/register": ^7.15.3 "@babel/runtime": ^7.15.4 - "@polkadot/dev": ^0.63.10 + "@polkadot/dev": ^0.63.18 "@polkadot/ts": ^0.4.12 "@polkadot/typegen": "workspace:packages/typegen" "@types/jest": ^27.0.2