Skip to content

Commit

Permalink
Adjust type asserts (#4952)
Browse files Browse the repository at this point in the history
  • Loading branch information
jacogr authored Jun 18, 2022
1 parent 0d2e938 commit b42669f
Show file tree
Hide file tree
Showing 34 changed files with 320 additions and 113 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
21 changes: 14 additions & 7 deletions packages/types-codec/src/abstract/Int.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -27,15 +27,19 @@ function toPercentage (value: BN, divisor: BN): string {
/** @internal */
function decodeAbstractInt (value: Exclude<AnyNumber, Uint8Array>, 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)) {
if (isHex(value, -1, true)) {
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)) {
Expand Down Expand Up @@ -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}`);
}
}

/**
Expand Down
6 changes: 6 additions & 0 deletions packages/types-codec/src/base/Compact.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -88,4 +92,6 @@ describe('Compact', (): void => {
});
});
});

performance('Compact', 75_000, [[new Uint8Array([63 << 2])]], (v: Uint8Array) => new CompactU32(registry, v));
});
18 changes: 13 additions & 5 deletions packages/types-codec/src/base/Enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -45,7 +45,9 @@ function isRustEnum (def: Record<string, string | CodecClass> | Record<string, n
const defValues = Object.values(def);

if (defValues.some((v) => 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;
}
Expand Down Expand Up @@ -97,7 +99,9 @@ function extractDef (registry: Registry, _def: Record<string, string | CodecClas
function createFromValue (registry: Registry, def: TypesDef, index = 0, value?: unknown): Decoded {
const entry = Object.values(def).find((e) => 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,
Expand All @@ -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);
Expand Down Expand Up @@ -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;
});
Expand Down
6 changes: 4 additions & 2 deletions packages/types-codec/src/base/Option.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -247,7 +247,9 @@ export class Option<T extends Codec> implements IOption<T> {
* @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;
}
Expand Down
10 changes: 6 additions & 4 deletions packages/types-codec/src/base/Result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

import type { Codec, CodecClass, IResult, Registry } from '../types';

import { assert } from '@polkadot/util';

import { Enum } from './Enum';

/**
Expand All @@ -31,7 +29,9 @@ export class Result<O extends Codec, E extends Codec> 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;
}
Expand All @@ -47,7 +47,9 @@ export class Result<O extends Codec, E extends Codec> 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;
}
Expand Down
4 changes: 4 additions & 0 deletions packages/types-codec/src/base/UInt.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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));
});
7 changes: 6 additions & 1 deletion packages/types-codec/src/base/Vec.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -190,4 +193,6 @@ describe('Vec', (): void => {
});
});
});

performance('Vec<U32>', 40_000, [[new Uint8Array([3 << 2, 11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34])]], (v: Uint8Array) => new VecU32(registry, v));
});
6 changes: 4 additions & 2 deletions packages/types-codec/src/base/Vec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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];
}
Expand Down
6 changes: 4 additions & 2 deletions packages/types-codec/src/extended/BitVec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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)];
}
Expand Down
9 changes: 6 additions & 3 deletions packages/types-codec/src/extended/Bytes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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];
}
Expand Down
6 changes: 4 additions & 2 deletions packages/types-codec/src/extended/U8aFixed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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];
}
Expand Down
8 changes: 6 additions & 2 deletions packages/types-codec/src/extended/WrapperKeepOpaque.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -110,6 +110,10 @@ export class WrapperKeepOpaque<T extends Codec> 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;
}
}
6 changes: 4 additions & 2 deletions packages/types-codec/src/native/Raw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down
18 changes: 13 additions & 5 deletions packages/types-codec/src/native/Set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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;
}
Expand All @@ -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<string> | 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;

Expand Down Expand Up @@ -161,7 +167,9 @@ export class CodecSet extends Set<string> implements ISet<string> {
// ^^^ 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);

Expand Down
Loading

0 comments on commit b42669f

Please sign in to comment.