From 8dc0f0486d034e82ac9f9a6c850169bd71033f63 Mon Sep 17 00:00:00 2001 From: Jaco Date: Fri, 13 May 2022 15:55:13 +0200 Subject: [PATCH] Small decoding cleanups (#4823) * Small decoding cleanups * isHex * Adjust struct * Additional --- .../types-codec/src/abstract/AbstractInt.ts | 8 ++--- packages/types-codec/src/base/Enum.ts | 10 +++--- packages/types-codec/src/base/Tuple.ts | 2 +- packages/types-codec/src/base/VecFixed.ts | 2 +- packages/types-codec/src/extended/BTreeSet.ts | 22 ++++++------- packages/types-codec/src/extended/Bytes.ts | 21 ++++-------- packages/types-codec/src/extended/Map.ts | 18 +++++------ packages/types-codec/src/native/Bool.ts | 19 ++++------- packages/types-codec/src/native/Date.ts | 2 +- packages/types-codec/src/native/Set.ts | 4 +-- packages/types-codec/src/native/Text.ts | 2 -- packages/types/src/metadata/Metadata.ts | 32 +++++++++---------- .../types/src/metadata/MetadataVersioned.ts | 5 +-- 13 files changed, 63 insertions(+), 84 deletions(-) diff --git a/packages/types-codec/src/abstract/AbstractInt.ts b/packages/types-codec/src/abstract/AbstractInt.ts index 8f0ddea773..038e8764d1 100644 --- a/packages/types-codec/src/abstract/AbstractInt.ts +++ b/packages/types-codec/src/abstract/AbstractInt.ts @@ -25,10 +25,8 @@ function toPercentage (value: BN, divisor: BN): string { } /** @internal */ -function decodeAbstractInt (value: AnyNumber, bitLength: UIntBitLength, isNegative: boolean): string { - if (isU8a(value)) { - return u8aToBn(value.subarray(0, bitLength / 8), { isLe: true, isNegative }).toString(); - } else if (isBn(value)) { +function decodeAbstractInt (value: Exclude, isNegative: boolean): string { + if (isBn(value)) { return value.toString(); } else if (isHex(value, -1, true)) { return hexToBn(value, { isLe: false, isNegative }).toString(); @@ -65,7 +63,7 @@ export abstract class AbstractInt extends BN implements INumber { // shortcut isU8a as used in SCALE decoding isU8a(value) ? u8aToBn(value.subarray(0, bitLength / 8), { isLe: true, isNegative: isSigned }).toString() - : decodeAbstractInt(value, bitLength, isSigned) + : decodeAbstractInt(value, isSigned) ); this.registry = registry; diff --git a/packages/types-codec/src/base/Enum.ts b/packages/types-codec/src/base/Enum.ts index 0c43a2107c..f5534276c2 100644 --- a/packages/types-codec/src/base/Enum.ts +++ b/packages/types-codec/src/base/Enum.ts @@ -4,7 +4,7 @@ import type { HexString } from '@polkadot/util/types'; import type { AnyJson, Codec, CodecClass, IEnum, Inspect, IU8a, Registry } from '../types'; -import { assert, isHex, isNumber, isObject, isString, isU8a, isUndefined, objectProperties, stringCamelCase, stringify, stringPascalCase, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; +import { assert, isCodec, isHex, isNumber, isObject, isString, isU8a, isUndefined, objectProperties, stringCamelCase, stringify, stringPascalCase, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; import { mapToTypeMap, typesToMap } from '../utils'; import { Null } from './Null'; @@ -88,7 +88,7 @@ function createFromValue (registry: Registry, def: TypesDef, index = 0, value?: return { index, - value: value instanceof entry.Type + value: isCodec(value) && value instanceof entry.Type ? value : new entry.Type(registry, value) }; @@ -266,7 +266,7 @@ export class Enum implements IEnum { * @deprecated use isNone */ public get isNull (): boolean { - return this.#raw instanceof Null; + return this.isNone; } /** @@ -310,7 +310,7 @@ export class Enum implements IEnum { return this.type === other; } else if (isHex(other)) { return this.toHex() === other; - } else if (other instanceof Enum) { + } else if (isCodec(other) && other instanceof Enum) { return this.index === other.index && this.value.eq(other.value); } else if (isObject(other)) { return this.value.eq(other[this.type]); @@ -404,7 +404,7 @@ export class Enum implements IEnum { * @description Returns the string representation of the value */ public toString (): string { - return this.isNull + return this.isNone ? this.type : stringify(this.toJSON()); } diff --git a/packages/types-codec/src/base/Tuple.ts b/packages/types-codec/src/base/Tuple.ts index a7baf6351f..b5e3e875d4 100644 --- a/packages/types-codec/src/base/Tuple.ts +++ b/packages/types-codec/src/base/Tuple.ts @@ -19,7 +19,7 @@ type TupleTypes = TupleType[] | { }; /** @internal */ -function decodeTuple (registry: Registry, Classes: TupleCodecClasss, value?: AnyTupleValue): [Codec[], number] { +function decodeTuple (registry: Registry, Classes: TupleCodecClasss, value?: Exclude): [Codec[], number] { if (isU8a(value) || isHex(value)) { return decodeU8a(registry, u8aToU8a(value), Classes); } diff --git a/packages/types-codec/src/base/VecFixed.ts b/packages/types-codec/src/base/VecFixed.ts index bdada91cc8..48af559d35 100644 --- a/packages/types-codec/src/base/VecFixed.ts +++ b/packages/types-codec/src/base/VecFixed.ts @@ -11,7 +11,7 @@ import { decodeU8aVec, typeToConstructor } from '../utils'; import { decodeVec } from './Vec'; /** @internal */ -function decodeVecFixed (registry: Registry, value: Uint8Array | HexString | unknown[], Type: CodecClass, length: number): [T[], number, number] { +function decodeVecFixed (registry: Registry, value: HexString | unknown[], Type: CodecClass, length: number): [T[], number, number] { const [values, decodedLength, decodedLengthNoOffset] = decodeVec(registry, Type, value, length); while (values.length < length) { diff --git a/packages/types-codec/src/extended/BTreeSet.ts b/packages/types-codec/src/extended/BTreeSet.ts index f3451ddc9c..fb4184e80b 100644 --- a/packages/types-codec/src/extended/BTreeSet.ts +++ b/packages/types-codec/src/extended/BTreeSet.ts @@ -11,7 +11,7 @@ import { compareSet, decodeU8aVec, sortSet, typeToConstructor } from '../utils'; const l = logger('BTreeSet'); /** @internal */ -function decodeSetFromU8a (registry: Registry, ValClass: CodecClass, u8a: Uint8Array): [Set, number] { +function decodeSetFromU8a (registry: Registry, ValClass: CodecClass, u8a: Uint8Array): [CodecClass, Set, number] { const output = new Set(); const [offset, length] = compactFromU8a(u8a); const [values, decodedLength] = decodeU8aVec(registry, u8a, offset, ValClass, length.toNumber()); @@ -20,11 +20,11 @@ function decodeSetFromU8a (registry: Registry, ValClass: CodecC output.add(values[i]); } - return [output, decodedLength]; + return [ValClass, output, decodedLength]; } /** @internal */ -function decodeSetFromSet (registry: Registry, ValClass: CodecClass, value: Set | string[]): [Set, number] { +function decodeSetFromSet (registry: Registry, ValClass: CodecClass, value: Set | string[]): [CodecClass, Set, number] { const output = new Set(); value.forEach((val: any) => { @@ -37,7 +37,7 @@ function decodeSetFromSet (registry: Registry, ValClass: CodecC } }); - return [output, 0]; + return [ValClass, output, 0]; } /** @@ -54,14 +54,12 @@ function decodeSetFromSet (registry: Registry, ValClass: CodecC * @param jsonSet * @internal */ -function decodeSet (registry: Registry, valType: CodecClass | string, value?: Uint8Array | string | string[] | Set): [Set, number] { - if (!value) { - return [new Set(), 0]; - } - +function decodeSet (registry: Registry, valType: CodecClass | string, value?: Uint8Array | string | string[] | Set): [CodecClass, Set, number] { const ValClass = typeToConstructor(registry, valType); - if (isU8a(value) || isHex(value)) { + if (!value) { + return [ValClass, new Set(), 0]; + } else if (isU8a(value) || isHex(value)) { return decodeSetFromU8a(registry, ValClass, u8aToU8a(value)); } else if (Array.isArray(value) || value instanceof Set) { return decodeSetFromSet(registry, ValClass, value); @@ -80,13 +78,13 @@ export class BTreeSet extends Set implements ISet readonly #ValClass: CodecClass; constructor (registry: Registry, valType: CodecClass | string, rawValue?: Uint8Array | string | string[] | Set) { - const [values, decodedLength] = decodeSet(registry, valType, rawValue); + const [ValClass, values, decodedLength] = decodeSet(registry, valType, rawValue); super(sortSet(values)); this.registry = registry; this.initialU8aLength = decodedLength; - this.#ValClass = typeToConstructor(registry, valType); + this.#ValClass = ValClass; } public static with (valType: CodecClass | string): CodecClass> { diff --git a/packages/types-codec/src/extended/Bytes.ts b/packages/types-codec/src/extended/Bytes.ts index fe703aebdd..2f9543f769 100644 --- a/packages/types-codec/src/extended/Bytes.ts +++ b/packages/types-codec/src/extended/Bytes.ts @@ -3,7 +3,7 @@ import type { AnyU8a, Inspect, Registry } from '../types'; -import { assert, compactAddLength, compactFromU8a, compactToU8a, isString, isU8a, u8aToU8a } from '@polkadot/util'; +import { assert, compactAddLength, compactFromU8a, compactToU8a, isCodec, isString, isU8a, u8aToU8a } from '@polkadot/util'; import { Raw } from '../native/Raw'; @@ -26,19 +26,6 @@ function decodeBytesU8a (value: Uint8Array): [Uint8Array, number] { return [value.subarray(offset, total), total]; } -/** @internal */ -function decodeBytes (value?: AnyU8a): [Uint8Array | undefined, number] { - if (Array.isArray(value) || isString(value)) { - return [u8aToU8a(value), 0]; - } else if (!(value instanceof Raw) && isU8a(value)) { - // We are ensuring we are not a Raw instance. In the case of a Raw we already have gotten - // rid of the length, i.e. new Bytes(new Bytes(...)) will work as expected - return decodeBytesU8a(value); - } - - return [value, 0]; -} - /** * @name Bytes * @description @@ -48,7 +35,11 @@ function decodeBytes (value?: AnyU8a): [Uint8Array | undefined, number] { */ export class Bytes extends Raw { constructor (registry: Registry, value?: AnyU8a) { - const [u8a, decodedLength] = decodeBytes(value); + const [u8a, decodedLength] = isU8a(value) && !(isCodec(value) && value instanceof Raw) + ? decodeBytesU8a(value) + : Array.isArray(value) || isString(value) + ? [u8aToU8a(value), 0] + : [value, 0]; super(registry, u8a, decodedLength); } diff --git a/packages/types-codec/src/extended/Map.ts b/packages/types-codec/src/extended/Map.ts index 395caebd45..1f38a2f205 100644 --- a/packages/types-codec/src/extended/Map.ts +++ b/packages/types-codec/src/extended/Map.ts @@ -14,7 +14,7 @@ import { compareMap, decodeU8a, sortMap, typeToConstructor } from '../utils'; const l = logger('Map'); /** @internal */ -function decodeMapFromU8a (registry: Registry, KeyClass: CodecClass, ValClass: CodecClass, u8a: Uint8Array): [Map, number] { +function decodeMapFromU8a (registry: Registry, KeyClass: CodecClass, ValClass: CodecClass, u8a: Uint8Array): [CodecClass, CodecClass, Map, number] { const output = new Map(); const [offset, length] = compactFromU8a(u8a); const count = length.toNumber(); @@ -30,11 +30,11 @@ function decodeMapFromU8a (registry: Registry, output.set(values[i] as K, values[i + 1] as V); } - return [output, offset + decodedLength]; + return [KeyClass, ValClass, output, offset + decodedLength]; } /** @internal */ -function decodeMapFromMap (registry: Registry, KeyClass: CodecClass, ValClass: CodecClass, value: Map): [Map, number] { +function decodeMapFromMap (registry: Registry, KeyClass: CodecClass, ValClass: CodecClass, value: Map): [CodecClass, CodecClass, Map, number] { const output = new Map(); for (const [key, val] of value.entries()) { @@ -58,7 +58,7 @@ function decodeMapFromMap (registry: Registry, } } - return [output, 0]; + return [KeyClass, ValClass, output, 0]; } /** @@ -76,12 +76,12 @@ function decodeMapFromMap (registry: Registry, * @param jsonMap * @internal */ -function decodeMap (registry: Registry, keyType: CodecClass | string, valType: CodecClass | string, value?: Uint8Array | string | Map): [Map, number] { +function decodeMap (registry: Registry, keyType: CodecClass | string, valType: CodecClass | string, value?: Uint8Array | string | Map): [CodecClass, CodecClass, Map, number] { const KeyClass = typeToConstructor(registry, keyType); const ValClass = typeToConstructor(registry, valType); if (!value) { - return [new Map(), 0]; + return [KeyClass, ValClass, new Map(), 0]; } else if (isU8a(value) || isHex(value)) { return decodeMapFromU8a(registry, KeyClass, ValClass, u8aToU8a(value)); } else if (value instanceof Map) { @@ -107,14 +107,14 @@ export class CodecMap extends readonly #type: string; constructor (registry: Registry, keyType: CodecClass | string, valType: CodecClass | string, rawValue: Uint8Array | string | Map | undefined, type: 'BTreeMap' | 'HashMap' = 'HashMap') { - const [decoded, decodedLength] = decodeMap(registry, keyType, valType, rawValue); + const [KeyClass, ValClass, decoded, decodedLength] = decodeMap(registry, keyType, valType, rawValue); super(type === 'BTreeMap' ? sortMap(decoded) : decoded); this.registry = registry; this.initialU8aLength = decodedLength; - this.#KeyClass = typeToConstructor(registry, keyType); - this.#ValClass = typeToConstructor(registry, valType); + this.#KeyClass = KeyClass; + this.#ValClass = ValClass; this.#type = type; } diff --git a/packages/types-codec/src/native/Bool.ts b/packages/types-codec/src/native/Bool.ts index cdb6b033fb..15c82a3508 100644 --- a/packages/types-codec/src/native/Bool.ts +++ b/packages/types-codec/src/native/Bool.ts @@ -6,17 +6,6 @@ import type { Codec, Inspect, IU8a, Registry } from '../types'; import { isU8a, u8aToHex } from '@polkadot/util'; -/** @internal */ -function decodeBool (value: any): boolean { - if (isU8a(value)) { - return value[0] === 1; - } else if (value instanceof Boolean) { - return value.valueOf(); - } - - return !!value; -} - /** * @name bool * @description @@ -30,7 +19,13 @@ export class bool extends Boolean implements Codec { // eslint-disable-next-line @typescript-eslint/ban-types constructor (registry: Registry, value: bool | Boolean | Uint8Array | boolean | number = false) { - super(decodeBool(value)); + super( + isU8a(value) + ? value[0] === 1 + : value instanceof Boolean + ? value.valueOf() + : !!value + ); this.registry = registry; } diff --git a/packages/types-codec/src/native/Date.ts b/packages/types-codec/src/native/Date.ts index 8096c51037..066d93a210 100644 --- a/packages/types-codec/src/native/Date.ts +++ b/packages/types-codec/src/native/Date.ts @@ -10,7 +10,7 @@ const BITLENGTH: UIntBitLength = 64; function decodeDate (value: CodecDate | Date | AnyNumber): Date { if (isU8a(value)) { - value = u8aToBn(value.subarray(0, BITLENGTH / 8), true); + value = u8aToBn(value.subarray(0, BITLENGTH / 8)); } else if (value instanceof Date) { return value; } else if (isString(value)) { diff --git a/packages/types-codec/src/native/Set.ts b/packages/types-codec/src/native/Set.ts index 9abf47378d..b1f37e4ae1 100644 --- a/packages/types-codec/src/native/Set.ts +++ b/packages/types-codec/src/native/Set.ts @@ -111,8 +111,8 @@ export class CodecSet extends Set implements ISet { } return class extends CodecSet { - constructor (registry: Registry, value?: unknown) { - super(registry, values, value as undefined, bitLength); + constructor (registry: Registry, value?: string[] | Set | Uint8Array | BN | number | string) { + super(registry, values, value, bitLength); objectProperties(this, isKeys, (_, i) => this.strings.includes(keys[i])); } diff --git a/packages/types-codec/src/native/Text.ts b/packages/types-codec/src/native/Text.ts index 39ea3b4a59..8993101ae5 100644 --- a/packages/types-codec/src/native/Text.ts +++ b/packages/types-codec/src/native/Text.ts @@ -45,8 +45,6 @@ function decodeText (value?: null | Text | string | AnyU8a | { toString: () => s * object, inheriting all methods exposed from `String`. * @noInheritDoc */ -// TODO -// - Strings should probably be trimmed (docs do come through with extra padding) export class Text extends String implements IText { public readonly registry: Registry; diff --git a/packages/types/src/metadata/Metadata.ts b/packages/types/src/metadata/Metadata.ts index c56ab687e8..94276f7c7b 100644 --- a/packages/types/src/metadata/Metadata.ts +++ b/packages/types/src/metadata/Metadata.ts @@ -4,7 +4,7 @@ import type { Registry } from '@polkadot/types-codec/types'; import type { HexString } from '@polkadot/util/types'; -import { isHex, isU8a, u8aToU8a } from '@polkadot/util'; +import { isString, isU8a, u8aToU8a } from '@polkadot/util'; import { MetadataVersioned } from './MetadataVersioned'; @@ -14,25 +14,25 @@ const VERSION_IDX = 4; // magic + lowest supported version const EMPTY_METADATA = new Uint8Array([0x6d, 0x65, 0x74, 0x61, 9]); -function decodeU8a (registry: Registry, value: Uint8Array): MetadataVersioned { +function decodeU8a (registry: Registry, value: Uint8Array): MetadataVersioned | Uint8Array { const u8a = value.length === 0 ? EMPTY_METADATA : value; - try { - return new MetadataVersioned(registry, u8a); - } catch (error) { - // This is an f-ing hack as a follow-up to another ugly hack - // https://github.com/polkadot-js/api/commit/a9211690be6b68ad6c6dad7852f1665cadcfa5b2 - // when we fail on V9, try to re-parse it as v10... yes... HACK - if (u8a[VERSION_IDX] === 9) { + // This is an f-ing hack as a follow-up to another ugly hack + // https://github.com/polkadot-js/api/commit/a9211690be6b68ad6c6dad7852f1665cadcfa5b2 + // when we fail on V9, try to re-parse it as v10... + if (u8a[VERSION_IDX] === 9) { + try { + return new MetadataVersioned(registry, u8a); + } catch (error) { u8a[VERSION_IDX] = 10; - return decodeU8a(registry, u8a); + return u8a; } - - throw error; } + + return u8a; } /** @@ -46,11 +46,9 @@ export class Metadata extends MetadataVersioned { super( registry, - isU8a(value) - ? decodeU8a(registry, value) - : isHex(value) - ? decodeU8a(registry, u8aToU8a(value)) - : new MetadataVersioned(registry, value) + isU8a(value) || isString(value) + ? decodeU8a(registry, u8aToU8a(value)) + : value ); // console.timeEnd('Metadata') diff --git a/packages/types/src/metadata/MetadataVersioned.ts b/packages/types/src/metadata/MetadataVersioned.ts index 621f19121f..97d4ba0fac 100644 --- a/packages/types/src/metadata/MetadataVersioned.ts +++ b/packages/types/src/metadata/MetadataVersioned.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { AnyJson } from '@polkadot/types-codec/types'; +import type { HexString } from '@polkadot/util/types'; import type { MetadataAll, MetadataLatest, MetadataV9, MetadataV10, MetadataV11, MetadataV12, MetadataV13, MetadataV14 } from '../interfaces/metadata'; import type { Registry } from '../types'; @@ -31,13 +32,13 @@ const LATEST_VERSION = 14; export class MetadataVersioned extends Struct { readonly #converted = new Map(); - constructor (registry: Registry, value?: unknown) { + constructor (registry: Registry, value?: Uint8Array | HexString | Map | Record) { // console.time('MetadataVersioned') super(registry, { magicNumber: MagicNumber, metadata: 'MetadataAll' - }, value as Map); + }, value); // console.timeEnd('MetadataVersioned') }