From f13462f7cf5849d4904dcc8af1972043c9fbaae5 Mon Sep 17 00:00:00 2001 From: Jaco Date: Tue, 8 Feb 2022 11:34:01 +0100 Subject: [PATCH] Add inspect(...args) to storage (#4545) --- packages/typegen/src/util/formatting.ts | 2 +- packages/types-codec/src/types/codec.ts | 6 ++ packages/types/src/generic/Call.ts | 2 +- packages/types/src/generic/Event.ts | 2 +- .../decorate/storage/createFunction.ts | 81 +++++++++++++++---- packages/types/src/primitive/StorageKey.ts | 2 +- packages/types/src/primitive/types.ts | 3 +- packages/types/src/types/codec.ts | 2 +- 8 files changed, 79 insertions(+), 21 deletions(-) diff --git a/packages/typegen/src/util/formatting.ts b/packages/typegen/src/util/formatting.ts index 7013210ef0..8dc0a47283 100644 --- a/packages/typegen/src/util/formatting.ts +++ b/packages/typegen/src/util/formatting.ts @@ -183,7 +183,7 @@ const formatters: Record, imports: TypeImports, withShortcut: boolean) => { - return formatType(registry, definitions, (registry).lookup.getTypeDef(typeDef.type), imports, withShortcut); + return formatType(registry, definitions, registry.lookup.getTypeDef(typeDef.type), imports, withShortcut); }, [TypeDefInfo.Struct]: (registry: Registry, typeDef: TypeDef, definitions: Record, imports: TypeImports, withShortcut: boolean) => { diff --git a/packages/types-codec/src/types/codec.ts b/packages/types-codec/src/types/codec.ts index 650013a076..79f9b09312 100644 --- a/packages/types-codec/src/types/codec.ts +++ b/packages/types-codec/src/types/codec.ts @@ -8,6 +8,12 @@ import type { Registry } from './registry'; export type BareOpts = boolean | Record; +export interface Inspect { + inner: Inspect[]; + name?: string; + value?: Uint8Array; +} + /** * @name Codec * @description diff --git a/packages/types/src/generic/Call.ts b/packages/types/src/generic/Call.ts index e58456b21b..935ba0cf35 100644 --- a/packages/types/src/generic/Call.ts +++ b/packages/types/src/generic/Call.ts @@ -127,7 +127,7 @@ export class GenericCall extends Struct implement let method = 'unknown.unknown'; try { - const c = (registry).findMetaCall(decoded.callIndex); + const c = registry.findMetaCall(decoded.callIndex); method = `${c.section}.${c.method}`; } catch (error) { diff --git a/packages/types/src/generic/Event.ts b/packages/types/src/generic/Event.ts index 7e41c99bc4..7f1e5dece3 100644 --- a/packages/types/src/generic/Event.ts +++ b/packages/types/src/generic/Event.ts @@ -57,7 +57,7 @@ export class GenericEventData extends Tuple implements IEventData { this.#meta = meta; this.#method = method; this.#section = section; - this.#typeDef = fields.map(({ type }) => (registry).lookup.getTypeDef(type)); + this.#typeDef = fields.map(({ type }) => registry.lookup.getTypeDef(type)); } /** diff --git a/packages/types/src/metadata/decorate/storage/createFunction.ts b/packages/types/src/metadata/decorate/storage/createFunction.ts index 31eae07200..2558bb33b1 100644 --- a/packages/types/src/metadata/decorate/storage/createFunction.ts +++ b/packages/types/src/metadata/decorate/storage/createFunction.ts @@ -1,7 +1,7 @@ // Copyright 2017-2022 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { ICompact, INumber } from '@polkadot/types-codec/types'; +import type { ICompact, Inspect, INumber } from '@polkadot/types-codec/types'; import type { StorageEntryMetadataLatest, StorageHasher } from '../../../interfaces/metadata'; import type { StorageEntry, StorageEntryIterator } from '../../../primitive/types'; import type { Registry } from '../../../types'; @@ -46,7 +46,14 @@ export const NO_RAW_ARGS: RawArgs = { }; /** @internal */ -export function createKeyRaw (registry: Registry, itemFn: CreateItemBase, { args, hashers, keys }: RawArgs): Uint8Array { +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(', ')}]`); +} + +/** @internal */ +export function createKeyRawParts (registry: Registry, itemFn: CreateItemBase, { args, hashers, keys }: RawArgs): [Uint8Array[], Uint8Array[]] { const extra = new Array(keys.length); for (let i = 0; i < keys.length; i++) { @@ -55,9 +62,36 @@ export function createKeyRaw (registry: Registry, itemFn: CreateItemBase, { args ); } + return [ + [ + xxhashAsU8a(itemFn.prefix, 128), + xxhashAsU8a(itemFn.method, 128) + ], + extra + ]; +} + +/** @internal */ +export function createKeyInspect (registry: Registry, itemFn: CreateItemFn, args: RawArgs): Inspect { + assertArgs(itemFn, args); + + const [prefix, extra] = createKeyRawParts(registry, itemFn, args); + const names = ['module', 'method'].concat(...args.args.map((_, i) => `arg${i}`)); + + return { + inner: prefix + .concat(...extra) + .map((value, i) => ({ inner: [], name: names[i], value })), + value: new Uint8Array() + }; +} + +/** @internal */ +export function createKeyRaw (registry: Registry, itemFn: CreateItemBase, args: RawArgs): Uint8Array { + const [prefix, extra] = createKeyRawParts(registry, itemFn, args); + return u8aConcat( - xxhashAsU8a(itemFn.prefix, 128), - xxhashAsU8a(itemFn.method, 128), + ...prefix, ...extra ); } @@ -68,20 +102,36 @@ function filterDefined (a: unknown): boolean { } /** @internal */ -function createKey (registry: Registry, itemFn: CreateItemFn, { args, hashers, keys }: RawArgs): Uint8Array { - const { method, section } = itemFn; +function createKey (registry: Registry, itemFn: CreateItemFn, args: RawArgs): Uint8Array { + assertArgs(itemFn, args); - 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(', ')}]`); - - // as per createKey, always add the length prefix (underlying it is Bytes) + // always add the length prefix (underlying it is Bytes) return compactAddLength( - createKeyRaw(registry, itemFn, { args, hashers, keys }) + createKeyRaw(registry, itemFn, args) ); } /** @internal */ -function createWStorageFn (registry: Registry, itemFn: CreateItemFn, options: CreateItemOptions): (...args: unknown[]) => Uint8Array { +function createStorageInspect (registry: Registry, itemFn: CreateItemFn, options: CreateItemOptions): (...args: unknown[]) => Inspect { + const { meta: { type } } = itemFn; + + return (...args: unknown[]) => { + if (type.isPlain) { + return options.skipHashing + ? { inner: [], name: 'encoded', value: u8aToU8a(options.key) } + : createKeyInspect(registry, itemFn, NO_RAW_ARGS); + } + + const { hashers, key } = type.asMap; + + return hashers.length === 1 + ? createKeyInspect(registry, itemFn, { args, hashers, keys: [key] }) + : createKeyInspect(registry, itemFn, { args, hashers, keys: registry.lookup.getSiType(key).def.asTuple }); + }; +} + +/** @internal */ +function createStorageFn (registry: Registry, itemFn: CreateItemFn, options: CreateItemOptions): (...args: unknown[]) => Uint8Array { const { meta: { type } } = itemFn; let cacheKey: Uint8Array | null = null; @@ -104,15 +154,16 @@ function createWStorageFn (registry: Registry, itemFn: CreateItemFn, options: Cr return hashers.length === 1 ? createKey(registry, itemFn, { args, hashers, keys: [key] }) - : createKey(registry, itemFn, { args, hashers, keys: (registry).lookup.getSiType(key).def.asTuple }); + : createKey(registry, itemFn, { args, hashers, keys: registry.lookup.getSiType(key).def.asTuple }); }; } /** @internal */ function createWithMeta (registry: Registry, itemFn: CreateItemFn, options: CreateItemOptions): StorageEntry { const { meta, method, prefix, section } = itemFn; - const storageFn = createWStorageFn(registry, itemFn, options) as StorageEntry; + const storageFn = createStorageFn(registry, itemFn, options) as StorageEntry; + storageFn.inspect = createStorageInspect(registry, itemFn, options); storageFn.meta = meta; storageFn.method = stringCamelCase(method); storageFn.prefix = prefix; @@ -165,7 +216,7 @@ function extendPrefixedMap (registry: Registry, itemFn: CreateItemFn, storageFn: const { hashers, key } = type.asMap; const keysVec = hashers.length === 1 ? [key] - : (registry).lookup.getSiType(key).def.asTuple; + : registry.lookup.getSiType(key).def.asTuple; return new Raw(registry, createKeyRaw(registry, itemFn, { args, hashers: hashers.slice(0, args.length), keys: keysVec.slice(0, args.length) })); } diff --git a/packages/types/src/primitive/StorageKey.ts b/packages/types/src/primitive/StorageKey.ts index 2bc80dad14..0dba1691e7 100644 --- a/packages/types/src/primitive/StorageKey.ts +++ b/packages/types/src/primitive/StorageKey.ts @@ -118,7 +118,7 @@ function decodeArgsFromMeta (registry: Registry, value: Uin const { hashers, key } = meta.type.asMap; const keys = hashers.length === 1 ? [key] - : (registry).lookup.getSiType(key).def.asTuple; + : registry.lookup.getSiType(key).def.asTuple; return decodeHashers(registry, value, hashers.map((h, i) => [h, keys[i]])); } diff --git a/packages/types/src/primitive/types.ts b/packages/types/src/primitive/types.ts index 5c2f688d01..3f691ac07e 100644 --- a/packages/types/src/primitive/types.ts +++ b/packages/types/src/primitive/types.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { StorageEntryMetadataLatest } from '../interfaces/metadata'; -import type { Codec } from '../types'; +import type { Codec, Inspect } from '../types'; export interface StorageEntryIterator { (...args: unknown[]): Uint8Array & Codec; @@ -12,6 +12,7 @@ export interface StorageEntryIterator { export interface StorageEntry { (...args: unknown[]): Uint8Array; iterKey?: StorageEntryIterator; + inspect: (...args: unknown[]) => Inspect; keyPrefix: (...args: unknown[]) => Uint8Array; meta: StorageEntryMetadataLatest; method: string; diff --git a/packages/types/src/types/codec.ts b/packages/types/src/types/codec.ts index 0a3389cd2c..ce1386caef 100644 --- a/packages/types/src/types/codec.ts +++ b/packages/types/src/types/codec.ts @@ -3,7 +3,7 @@ import type { Codec } from '@polkadot/types-codec/types'; -export type { AnyJson, AnyFunction, AnyNumber, AnyString, AnyTuple, AnyU8a, ArgsDef, BareOpts, Codec, CodecClass, CodecClass as Constructor, CodecTo } from '@polkadot/types-codec/types'; +export type { AnyJson, AnyFunction, AnyNumber, AnyString, AnyTuple, AnyU8a, ArgsDef, BareOpts, Codec, CodecClass, CodecClass as Constructor, CodecTo, Inspect } from '@polkadot/types-codec/types'; // helper to extract keys from an array export type ArrayElementType> = T extends ReadonlyArray