From 121bfc79132283d802299cec4a82c50f2a6ecc8f Mon Sep 17 00:00:00 2001 From: Jaco Date: Thu, 13 May 2021 10:08:52 +0300 Subject: [PATCH] VecFixed with hex inputs (& adjust isPedantic) (#3522) * VecFixed with hex inputs (& adjust isPedantic) * Update packages/types/src/codec/VecFixed.spec.ts * Cleanups --- CHANGELOG.md | 4 +- packages/types/src/codec/VecFixed.spec.ts | 6 ++- packages/types/src/codec/VecFixed.ts | 42 ++++++++++---------- packages/types/src/create/createType.spec.ts | 24 ++++++++--- packages/types/src/create/createType.ts | 20 ++++------ packages/types/src/primitive/Text.ts | 2 +- 6 files changed, 56 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3db02e3c4..ddff578748 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,8 +18,10 @@ Changes: - Use `BN_*` constants as non-specified defaults in contract interfaces - Remove all type overrides for node/node-template (API tracks latest) - Contracts ABI now supports metadata version where indexes are 0-based -- Remove support for the old-style V2 contracts from `api-contract` +- Remove support for the old-style V2 contracts from `@polkadot/api-contract` - Adjust historic decoration, return as-at registry via `getHeader`, `getMetadata` & `getRuntimeVersion` +- Adjust `VecFixed` to allow for the handling of hex inputs +- Fix `isPedantic` decoding to correctly handle `Text` ## 4.9.1 May 6, 2021 diff --git a/packages/types/src/codec/VecFixed.spec.ts b/packages/types/src/codec/VecFixed.spec.ts index e161841873..1c6ac8334d 100644 --- a/packages/types/src/codec/VecFixed.spec.ts +++ b/packages/types/src/codec/VecFixed.spec.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { TypeRegistry } from '../create'; -import { Text } from '../primitive'; +import { Text, u16 } from '../primitive'; import { VecFixed } from '.'; describe('VecFixed', (): void => { @@ -17,6 +17,10 @@ describe('VecFixed', (): void => { expect(new VecFixed(registry, Text, 2, new Uint8Array([0x00, 0x04, 0x31])).toHex()).toEqual('0x000431'); }); + it('constructs via hex', (): void => { + expect(new VecFixed(registry, u16, 2, '0x12345678').toHex()).toEqual('0x12345678'); + }); + it('decodes reusing instance inputs', (): void => { const foo = new Text(registry, 'bar'); diff --git a/packages/types/src/codec/VecFixed.ts b/packages/types/src/codec/VecFixed.ts index f6ba5972c5..2443384aa2 100644 --- a/packages/types/src/codec/VecFixed.ts +++ b/packages/types/src/codec/VecFixed.ts @@ -3,12 +3,31 @@ import type { Codec, Constructor, InterfaceTypes, Registry } from '../types'; -import { assert, compactToU8a, isU8a, u8aConcat } from '@polkadot/util'; +import { assert, compactToU8a, isHex, isU8a, u8aConcat } from '@polkadot/util'; import { AbstractArray } from './AbstractArray'; import { typeToConstructor } from './utils'; import { Vec } from './Vec'; +/** @internal */ +function decodeVecFixed (registry: Registry, Type: Constructor, allocLength: number, value: VecFixed | Uint8Array | string | any[]): T[] { + const values = Vec.decodeVec( + registry, + Type, + isU8a(value) || isHex(value) + ? u8aConcat(compactToU8a(allocLength), value) + : value + ); + + while (values.length < allocLength) { + values.push(new Type(registry)); + } + + assert(values.length === allocLength, () => `Expected a length of exactly ${allocLength} entries`); + + return values; +} + /** * @name VecFixed * @description @@ -20,30 +39,11 @@ export class VecFixed extends AbstractArray { constructor (registry: Registry, Type: Constructor | keyof InterfaceTypes, length: number, value: VecFixed | Uint8Array | string | any[] = [] as any[]) { const Clazz = typeToConstructor(registry, Type); - super(registry, ...VecFixed.decodeVecFixed(registry, Clazz, length, value)); + super(registry, ...decodeVecFixed(registry, Clazz, length, value)); this._Type = Clazz; } - /** @internal */ - public static decodeVecFixed (registry: Registry, Type: Constructor, allocLength: number, value: VecFixed | Uint8Array | string | any[]): T[] { - const values = Vec.decodeVec( - registry, - Type, - isU8a(value) - ? u8aConcat(compactToU8a(allocLength), value) - : value - ); - - while (values.length < allocLength) { - values.push(new Type(registry)); - } - - assert(values.length === allocLength, () => `Expected a length of exactly ${allocLength} entries`); - - return values; - } - public static with (Type: Constructor | keyof InterfaceTypes, length: number): Constructor> { return class extends VecFixed { constructor (registry: Registry, value?: any[]) { diff --git a/packages/types/src/create/createType.spec.ts b/packages/types/src/create/createType.spec.ts index 3757fd65b6..7d1c04e534 100644 --- a/packages/types/src/create/createType.spec.ts +++ b/packages/types/src/create/createType.spec.ts @@ -125,20 +125,32 @@ describe('createType', (): void => { describe('isPedantic', (): void => { it('correctly decodes Bytes', (): void => { expect( - createTypeUnsafe(registry, 'Bytes', ['0x12345678'], { isPedantic: true }) - ).toBeDefined(); + createTypeUnsafe(registry, 'Bytes', ['0x12345678'], { isPedantic: true }).toHex() + ).toEqual('0x12345678'); }); it('correctly decodes Bytes (prefixed)', (): void => { expect( - createTypeUnsafe(registry, 'Bytes', [new Uint8Array([4 << 2, 1, 2, 3, 4])], { isPedantic: true }) - ).toBeDefined(); + createTypeUnsafe(registry, 'Bytes', [new Uint8Array([4 << 2, 0x12, 0x34, 0x56, 0x78])], { isPedantic: true }).toHex() + ).toEqual('0x12345678'); }); it('correctly decodes Text', (): void => { expect( - createTypeUnsafe(registry, 'Text', ['0x2070726f7669646572'], { isPedantic: true }) - ).toBeDefined(); + createTypeUnsafe(registry, 'Text', ['0x70726f7669646572'], { isPedantic: true }).toString() + ).toEqual('provider'); + }); + + it('correctly decodes Type', (): void => { + expect( + createTypeUnsafe(registry, 'Type', ['0x5665633c75383e'], { isPedantic: true }).toString() + ).toEqual('Bytes'); // Vec -> Bytes + }); + + it('correctly decodes [u16; 4]', (): void => { + expect( + createTypeUnsafe(registry, '[u16; 4]', ['0x0012002300450067'], { isPedantic: true }).toHex() + ).toEqual('0x0012002300450067'); }); }); diff --git a/packages/types/src/create/createType.ts b/packages/types/src/create/createType.ts index 633b41f79c..c1afec5788 100644 --- a/packages/types/src/create/createType.ts +++ b/packages/types/src/create/createType.ts @@ -16,22 +16,18 @@ interface CreateOptions { // With isPedantic, actually check that the encoding matches that supplied. This // is much slower, but verifies that we have the correct types defined -// eslint-disable-next-line @typescript-eslint/ban-types function checkInstance (value: Uint8Array, created: FromReg): void { const u8a = created.toU8a(); const rawType = created.toRawType(); - // for length-prefixed byte types, just check the length, else the full encoding - const isEqual = ['Bytes', 'Text'].includes(rawType) - ? [ - // input was via hex, we don't have a prefix - (created as Bytes).length, - // input was raw bytes, we do have a prefix - u8a.length - ].includes(value.length) - : u8aEq(value, u8a); - - assert(isEqual, () => `${rawType}:: Decoded input doesn't match input, received ${u8aToHex(value, 512)} (${value.length} bytes), created ${u8aToHex(u8a, 512)} (${u8a.length} bytes)`); + assert( + u8aEq(value, u8a) || ( + // when length-prefixed from hex, just check the actual length + ['Bytes', 'Text', 'Type'].includes(rawType) && + value.length === (created as Bytes).length + ), + () => `${rawType}:: Decoded input doesn't match input, received ${u8aToHex(value, 512)} (${value.length} bytes), created ${u8aToHex(u8a, 512)} (${u8a.length} bytes)` + ); } // Initializes a type with a value. This also checks for fallbacks and in the cases diff --git a/packages/types/src/primitive/Text.ts b/packages/types/src/primitive/Text.ts index abecb459f7..8d87429d5f 100644 --- a/packages/types/src/primitive/Text.ts +++ b/packages/types/src/primitive/Text.ts @@ -109,7 +109,7 @@ export class Text extends String implements Codec { * @description Returns a hex string representation of the value */ public toHex (): string { - // like with Vec, when we are encoding to hex, we don't actually add + // like with Vec, when we are encoding to hex, we don't actually add // the length prefix (it is already implied by the actual string length) return u8aToHex(this.toU8a(true)); }