From 4796b538b8f36e8f9b9d1123a8c49e488538daa6 Mon Sep 17 00:00:00 2001 From: Jaco Date: Sat, 18 Jun 2022 13:37:09 +0300 Subject: [PATCH] Adjust type asserts --- CHANGELOG.md | 2 + packages/types-codec/src/abstract/Int.ts | 21 +++-- packages/types-codec/src/base/Compact.spec.ts | 6 ++ packages/types-codec/src/base/Enum.ts | 18 +++-- packages/types-codec/src/base/Option.ts | 6 +- packages/types-codec/src/base/Result.ts | 10 ++- packages/types-codec/src/base/UInt.spec.ts | 4 + packages/types-codec/src/base/Vec.spec.ts | 7 +- packages/types-codec/src/base/Vec.ts | 6 +- packages/types-codec/src/extended/BitVec.ts | 6 +- packages/types-codec/src/extended/Bytes.ts | 9 ++- packages/types-codec/src/extended/U8aFixed.ts | 6 +- .../src/extended/WrapperKeepOpaque.ts | 8 +- packages/types-codec/src/native/Raw.ts | 6 +- packages/types-codec/src/native/Set.ts | 18 +++-- packages/types-codec/src/native/Struct.ts | 9 ++- packages/types-codec/src/native/Text.spec.ts | 6 +- packages/types-codec/src/native/Text.ts | 9 ++- packages/types-codec/src/test/performance.ts | 77 +++++++++++++++++++ packages/types-codec/tsconfig.build.json | 3 +- packages/types-codec/tsconfig.spec.json | 3 +- packages/types-create/src/create/class.ts | 30 ++++++-- packages/types-create/src/create/type.ts | 6 +- packages/types-create/src/util/encodeTypes.ts | 26 +++++-- packages/types-create/src/util/getTypeDef.ts | 25 +++--- packages/types-create/src/util/typeSplit.ts | 6 +- packages/types/src/create/registry.ts | 17 ++-- packages/types/src/metadata/MagicNumber.ts | 5 +- .../types/src/metadata/MetadataVersioned.ts | 5 +- .../PortableRegistry/PortableRegistry.ts | 35 ++++++--- .../decorate/extrinsics/createUnchecked.ts | 6 +- packages/types/src/metadata/decorate/index.ts | 6 +- .../decorate/storage/createFunction.ts | 20 +++-- packages/types/src/metadata/util/testUtil.ts | 6 +- 34 files changed, 320 insertions(+), 113 deletions(-) create mode 100644 packages/types-codec/src/test/performance.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b904a82ae60..becede5f7595 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,9 @@ Contributed: Changes: - Deupe all internal type property getters +- Adjust `@polkadot/types-codec` asserts - Rename `Base` type to `AbstractBase` (reflecting usage) +- Alias `blockHash` on `rpc.engine.createBlock` return - Instantiate `Compact/UInt/Int` with `number` when passed - Adjust name extraction in `PortableRegistry` (maintainability) - Adjust string type path extraction (maintainability) diff --git a/packages/types-codec/src/abstract/Int.ts b/packages/types-codec/src/abstract/Int.ts index 7dbf8967eb46..06e7c80f951f 100644 --- a/packages/types-codec/src/abstract/Int.ts +++ b/packages/types-codec/src/abstract/Int.ts @@ -4,7 +4,7 @@ import type { HexString } from '@polkadot/util/types'; import type { AnyNumber, Inspect, INumber, IU8a, Registry, UIntBitLength } from '../types'; -import { assert, BN, BN_BILLION, BN_HUNDRED, BN_MILLION, BN_QUINTILL, BN_ZERO, bnToBn, bnToHex, bnToU8a, formatBalance, formatNumber, hexToBn, isBn, isHex, isNumber, isString, isU8a, u8aToBn, u8aToNumber } from '@polkadot/util'; +import { BN, BN_BILLION, BN_HUNDRED, BN_MILLION, BN_QUINTILL, bnToBn, bnToHex, bnToU8a, formatBalance, formatNumber, hexToBn, isBn, isHex, isNumber, isString, isU8a, u8aToBn, u8aToNumber } from '@polkadot/util'; export const DEFAULT_UINT_BITS = 64; @@ -27,7 +27,9 @@ function toPercentage (value: BN, divisor: BN): string { /** @internal */ function decodeAbstractInt (value: Exclude, isNegative: boolean): string | number { if (isNumber(value)) { - assert(value <= Number.MAX_SAFE_INTEGER && value >= Number.MIN_SAFE_INTEGER && Number.isInteger(value), 'Number needs to be an integer <= Number.MAX_SAFE_INTEGER, i.e. 2 ^ 53 - 1'); + if (!Number.isInteger(value) || value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER) { + throw new Error('Number needs to be an integer <= Number.MAX_SAFE_INTEGER, i.e. 2 ^ 53 - 1'); + } return value; } else if (isString(value)) { @@ -35,7 +37,9 @@ function decodeAbstractInt (value: Exclude, isNegative: b return hexToBn(value, { isLe: false, isNegative }).toString(); } - assert(!(value.includes('.') || value.includes(',') || value.includes('e')), 'String should not contain decimal points or scientific notation'); + if (value.includes('.') || value.includes(',') || value.includes('e')) { + throw new Error('String should not contain decimal points or scientific notation'); + } return value; } else if (isBn(value)) { @@ -79,11 +83,14 @@ export abstract class AbstractInt extends BN implements INumber { this.encodedLength = this.#bitLength / 8; this.isUnsigned = !isSigned; - const isPositive = this.gte(BN_ZERO); - const maxBits = bitLength - (isSigned && isPositive ? 1 : 0); + const isNegative = this.isNeg(); + const maxBits = bitLength - (isSigned && !isNegative ? 1 : 0); - assert(isSigned || isPositive, () => `${this.toRawType()}: Negative number passed to unsigned type`); - assert(super.bitLength() <= maxBits, () => `${this.toRawType()}: Input too large. Found input with ${super.bitLength()} bits, expected ${maxBits}`); + if (isNegative && !isSigned) { + throw new Error(`${this.toRawType()}: Negative number passed to unsigned type`); + } else if (super.bitLength() > maxBits) { + throw new Error(`${this.toRawType()}: Input too large. Found input with ${super.bitLength()} bits, expected ${maxBits}`); + } } /** diff --git a/packages/types-codec/src/base/Compact.spec.ts b/packages/types-codec/src/base/Compact.spec.ts index cf1faf658585..266ac6c3fae2 100644 --- a/packages/types-codec/src/base/Compact.spec.ts +++ b/packages/types-codec/src/base/Compact.spec.ts @@ -5,6 +5,10 @@ import { TypeRegistry } from '@polkadot/types'; import { CodecDate, Compact, U32 } from '@polkadot/types-codec'; import { BN } from '@polkadot/util'; +import { performance } from '../test/performance'; + +const CompactU32 = Compact.with(U32); + describe('Compact', (): void => { const registry = new TypeRegistry(); @@ -88,4 +92,6 @@ describe('Compact', (): void => { }); }); }); + + performance('Compact', 75_000, [[new Uint8Array([63 << 2])]], (v: Uint8Array) => new CompactU32(registry, v)); }); diff --git a/packages/types-codec/src/base/Enum.ts b/packages/types-codec/src/base/Enum.ts index 197838982d7f..2c2ed31d2595 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, u8aConcatStrict, u8aToHex, u8aToU8a } from '@polkadot/util'; +import { isHex, isNumber, isObject, isString, isU8a, isUndefined, objectProperties, stringCamelCase, stringify, stringPascalCase, u8aConcatStrict, u8aToHex, u8aToU8a } from '@polkadot/util'; import { mapToTypeMap, typesToMap } from '../utils'; import { Null } from './Null'; @@ -45,7 +45,9 @@ function isRustEnum (def: Record | Record isNumber(v))) { - assert(defValues.every((v) => isNumber(v) && v >= 0 && v <= 255), 'Invalid number-indexed enum definition'); + if (!defValues.every((v) => isNumber(v) && v >= 0 && v <= 255)) { + throw new Error('Invalid number-indexed enum definition'); + } return false; } @@ -97,7 +99,9 @@ function extractDef (registry: Registry, _def: Record e.index === index); - assert(!isUndefined(entry), () => `Unable to create Enum via index ${index}, in ${Object.keys(def).join(', ')}`); + if (isUndefined(entry)) { + throw new Error(`Unable to create Enum via index ${index}, in ${Object.keys(def).join(', ')}`); + } return { index, @@ -114,7 +118,9 @@ function decodeFromJSON (registry: Registry, def: TypesDef, key: string, value?: const keyLower = key.toLowerCase(); const index = keys.indexOf(keyLower); - assert(index !== -1, () => `Cannot map Enum JSON, unable to find '${key}' in ${keys.join(', ')}`); + if (index === -1) { + throw new Error(`Cannot map Enum JSON, unable to find '${key}' in ${keys.join(', ')}`); + } try { return createFromValue(registry, def, Object.values(def)[index].index, value); @@ -223,7 +229,9 @@ export class Enum implements IEnum { objectProperties(this, isKeys, (_, i) => this.type === keys[i]); objectProperties(this, asKeys, (k, i): Codec => { - assert(this[isKeys[i] as keyof this], () => `Cannot convert '${this.type}' via ${k}`); + if (!this[isKeys[i] as keyof this]) { + throw new Error(`Cannot convert '${this.type}' via ${k}`); + } return this.value; }); diff --git a/packages/types-codec/src/base/Option.ts b/packages/types-codec/src/base/Option.ts index ea88140604bb..167714bf2d9d 100644 --- a/packages/types-codec/src/base/Option.ts +++ b/packages/types-codec/src/base/Option.ts @@ -4,7 +4,7 @@ import type { HexString } from '@polkadot/util/types'; import type { AnyJson, Codec, CodecClass, Inspect, IOption, IU8a, Registry } from '../types'; -import { assert, isCodec, isNull, isU8a, isUndefined, u8aToHex } from '@polkadot/util'; +import { isCodec, isNull, isU8a, isUndefined, u8aToHex } from '@polkadot/util'; import { typeToConstructor } from '../utils'; import { Null } from './Null'; @@ -247,7 +247,9 @@ export class Option implements IOption { * @description Returns the value that the Option represents (if available), throws if null */ public unwrap (): T { - assert(this.isSome, 'Option: unwrapping a None value'); + if (this.isNone) { + throw new Error('Option: unwrapping a None value'); + } return this.#raw; } diff --git a/packages/types-codec/src/base/Result.ts b/packages/types-codec/src/base/Result.ts index 828f898d9155..769b554dd853 100644 --- a/packages/types-codec/src/base/Result.ts +++ b/packages/types-codec/src/base/Result.ts @@ -3,8 +3,6 @@ import type { Codec, CodecClass, IResult, Registry } from '../types'; -import { assert } from '@polkadot/util'; - import { Enum } from './Enum'; /** @@ -31,7 +29,9 @@ export class Result extends Enum implements IR * @description Returns the wrapper Err value (if isErr) */ public get asErr (): E { - assert(this.isErr, 'Cannot extract Err value from Ok result, check isErr first'); + if (!this.isErr) { + throw new Error('Cannot extract Err value from Ok result, check isErr first'); + } return this.value as E; } @@ -47,7 +47,9 @@ export class Result extends Enum implements IR * @description Returns the wrapper Ok value (if isOk) */ public get asOk (): O { - assert(this.isOk, 'Cannot extract Ok value from Err result, check isOk first'); + if (!this.isOk) { + throw new Error('Cannot extract Ok value from Err result, check isOk first'); + } return this.value as O; } diff --git a/packages/types-codec/src/base/UInt.spec.ts b/packages/types-codec/src/base/UInt.spec.ts index c0ae7022cf73..82d42ddcb811 100644 --- a/packages/types-codec/src/base/UInt.spec.ts +++ b/packages/types-codec/src/base/UInt.spec.ts @@ -5,6 +5,8 @@ import { TypeRegistry } from '@polkadot/types'; import { UInt } from '@polkadot/types-codec'; import { BN, BN_TWO, isBn } from '@polkadot/util'; +import { performance } from '../test/performance'; + describe('UInt', (): void => { const registry = new TypeRegistry(); @@ -180,4 +182,6 @@ describe('UInt', (): void => { expect(registry.createType('Balance', '123456789012345').toHuman()).toEqual('123.4567 Unit'); }); }); + + performance('UInt', 75_000, [[new Uint8Array([31, 32, 33, 34])]], (v: Uint8Array) => new UInt(registry, v)); }); diff --git a/packages/types-codec/src/base/Vec.spec.ts b/packages/types-codec/src/base/Vec.spec.ts index 5e8578d4d1b1..231539cde954 100644 --- a/packages/types-codec/src/base/Vec.spec.ts +++ b/packages/types-codec/src/base/Vec.spec.ts @@ -5,12 +5,15 @@ import type { PropIndex } from '@polkadot/types/interfaces/democracy'; import type { Codec, CodecTo, ITuple } from '@polkadot/types-codec/types'; import { createTypeUnsafe, GenericAccountId as AccountId, Metadata, TypeRegistry } from '@polkadot/types'; -import { Text, Vec } from '@polkadot/types-codec'; +import { Text, u32, Vec } from '@polkadot/types-codec'; import rpcMetadata from '@polkadot/types-support/metadata/static-substrate'; import { decodeAddress, randomAsU8a } from '@polkadot/util-crypto'; +import { performance } from '../test/performance'; + const registry = new TypeRegistry(); const metadata = new Metadata(registry, rpcMetadata); +const VecU32 = Vec.with(u32); registry.setMetadata(metadata); @@ -190,4 +193,6 @@ describe('Vec', (): void => { }); }); }); + + performance('Vec', 40_000, [[new Uint8Array([3 << 2, 11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34])]], (v: Uint8Array) => new VecU32(registry, v)); }); diff --git a/packages/types-codec/src/base/Vec.ts b/packages/types-codec/src/base/Vec.ts index 127e15c221e0..002931cd6dbc 100644 --- a/packages/types-codec/src/base/Vec.ts +++ b/packages/types-codec/src/base/Vec.ts @@ -4,7 +4,7 @@ import type { HexString } from '@polkadot/util/types'; import type { Codec, CodecClass, Registry } from '../types'; -import { assert, compactFromU8aLim, isU8a, logger, u8aToU8a } from '@polkadot/util'; +import { compactFromU8aLim, isU8a, logger, u8aToU8a } from '@polkadot/util'; import { AbstractArray } from '../abstract/Array'; import { decodeU8aVec, typeToConstructor } from '../utils'; @@ -30,7 +30,9 @@ function decodeVecLength (value: Uint8Array | HexString | unknown[]): [Uint8Arra const u8a = u8aToU8a(value); const [startAt, length] = compactFromU8aLim(u8a); - assert(length <= MAX_LENGTH, () => `Vec length ${length.toString()} exceeds ${MAX_LENGTH}`); + if (length > MAX_LENGTH) { + throw new Error(`Vec length ${length.toString()} exceeds ${MAX_LENGTH}`); + } return [u8a, length, startAt]; } diff --git a/packages/types-codec/src/extended/BitVec.ts b/packages/types-codec/src/extended/BitVec.ts index 9d9286229270..1bd822fab20e 100644 --- a/packages/types-codec/src/extended/BitVec.ts +++ b/packages/types-codec/src/extended/BitVec.ts @@ -3,7 +3,7 @@ import type { AnyU8a, Inspect, Registry } from '../types'; -import { assert, compactFromU8aLim, compactToU8a, isString, u8aConcatStrict, u8aToU8a } from '@polkadot/util'; +import { compactFromU8aLim, compactToU8a, isString, u8aConcatStrict, u8aToU8a } from '@polkadot/util'; import { Raw } from '../native/Raw'; @@ -17,7 +17,9 @@ function decodeBitVecU8a (value?: Uint8Array): [number, Uint8Array] { const [offset, length] = compactFromU8aLim(value); const total = offset + Math.ceil(length / 8); - assert(total <= value.length, () => `BitVec: required length less than remainder, expected at least ${total}, found ${value.length}`); + if (total > value.length) { + throw new Error(`BitVec: required length less than remainder, expected at least ${total}, found ${value.length}`); + } return [length, value.subarray(offset, total)]; } diff --git a/packages/types-codec/src/extended/Bytes.ts b/packages/types-codec/src/extended/Bytes.ts index ee6eb1b3b456..b3a0cf3d4dbd 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, compactFromU8aLim, compactToU8a, isString, isU8a, u8aToU8a } from '@polkadot/util'; +import { compactAddLength, compactFromU8aLim, compactToU8a, isString, isU8a, u8aToU8a } from '@polkadot/util'; import { Raw } from '../native/Raw'; @@ -20,8 +20,11 @@ function decodeBytesU8a (value: Uint8Array): [Uint8Array, number] { const [offset, length] = compactFromU8aLim(value); const total = offset + length; - assert(length <= MAX_LENGTH, () => `Bytes length ${length.toString()} exceeds ${MAX_LENGTH}`); - assert(total <= value.length, () => `Bytes: required length less than remainder, expected at least ${total}, found ${value.length}`); + if (length > MAX_LENGTH) { + throw new Error(`Bytes length ${length.toString()} exceeds ${MAX_LENGTH}`); + } else if (total > value.length) { + throw new Error(`Bytes: required length less than remainder, expected at least ${total}, found ${value.length}`); + } return [value.subarray(offset, total), total]; } diff --git a/packages/types-codec/src/extended/U8aFixed.ts b/packages/types-codec/src/extended/U8aFixed.ts index 5248b004b27f..f0353e54be6e 100644 --- a/packages/types-codec/src/extended/U8aFixed.ts +++ b/packages/types-codec/src/extended/U8aFixed.ts @@ -3,7 +3,7 @@ import type { AnyU8a, CodecClass, Registry, U8aBitLength } from '../types'; -import { assert, isU8a, u8aToU8a } from '@polkadot/util'; +import { isU8a, u8aToU8a } from '@polkadot/util'; import { Raw } from '../native/Raw'; @@ -16,7 +16,9 @@ function decodeU8aFixed (value: AnyU8a, bitLength: U8aBitLength): [AnyU8a, numbe return [new Uint8Array(byteLength), 0]; } - assert(isU8a(value) ? u8a.length >= byteLength : u8a.length === byteLength, () => `Expected input with ${byteLength} bytes (${bitLength} bits), found ${u8a.length} bytes`); + if (isU8a(value) ? u8a.length < byteLength : u8a.length !== byteLength) { + throw new Error(`Expected input with ${byteLength} bytes (${bitLength} bits), found ${u8a.length} bytes`); + } return [u8a.subarray(0, byteLength), byteLength]; } diff --git a/packages/types-codec/src/extended/WrapperKeepOpaque.ts b/packages/types-codec/src/extended/WrapperKeepOpaque.ts index 2f05147a3192..df8c284f615b 100644 --- a/packages/types-codec/src/extended/WrapperKeepOpaque.ts +++ b/packages/types-codec/src/extended/WrapperKeepOpaque.ts @@ -3,7 +3,7 @@ import type { AnyJson, AnyU8a, Codec, CodecClass, Inspect, Registry } from '../types'; -import { assertReturn, compactAddLength, compactStripLength, compactToU8a, isHex, isU8a } from '@polkadot/util'; +import { compactAddLength, compactStripLength, compactToU8a, isHex, isU8a } from '@polkadot/util'; import { Raw } from '../native/Raw'; import { typeToConstructor } from '../utils'; @@ -110,6 +110,10 @@ export class WrapperKeepOpaque extends Bytes { * @description Returns the decoded that the WrapperKeepOpaque represents (if available), throws if non-decodable */ public unwrap (): T { - return assertReturn(this.#decoded, () => `${this.#opaqueName}: unwrapping an undecodable value`); + if (!this.#decoded) { + throw new Error(`${this.#opaqueName}: unwrapping an undecodable value`); + } + + return this.#decoded; } } diff --git a/packages/types-codec/src/native/Raw.ts b/packages/types-codec/src/native/Raw.ts index 06defdb7c668..b231867217c8 100644 --- a/packages/types-codec/src/native/Raw.ts +++ b/packages/types-codec/src/native/Raw.ts @@ -4,7 +4,7 @@ import type { HexString } from '@polkadot/util/types'; import type { AnyJson, AnyU8a, Inspect, IU8a, Registry } from '../types'; -import { assert, isAscii, isUndefined, isUtf8, u8aToHex, u8aToString, u8aToU8a } from '@polkadot/util'; +import { isAscii, isUndefined, isUtf8, u8aToHex, u8aToString, u8aToU8a } from '@polkadot/util'; /** * @name Raw @@ -177,7 +177,9 @@ export class Raw extends Uint8Array implements IU8a { * @description Returns the wrapped data as a UTF-8 string */ public toUtf8 (): string { - assert(this.isUtf8, 'The character sequence is not a valid Utf8 string'); + if (!this.isUtf8) { + throw new Error('The character sequence is not a valid Utf8 string'); + } return u8aToString(this); } diff --git a/packages/types-codec/src/native/Set.ts b/packages/types-codec/src/native/Set.ts index 9f02c768948e..de375bceadf9 100644 --- a/packages/types-codec/src/native/Set.ts +++ b/packages/types-codec/src/native/Set.ts @@ -4,7 +4,7 @@ import type { HexString } from '@polkadot/util/types'; import type { CodecClass, Inspect, ISet, IU8a, Registry } from '../types'; -import { assert, BN, bnToBn, bnToU8a, isBn, isNumber, isString, isU8a, isUndefined, objectProperties, stringify, stringPascalCase, u8aToBn, u8aToHex, u8aToU8a } from '@polkadot/util'; +import { BN, bnToBn, bnToU8a, isBn, isNumber, isString, isU8a, isUndefined, objectProperties, stringify, stringPascalCase, u8aToBn, u8aToHex, u8aToU8a } from '@polkadot/util'; import { compareArray } from '../utils'; @@ -27,7 +27,9 @@ function decodeSetArray (setValues: SetValues, values: string[]): string[] { for (let i = 0; i < values.length; i++) { const key = values[i]; - assert(!isUndefined(setValues[key]), () => `Set: Invalid key '${key}' passed to Set, allowed ${Object.keys(setValues).join(', ')}`); + if (isUndefined(setValues[key])) { + throw new Error(`Set: Invalid key '${key}' passed to Set, allowed ${Object.keys(setValues).join(', ')}`); + } result[i] = key; } @@ -51,14 +53,18 @@ function decodeSetNumber (setValues: SetValues, _value: BN | number): string[] { const computed = encodeSet(setValues, result); - assert(bn.eq(computed), () => `Set: Mismatch decoding '${bn.toString()}', computed as '${computed.toString()}' with ${result.join(', ')}`); + if (!bn.eq(computed)) { + throw new Error(`Set: Mismatch decoding '${bn.toString()}', computed as '${computed.toString()}' with ${result.join(', ')}`); + } return result; } /** @internal */ function decodeSet (setValues: SetValues, value: string[] | Set | Uint8Array | BN | number | string = 0, bitLength: number): string[] { - assert(bitLength % 8 === 0, () => `Expected valid bitLength, power of 8, found ${bitLength}`); + if (bitLength % 8 !== 0) { + throw new Error(`Expected valid bitLength, power of 8, found ${bitLength}`); + } const byteLength = bitLength / 8; @@ -161,7 +167,9 @@ export class CodecSet extends Set implements ISet { // ^^^ add = () property done to assign this instance's this, otherwise Set.add creates "some" chaos // we have the isUndefined(this._setValues) in here as well, add is used internally // in the Set constructor (so it is undefined at this point, and should allow) - assert(isUndefined(this.#allowed) || !isUndefined(this.#allowed[key]), () => `Set: Invalid key '${key}' on add`); + if (this.#allowed && isUndefined(this.#allowed[key])) { + throw new Error(`Set: Invalid key '${key}' on add`); + } super.add(key); diff --git a/packages/types-codec/src/native/Struct.ts b/packages/types-codec/src/native/Struct.ts index 99cc67e3ea5f..d09c20624ff1 100644 --- a/packages/types-codec/src/native/Struct.ts +++ b/packages/types-codec/src/native/Struct.ts @@ -4,7 +4,7 @@ import type { HexString } from '@polkadot/util/types'; import type { AnyJson, BareOpts, Codec, CodecClass, Inspect, IStruct, IU8a, Registry } from '../types'; -import { assert, isBoolean, isFunction, isHex, isObject, isU8a, isUndefined, objectProperties, stringCamelCase, stringify, u8aConcatStrict, u8aToHex, u8aToU8a } from '@polkadot/util'; +import { isBoolean, isFunction, isHex, isObject, isU8a, isUndefined, objectProperties, stringCamelCase, stringify, u8aConcatStrict, u8aToHex, u8aToU8a } from '@polkadot/util'; import { compareMap, decodeU8aStruct, mapToTypeMap, typesToMap } from '../utils'; @@ -27,8 +27,11 @@ function decodeStructFromObject (registry: Registry, [Types, keys]: Definition, const typeofArray = Array.isArray(value); const typeofMap = value instanceof Map; - 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 === keys.length, () => `Struct: Unable to map ${stringify(value)} array to object with known keys ${keys.join(', ')}`); + if (!typeofArray && !typeofMap && !isObject(value)) { + throw new Error(`Struct: Cannot decode value ${stringify(value)} (typeof ${typeof value}), expected an input object, map or array`); + } else if (typeofArray && value.length !== keys.length) { + throw new Error(`Struct: Unable to map ${stringify(value)} array to object with known keys ${keys.join(', ')}`); + } const raw = new Array<[string, Codec]>(keys.length); diff --git a/packages/types-codec/src/native/Text.spec.ts b/packages/types-codec/src/native/Text.spec.ts index fb0126a48984..b22b77d1cd04 100644 --- a/packages/types-codec/src/native/Text.spec.ts +++ b/packages/types-codec/src/native/Text.spec.ts @@ -7,6 +7,8 @@ import { TypeRegistry } from '@polkadot/types'; import { Bytes, Raw, Text } from '@polkadot/types-codec'; import { stringToU8a } from '@polkadot/util'; +import { performance } from '../test/performance'; + describe('Text', (): void => { const registry = new TypeRegistry(); @@ -68,7 +70,7 @@ describe('Text', (): void => { expect(test).toHaveLength(2); }); - it('has a snae inspect', (): void => { + it('has a sane inspect', (): void => { expect( new Text(registry, 'abcde').inspect() ).toEqual({ @@ -76,4 +78,6 @@ describe('Text', (): void => { }); }); }); + + performance('Text', 100_000, [[new Uint8Array([6 << 2, 102, 111, 111, 102, 111, 111])]], (v: Uint8Array) => new Text(registry, v)); }); diff --git a/packages/types-codec/src/native/Text.ts b/packages/types-codec/src/native/Text.ts index 608df159b3a1..5b0283d4bdd3 100644 --- a/packages/types-codec/src/native/Text.ts +++ b/packages/types-codec/src/native/Text.ts @@ -4,7 +4,7 @@ import type { HexString } from '@polkadot/util/types'; import type { AnyString, AnyU8a, Inspect, IText, IU8a, Registry } from '../types'; -import { assert, compactAddLength, compactFromU8aLim, compactToU8a, hexToU8a, isHex, isString, isU8a, stringToU8a, u8aToHex, u8aToString } from '@polkadot/util'; +import { compactAddLength, compactFromU8aLim, compactToU8a, hexToU8a, isHex, isString, isU8a, stringToU8a, u8aToHex, u8aToString } from '@polkadot/util'; import { Raw } from './Raw'; @@ -26,8 +26,11 @@ function decodeText (value?: null | AnyString | AnyU8a | { toString: () => strin const [offset, length] = compactFromU8aLim(value); const total = offset + length; - assert(length <= MAX_LENGTH, () => `Text: length ${length.toString()} exceeds ${MAX_LENGTH}`); - assert(total <= value.length, () => `Text: required length less than remainder, expected at least ${total}, found ${value.length}`); + if (length > MAX_LENGTH) { + throw new Error(`Text: length ${length.toString()} exceeds ${MAX_LENGTH}`); + } else if (total > value.length) { + throw new Error(`Text: required length less than remainder, expected at least ${total}, found ${value.length}`); + } return [u8aToString(value.subarray(offset, total)), total]; } else if (isHex(value)) { diff --git a/packages/types-codec/src/test/performance.ts b/packages/types-codec/src/test/performance.ts new file mode 100644 index 000000000000..94d52d2c6495 --- /dev/null +++ b/packages/types-codec/src/test/performance.ts @@ -0,0 +1,77 @@ +// Copyright 2017-2022 @polkadot/types-codec authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +// Shamelessly copied from @polkadot/util/test + +import { formatDecimal, formatNumber } from '@polkadot/util'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type ExecFn = (...params: any[]) => unknown; + +const NUM_PAD = 16; +const PRE_PAD = 32; + +function loop (count: number, inputs: unknown[][], exec: ExecFn): [number, unknown[]] { + const start = Date.now(); + const results = new Array(inputs.length); + + for (let i = 0; i < count; i++) { + const result = exec(...inputs[i % inputs.length]); + + if (i < inputs.length) { + results[i] = result; + } + } + + return [Date.now() - start, results]; +} + +export function formatFixed (value: number): string { + const [a, b] = value.toFixed(2).split('.'); + + return [formatDecimal(a), b].join('.'); +} + +export function formatOps (count: number, time: number): string { + const micro = (time * 1000) / count; + const ops = 1_000_000 / micro; + + return ` +${formatFixed(ops).padStart(NUM_PAD + PRE_PAD + 1)} ops/s +${formatFixed(micro).padStart(NUM_PAD + PRE_PAD + 1)} μs/op`; +} + +export function performance (name: string, count: number, inputs: unknown[][], exec: ExecFn): void { + it(`performance: ${name}`, (): void => { + const [time] = loop(count, inputs, exec); + + console.log(` +performance run for ${name} completed with ${formatNumber(count)} iterations. + +${`${name}:`.padStart(PRE_PAD)} ${time.toString().padStart(NUM_PAD)} ms${formatOps(count, time)} +`); + }); +} + +export function performanceCmp (name: string, [first, second]: [string, string], count: number, inputs: unknown[][], exec: ExecFn): void { + it(`performance: ${name}`, (): void => { + const pa = inputs.map((values) => [...values, false]); + const pb = inputs.map((values) => [...values, true]); + const [ta, ra] = loop(count, pa, exec); + const [tb, rb] = loop(count, pb, exec); + + console.log(` +performance run for ${name} completed with ${formatNumber(count)} iterations. + +${`${first}:`.padStart(PRE_PAD)} ${ta.toString().padStart(NUM_PAD)} ms ${ta < tb ? '(fastest)' : `(slowest, ${(ta / tb).toFixed(2)}x)`}${formatOps(count, ta)} + +${`${second}:`.padStart(PRE_PAD)} ${tb.toString().padStart(NUM_PAD)} ms ${ta > tb ? '(fastest)' : `(slowest, ${(tb / ta).toFixed(2)}x)`}${formatOps(count, tb)} +`); + + const unmatched = ra.filter((r, i) => + JSON.stringify(r) !== JSON.stringify(rb[i]) + ); + + expect(unmatched.length).toEqual(0); + }); +} diff --git a/packages/types-codec/tsconfig.build.json b/packages/types-codec/tsconfig.build.json index eb4a7fbcd17d..81bf587d0ef0 100644 --- a/packages/types-codec/tsconfig.build.json +++ b/packages/types-codec/tsconfig.build.json @@ -9,7 +9,8 @@ "exclude": [ "**/*.data.ts", "**/checkTypes.manual.ts", - "**/*.spec.ts" + "**/*.spec.ts", + "**/test/*" ], "references": [] } diff --git a/packages/types-codec/tsconfig.spec.json b/packages/types-codec/tsconfig.spec.json index 1adfdc3ce3d7..cfcb911cfd48 100644 --- a/packages/types-codec/tsconfig.spec.json +++ b/packages/types-codec/tsconfig.spec.json @@ -11,7 +11,8 @@ "include": [ "**/*.data.ts", "**/checkTypes.manual.ts", - "**/*.spec.ts" + "**/*.spec.ts", + "**/test/*" ], "references": [ { "path": "../types/tsconfig.build.json" }, diff --git a/packages/types-create/src/create/class.ts b/packages/types-create/src/create/class.ts index a588df8a0785..e7384c18c1c7 100644 --- a/packages/types-create/src/create/class.ts +++ b/packages/types-create/src/create/class.ts @@ -5,7 +5,7 @@ import type { Codec, CodecClass, Registry, U8aBitLength, UIntBitLength } from '@ import type { TypeDef } from '../types'; import { BTreeMap, BTreeSet, Bytes, CodecSet, Compact, DoNotConstruct, Enum, HashMap, Int, Null, Option, Range, RangeInclusive, Result, Struct, Tuple, U8aFixed, UInt, Vec, VecFixed, WrapperKeepOpaque, WrapperOpaque } from '@polkadot/types-codec'; -import { assert, isNumber, stringify } from '@polkadot/util'; +import { isNumber, stringify } from '@polkadot/util'; import { TypeDefInfo } from '../types'; import { getTypeDef } from '../util/getTypeDef'; @@ -15,13 +15,17 @@ function getTypeDefType ({ lookupName, type }: TypeDef): string { } function getSubDefArray (value: TypeDef): TypeDef[] { - assert(value.sub && Array.isArray(value.sub), () => `Expected subtype as TypeDef[] in ${stringify(value)}`); + if (!Array.isArray(value.sub)) { + throw new Error(`Expected subtype as TypeDef[] in ${stringify(value)}`); + } return value.sub; } function getSubDef (value: TypeDef): TypeDef { - assert(value.sub && !Array.isArray(value.sub), () => `Expected subtype as TypeDef in ${stringify(value)}`); + if (!value.sub || Array.isArray(value.sub)) { + throw new Error(`Expected subtype as TypeDef in ${stringify(value)}`); + } return value.sub; } @@ -48,7 +52,9 @@ function getTypeClassArray (value: TypeDef): string[] { } function createInt (Clazz: typeof Int | typeof UInt, { displayName, length }: TypeDef): CodecClass { - assert(isNumber(length), () => `Expected bitLength information for ${displayName || Clazz.constructor.name}`); + if (!isNumber(length)) { + throw new Error(`Expected bitLength information for ${displayName || Clazz.constructor.name}`); + } return Clazz.with(length as UIntBitLength, displayName); } @@ -116,7 +122,9 @@ const infoMapping: Record C Null, [TypeDefInfo.Option]: (registry: Registry, value: TypeDef): CodecClass => { - assert(value.sub && !Array.isArray(value.sub), 'Expected type information for Option'); + if (!value.sub || Array.isArray(value.sub)) { + throw new Error('Expected type information for Option'); + } // NOTE This is opt-in (unhandled), not by default // if (value.sub.type === 'bool') { @@ -165,7 +173,9 @@ const infoMapping: Record C createInt(UInt, value), [TypeDefInfo.Vec]: (registry: Registry, { sub }: TypeDef): CodecClass => { - assert(sub && !Array.isArray(sub), 'Expected type information for vector'); + if (!sub || Array.isArray(sub)) { + throw new Error('Expected type information for vector'); + } return ( sub.type === 'u8' @@ -175,7 +185,9 @@ const infoMapping: Record C }, [TypeDefInfo.VecFixed]: (registry: Registry, { displayName, length, sub }: TypeDef): CodecClass => { - assert(sub && isNumber(length) && !Array.isArray(sub), 'Expected length & type information for fixed vector'); + if (!isNumber(length) || !sub || Array.isArray(sub)) { + throw new Error('Expected length & type information for fixed vector'); + } return ( sub.type === 'u8' @@ -195,7 +207,9 @@ export function constructTypeClass (registry: Registry, try { const Type = infoMapping[typeDef.info](registry, typeDef); - assert(Type, 'No class created'); + if (!Type) { + throw new Error('No class created'); + } // don't clobber any existing if (!Type.__fallbackType && typeDef.fallbackType) { diff --git a/packages/types-create/src/create/type.ts b/packages/types-create/src/create/type.ts index dcc01387ea88..036714bf3f44 100644 --- a/packages/types-create/src/create/type.ts +++ b/packages/types-create/src/create/type.ts @@ -5,7 +5,7 @@ import type { Codec, CodecClass, IU8a, Registry } from '@polkadot/types-codec/ty import type { CreateOptions } from '../types'; import { Bytes, Option } from '@polkadot/types-codec'; -import { assert, isHex, isU8a, u8aEq, u8aToHex, u8aToU8a } from '@polkadot/util'; +import { isHex, isU8a, u8aEq, u8aToHex, u8aToU8a } from '@polkadot/util'; import { createClassUnsafe } from './class'; @@ -29,7 +29,9 @@ function checkInstance (created: Codec, matcher: Uint8Array): void { ) ); - assert(isOk, () => `${rawType}:: Decoded input doesn't match input, received ${u8aToHex(matcher, 512)} (${matcher.length} bytes), created ${u8aToHex(u8a, 512)} (${u8a.length} bytes)`); + if (!isOk) { + throw new Error(`${rawType}:: Decoded input doesn't match input, received ${u8aToHex(matcher, 512)} (${matcher.length} bytes), created ${u8aToHex(u8a, 512)} (${u8a.length} bytes)`); + } } function checkPedantic (created: Codec, [value]: unknown[]): void { diff --git a/packages/types-create/src/util/encodeTypes.ts b/packages/types-create/src/util/encodeTypes.ts index deaf609908cd..880ce256a2f5 100644 --- a/packages/types-create/src/util/encodeTypes.ts +++ b/packages/types-create/src/util/encodeTypes.ts @@ -4,7 +4,7 @@ import type { Registry } from '@polkadot/types-codec/types'; import type { TypeDef } from '@polkadot/types-create/types'; -import { assert, isNumber, isUndefined, objectSpread, stringify } from '@polkadot/util'; +import { isNumber, isUndefined, objectSpread, stringify } from '@polkadot/util'; import { TypeDefInfo } from '../types'; @@ -45,7 +45,9 @@ function encodeWithParams (registry: Registry, typeDef: TypeDef, outer: string): function encodeSubTypes (registry: Registry, sub: TypeDef[], asEnum?: boolean, extra?: Record): string { const names = sub.map(({ name }) => name); - assert(names.every((n) => !!n), () => `Subtypes does not have consistent names, ${names.join(', ')}`); + if (!names.every((n) => !!n)) { + throw new Error(`Subtypes does not have consistent names, ${names.join(', ')}`); + } const inner: Record = objectSpread({}, extra); @@ -78,7 +80,9 @@ const encoders: Record st `DoNotConstruct<${lookupName || displayName || (isUndefined(lookupIndex) ? 'Unknown' : registry.createLookupType(lookupIndex))}>`, [TypeDefInfo.Enum]: (registry: Registry, { sub }: TypeDef): string => { - assert(sub && Array.isArray(sub), 'Unable to encode Enum type'); + if (!Array.isArray(sub)) { + throw new Error('Unable to encode Enum type'); + } // c-like enums have all Null entries // TODO We need to take the disciminant into account and auto-add empty entries @@ -118,7 +122,9 @@ const encoders: Record st // eslint-disable-next-line @typescript-eslint/no-unused-vars [TypeDefInfo.Set]: (registry: Registry, { length = 8, sub }: TypeDef): string => { - assert(sub && Array.isArray(sub), 'Unable to encode Set type'); + if (!Array.isArray(sub)) { + throw new Error('Unable to encode Set type'); + } return stringify({ _set: sub.reduce((all, { index, name }, count) => @@ -132,7 +138,9 @@ const encoders: Record st lookupName || type, [TypeDefInfo.Struct]: (registry: Registry, { alias, sub }: TypeDef): string => { - assert(sub && Array.isArray(sub), 'Unable to encode Struct type'); + if (!Array.isArray(sub)) { + throw new Error('Unable to encode Struct type'); + } return encodeSubTypes(registry, sub, false, alias @@ -146,7 +154,9 @@ const encoders: Record st }, [TypeDefInfo.Tuple]: (registry: Registry, { sub }: TypeDef): string => { - assert(sub && Array.isArray(sub), 'Unable to encode Tuple type'); + if (!Array.isArray(sub)) { + throw new Error('Unable to encode Tuple type'); + } return `(${sub.map((type) => encodeTypeDef(registry, type)).join(',')})`; }, @@ -158,7 +168,9 @@ const encoders: Record st encodeWithParams(registry, typeDef, 'Vec'), [TypeDefInfo.VecFixed]: (registry: Registry, { length, sub }: TypeDef): string => { - assert(isNumber(length) && !isUndefined(sub) && !Array.isArray(sub), 'Unable to encode VecFixed type'); + if (!isNumber(length) || !sub || Array.isArray(sub)) { + throw new Error('Unable to encode VecFixed type'); + } return `[${sub.type};${length}]`; }, diff --git a/packages/types-create/src/util/getTypeDef.ts b/packages/types-create/src/util/getTypeDef.ts index 8e34f213af8a..251cd1d4edeb 100644 --- a/packages/types-create/src/util/getTypeDef.ts +++ b/packages/types-create/src/util/getTypeDef.ts @@ -5,7 +5,7 @@ import type { AnyString } from '@polkadot/types-codec/types'; import type { TypeDef } from '@polkadot/types-create/types'; import { sanitize } from '@polkadot/types-codec'; -import { assert, isNumber, isString, objectSpread } from '@polkadot/util'; +import { isNumber, isString, objectSpread } from '@polkadot/util'; import { TypeDefInfo } from '../types'; import { typeSplit } from './typeSplit'; @@ -15,9 +15,6 @@ interface TypeDefOptions { displayName?: string; } -const MAX_NESTED = 64; -const MAX_FIX_LEN = 2048; -const MAX_BIT_LEN = 8192; const KNOWN_INTERNALS = ['_alias', '_fallback']; function getTypeString (typeOrObj: any): string { @@ -30,7 +27,9 @@ function isRustEnum (details: Record | Record): const values = Object.values(details); if (values.some((v) => isNumber(v))) { - assert(values.every((v) => isNumber(v) && v >= 0 && v <= 255), 'Invalid number-indexed enum definition'); + if (!values.every((v) => isNumber(v) && v >= 0 && v <= 255)) { + throw new Error('Invalid number-indexed enum definition'); + } return false; } @@ -145,13 +144,17 @@ function _decodeFixedVec (value: TypeDef, type: string, _: string, count: number } } - assert(index !== -1, () => `${type}: Unable to extract location of ';'`); + if (index === -1) { + throw new Error(`${type}: Unable to extract location of ';'`); + } const vecType = type.substring(1, index); const [strLength, displayName] = type.substring(index + 1, max).split(';'); const length = parseInt(strLength.trim(), 10); - assert(length <= MAX_FIX_LEN, () => `${type}: Only support for [Type; ], where length <= ${MAX_FIX_LEN}`); + if (length > 2048) { + throw new Error(`${type}: Only support for [Type; ], where length <= 2048`); + } value.displayName = displayName; value.length = length; @@ -175,7 +178,9 @@ function _decodeAnyInt (value: TypeDef, type: string, _: string, clazz: 'Int' | const [strLength, displayName] = type.substring(clazz.length + 1, type.length - 1).split(','); const length = parseInt(strLength.trim(), 10); - assert(length <= MAX_BIT_LEN && (length % 8) === 0, () => `${type}: Only support for ${clazz}, where length <= ${MAX_BIT_LEN} and a power of 8, found ${length}`); + if ((length > 8192) || (length % 8)) { + throw new Error(`${type}: Only support for ${clazz}, where length <= 8192 and a power of 8, found ${length}`); + } value.displayName = displayName; value.length = length; @@ -238,7 +243,9 @@ export function getTypeDef (_type: AnyString, { displayName, name }: TypeDefOpti const type = sanitize(_type); const value: TypeDef = { displayName, info: TypeDefInfo.Plain, name, type }; - assert(++count !== MAX_NESTED, 'getTypeDef: Maximum nested limit reached'); + if (++count > 64) { + throw new Error('getTypeDef: Maximum nested limit reached'); + } const nested = nestedExtraction.find((nested) => hasWrapper(type, nested)); diff --git a/packages/types-create/src/util/typeSplit.ts b/packages/types-create/src/util/typeSplit.ts index c7f8d155c504..fdbf8418d7b6 100644 --- a/packages/types-create/src/util/typeSplit.ts +++ b/packages/types-create/src/util/typeSplit.ts @@ -1,8 +1,6 @@ // Copyright 2017-2022 @polkadot/types-create authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { assert } from '@polkadot/util'; - // safely split a string on ', ' while taking care of any nested occurences export function typeSplit (type: string): string[] { const result: string[] = []; @@ -47,7 +45,9 @@ export function typeSplit (type: string): string[] { } // ensure we have all the terminators taken care of - assert(!(c || f || s || t), () => `Invalid definition (missing terminators) found in ${type}`); + if (c || f || s || t) { + throw new Error(`Invalid definition (missing terminators) found in ${type}`); + } // the final leg of the journey result.push(type.substring(start, type.length).trim()); diff --git a/packages/types/src/create/registry.ts b/packages/types/src/create/registry.ts index abb73fc7097e..92fe3a7ffbad 100644 --- a/packages/types/src/create/registry.ts +++ b/packages/types/src/create/registry.ts @@ -9,7 +9,7 @@ import type { CallFunction, CodecHasher, Definitions, DetectCodec, RegisteredTyp import { DoNotConstruct, Json, Raw } from '@polkadot/types-codec'; import { constructTypeClass, createClassUnsafe, createTypeUnsafe } from '@polkadot/types-create'; -import { assert, assertReturn, BN_ZERO, formatBalance, isFunction, isNumber, isString, isU8a, lazyMethod, logger, objectSpread, stringCamelCase, stringify } from '@polkadot/util'; +import { assertReturn, BN_ZERO, formatBalance, isFunction, isNumber, isString, isU8a, lazyMethod, logger, objectSpread, stringCamelCase, stringify } from '@polkadot/util'; import { blake2AsU8a } from '@polkadot/util-crypto'; import { expandExtensionTypes, fallbackExtensions, findUnknownExtensions } from '../extrinsic/signedExtensions'; @@ -410,7 +410,9 @@ export class TypeRegistry implements Registry { public getOrThrow > (name: K, msg?: string): CodecClass { const Clazz = this.get(name); - assert(Clazz, msg || `type ${name} not found`); + if (!Clazz) { + throw new Error(msg || `type ${name} not found`); + } return Clazz as unknown as CodecClass; } @@ -454,8 +456,11 @@ export class TypeRegistry implements Registry { if (isFunction(arg1)) { this.#classes.set(arg1.name, arg1); } else if (isString(arg1)) { - assert(isFunction(arg2), () => `Expected class definition passed to '${arg1}' registration`); - assert(arg1 !== arg2.toString(), () => `Unable to register circular ${arg1} === ${arg1}`); + if (!isFunction(arg2)) { + throw new Error(`Expected class definition passed to '${arg1}' registration`); + } else if (arg1 === arg2.toString()) { + throw new Error(`Unable to register circular ${arg1} === ${arg1}`); + } this.#classes.set(arg1, arg2); } else { @@ -477,7 +482,9 @@ export class TypeRegistry implements Registry { ? type : stringify(type); - assert(name !== def, () => `Unable to register circular ${name} === ${def}`); + if (name === def) { + throw new Error(`Unable to register circular ${name} === ${def}`); + } // we already have this type, remove the classes registered for it if (this.#classes.has(name)) { diff --git a/packages/types/src/metadata/MagicNumber.ts b/packages/types/src/metadata/MagicNumber.ts index 6b4cd386113e..0c2afc27dce0 100644 --- a/packages/types/src/metadata/MagicNumber.ts +++ b/packages/types/src/metadata/MagicNumber.ts @@ -4,7 +4,6 @@ import type { AnyNumber, Registry } from '@polkadot/types-codec/types'; import { U32 } from '@polkadot/types-codec'; -import { assert } from '@polkadot/util'; export const MAGIC_NUMBER = 0x6174656d; // `meta`, reversed for Little Endian encoding @@ -12,8 +11,8 @@ export class MagicNumber extends U32 { constructor (registry: Registry, value?: AnyNumber) { super(registry, value); - if (!this.isEmpty) { - assert(this.eq(MAGIC_NUMBER), () => `MagicNumber mismatch: expected ${registry.createTypeUnsafe('u32', [MAGIC_NUMBER]).toHex()}, found ${this.toHex()}`); + if (!this.isEmpty && !this.eq(MAGIC_NUMBER)) { + throw new Error(`MagicNumber mismatch: expected ${registry.createTypeUnsafe('u32', [MAGIC_NUMBER]).toHex()}, found ${this.toHex()}`); } } } diff --git a/packages/types/src/metadata/MetadataVersioned.ts b/packages/types/src/metadata/MetadataVersioned.ts index 021bf58be545..3f982f88d828 100644 --- a/packages/types/src/metadata/MetadataVersioned.ts +++ b/packages/types/src/metadata/MetadataVersioned.ts @@ -7,7 +7,6 @@ import type { MetadataAll, MetadataLatest, MetadataV9, MetadataV10, MetadataV11, import type { Registry } from '../types'; import { Struct } from '@polkadot/types-codec'; -import { assert } from '@polkadot/util'; import { toV10 } from './v9/toV10'; import { toV11 } from './v10/toV11'; @@ -44,7 +43,9 @@ export class MetadataVersioned extends Struct { } #assertVersion = (version: number): boolean => { - assert(this.version <= version, () => `Cannot convert metadata from version ${this.version} to ${version}`); + if (this.version > version) { + throw new Error(`Cannot convert metadata from version ${this.version} to ${version}`); + } return this.version === version; }; diff --git a/packages/types/src/metadata/PortableRegistry/PortableRegistry.ts b/packages/types/src/metadata/PortableRegistry/PortableRegistry.ts index ed97e55e1d1b..700308042dad 100644 --- a/packages/types/src/metadata/PortableRegistry/PortableRegistry.ts +++ b/packages/types/src/metadata/PortableRegistry/PortableRegistry.ts @@ -9,7 +9,7 @@ import type { SiField, SiLookupTypeId, SiPath, SiType, SiTypeDefArray, SiTypeDef import { sanitize, Struct, u32 } from '@polkadot/types-codec'; import { getTypeDef, TypeDefInfo, withTypeString } from '@polkadot/types-create'; -import { assert, assertUnreachable, isNumber, isString, logger, objectSpread, stringCamelCase, stringify, stringPascalCase } from '@polkadot/util'; +import { assertUnreachable, isNumber, isString, logger, objectSpread, stringCamelCase, stringify, stringPascalCase } from '@polkadot/util'; const l = logger('PortableRegistry'); @@ -540,7 +540,9 @@ export class PortableRegistry extends Struct implements ILookup { // ensure that we have actually initialized it correctly const found = (this.#types || this.types)[this.#getLookupId(lookupId)]; - assert(found, () => `PortableRegistry: Unable to find type with lookupId ${lookupId.toString()}`); + if (!found) { + throw new Error(`PortableRegistry: Unable to find type with lookupId ${lookupId.toString()}`); + } return found.type; } @@ -626,7 +628,9 @@ export class PortableRegistry extends Struct implements ILookup { #getLookupId (lookupId: SiLookupTypeId | string | number): number { if (isString(lookupId)) { - assert(this.registry.isLookupType(lookupId), () => `PortableRegistry: Expected a lookup string type, found ${lookupId}`); + if (!this.registry.isLookupType(lookupId)) { + throw new Error(`PortableRegistry: Expected a lookup string type, found ${lookupId}`); + } return parseInt(lookupId.replace('Lookup', ''), 10); } else if (isNumber(lookupId)) { @@ -665,12 +669,16 @@ export class PortableRegistry extends Struct implements ILookup { return objectSpread({ docs: sanitizeDocs(type.docs), namespace }, typeDef); } - #extractArray (_: number, { len: length, type }: SiTypeDefArray): TypeDef { - assert(!length || length.toNumber() <= 256, 'Only support for [Type; ], where length <= 256'); + #extractArray (_: number, { len, type }: SiTypeDefArray): TypeDef { + const length = len.toNumber(); + + if (length > 2048) { + throw new Error('Only support for [Type; ], where length <= 2048'); + } return withTypeString(this.registry, { info: TypeDefInfo.VecFixed, - length: length.toNumber(), + length, sub: this.#createSiDef(type) }); } @@ -687,8 +695,11 @@ export class PortableRegistry extends Struct implements ILookup { // NOTE: Currently the BitVec type is one-way only, i.e. we only use it to decode, not // re-encode stuff. As such we ignore the msb/lsb identifier given by bitOrderType, or rather // we don't pass it though at all (all displays in LSB) - assert(BITVEC_NS.includes(bitOrder.namespace || ''), () => `Unexpected bitOrder found as ${bitOrder.namespace || ''}`); - assert(bitStore.info === TypeDefInfo.Plain && bitStore.type === 'u8', () => `Only u8 bitStore is currently supported, found ${bitStore.type}`); + if (!BITVEC_NS.includes(bitOrder.namespace || '')) { + throw new Error(`Unexpected bitOrder found as ${bitOrder.namespace || ''}`); + } else if (bitStore.info !== TypeDefInfo.Plain || bitStore.type !== 'u8') { + throw new Error(`Only u8 bitStore is currently supported, found ${bitStore.type}`); + } return { info: TypeDefInfo.Plain, @@ -741,7 +752,9 @@ export class PortableRegistry extends Struct implements ILookup { } #extractCompositeSet (_: number, params: SiTypeParameter[], fields: SiField[]): TypeDef { - assert(params.length === 1 && fields.length === 1, 'Set handling expects param/field as single entries'); + if (params.length !== 1 || fields.length !== 1) { + throw new Error('Set handling expects param/field as single entries'); + } return withTypeString(this.registry, { info: TypeDefInfo.Set, @@ -767,7 +780,9 @@ export class PortableRegistry extends Struct implements ILookup { isTuple = isTuple && name.isNone; } - assert(isTuple || isStruct, 'Invalid fields type detected, expected either Tuple (all unnamed) or Struct (all named)'); + if (!isTuple && !isStruct) { + throw new Error('Invalid fields type detected, expected either Tuple (all unnamed) or Struct (all named)'); + } if (fields.length === 0) { return { diff --git a/packages/types/src/metadata/decorate/extrinsics/createUnchecked.ts b/packages/types/src/metadata/decorate/extrinsics/createUnchecked.ts index ccd082d359c8..a119c0b9a983 100644 --- a/packages/types/src/metadata/decorate/extrinsics/createUnchecked.ts +++ b/packages/types/src/metadata/decorate/extrinsics/createUnchecked.ts @@ -5,7 +5,7 @@ import type { AnyJson, AnyTuple, Registry } from '@polkadot/types-codec/types'; import type { Call, FunctionMetadataLatest } from '../../../interfaces'; import type { CallFunction, IMethod } from '../../../types'; -import { assert, stringCamelCase } from '@polkadot/util'; +import { stringCamelCase } from '@polkadot/util'; function isTx (tx: IMethod, callIndex: Uint8Array): tx is IMethod { return tx.callIndex[0] === callIndex[0] && tx.callIndex[1] === callIndex[1]; @@ -17,7 +17,9 @@ export function createUnchecked (registry: Registry, section: string, callIndex: const funcName = stringCamelCase(callMetadata.name); const extrinsicFn = (...args: unknown[]): Call => { - assert(expectedArgs.length === args.length, () => `Extrinsic ${section}.${funcName} expects ${expectedArgs.length} arguments, got ${args.length}.`); + if (expectedArgs.length !== args.length) { + throw new Error(`Extrinsic ${section}.${funcName} expects ${expectedArgs.length} arguments, got ${args.length}.`); + } return registry.createTypeUnsafe('Call', [{ args, callIndex }, callMetadata]); }; diff --git a/packages/types/src/metadata/decorate/index.ts b/packages/types/src/metadata/decorate/index.ts index c80fbc16f36e..174d405525c3 100644 --- a/packages/types/src/metadata/decorate/index.ts +++ b/packages/types/src/metadata/decorate/index.ts @@ -4,8 +4,6 @@ import type { Registry } from '@polkadot/types-codec/types'; import type { DecoratedMeta } from './types'; -import { assert } from '@polkadot/util'; - import { Metadata } from '../Metadata'; import { decorateConstants } from './constants'; import { decorateErrors } from './errors'; @@ -17,7 +15,9 @@ import { decorateStorage } from './storage'; * Expands the metadata by decoration into consts, query and tx sections */ export function expandMetadata (registry: Registry, metadata: Metadata): DecoratedMeta { - assert(metadata instanceof Metadata, 'You need to pass a valid Metadata instance to Decorated'); + if (!(metadata instanceof Metadata)) { + throw new Error('You need to pass a valid Metadata instance to Decorated'); + } const latest = metadata.asLatest; const version = metadata.version; diff --git a/packages/types/src/metadata/decorate/storage/createFunction.ts b/packages/types/src/metadata/decorate/storage/createFunction.ts index 02a8306ba30c..fee860d0eb13 100644 --- a/packages/types/src/metadata/decorate/storage/createFunction.ts +++ b/packages/types/src/metadata/decorate/storage/createFunction.ts @@ -7,7 +7,7 @@ import type { StorageEntry, StorageEntryIterator } from '../../../primitive/type import type { Registry } from '../../../types'; import { Raw } from '@polkadot/types-codec'; -import { assert, compactAddLength, compactStripLength, isUndefined, objectSpread, stringCamelCase, u8aConcat, u8aToU8a } from '@polkadot/util'; +import { compactAddLength, compactStripLength, isUndefined, objectSpread, stringCamelCase, u8aConcat, u8aToU8a } from '@polkadot/util'; import { xxhashAsU8a } from '@polkadot/util-crypto'; import { StorageKey } from '../../../primitive'; @@ -48,9 +48,11 @@ export const NO_RAW_ARGS: RawArgs = { /** @internal */ function assertArgs ({ method, section }: CreateItemFn, { args, keys }: RawArgs): void { - assert(Array.isArray(args), () => `Call to ${stringCamelCase(section || 'unknown')}.${stringCamelCase(method || 'unknown')} needs ${keys.length} arguments`); - - assert(args.filter(filterDefined).length === keys.length, () => `Call to ${stringCamelCase(section || 'unknown')}.${stringCamelCase(method || 'unknown')} needs ${keys.length} arguments, found [${args.join(', ')}]`); + if (!Array.isArray(args)) { + throw new Error(`Call to ${stringCamelCase(section || 'unknown')}.${stringCamelCase(method || 'unknown')} needs ${keys.length} arguments`); + } else if (args.filter(filterDefined).length !== keys.length) { + throw new Error(`Call to ${stringCamelCase(section || 'unknown')}.${stringCamelCase(method || 'unknown')} needs ${keys.length} arguments, found [${args.join(', ')}]`); + } } /** @internal */ @@ -217,13 +219,9 @@ function extendPrefixedMap (registry: Registry, itemFn: CreateItemFn, storageFn: const { meta: { type }, method, section } = itemFn; storageFn.iterKey = extendHeadMeta(registry, itemFn, storageFn, (...args: unknown[]): Raw => { - assert( - ( - (args.length === 0) || - (type.isMap && args.length < type.asMap.hashers.length) - ), - () => `Iteration ${stringCamelCase(section || 'unknown')}.${stringCamelCase(method || 'unknown')} needs arguments to be at least one less than the full arguments, found [${args.join(', ')}]` - ); + if (args.length && (type.isPlain || (args.length > type.asMap.hashers.length))) { + throw new Error(`Iteration ${stringCamelCase(section || 'unknown')}.${stringCamelCase(method || 'unknown')} needs arguments to be at least one less than the full arguments, found [${args.join(', ')}]`); + } if (args.length) { if (type.isMap) { diff --git a/packages/types/src/metadata/util/testUtil.ts b/packages/types/src/metadata/util/testUtil.ts index f72a7c82778c..86a164b41879 100644 --- a/packages/types/src/metadata/util/testUtil.ts +++ b/packages/types/src/metadata/util/testUtil.ts @@ -7,7 +7,7 @@ import type { Check } from './types'; import fs from 'fs'; import path from 'path'; -import { assert, hexToU8a, stringCamelCase, stringify, u8aToHex } from '@polkadot/util'; +import { hexToU8a, stringCamelCase, stringify, u8aToHex } from '@polkadot/util'; import { TypeRegistry } from '../../create'; import { unwrapStorageSi, unwrapStorageType } from '../../primitive/StorageKey'; @@ -107,7 +107,9 @@ export function defaultValues (registry: Registry, { data, fails = [] }: Check, if (withFallbackCheck) { const [hexType, hexOrig] = [u8aToHex(instance.toU8a()), u8aToHex(fallback.toU8a(true))]; - assert(hexType === hexOrig, () => `Fallback does not match (${((hexOrig.length - 2) / 2) - ((hexType.length - 2) / 2)} bytes missing): ${hexType} !== ${hexOrig}`); + if (hexType !== hexOrig) { + throw new Error(`Fallback does not match (${((hexOrig.length - 2) / 2) - ((hexType.length - 2) / 2)} bytes missing): ${hexType} !== ${hexOrig}`); + } } } catch (error) { const message = `${location}:: ${(error as Error).message}`;