Adjust with current scale-info outputs (#3779)

* Adjust with current scale-info outputs

* Linting

* Exhaustive check for encoders
This commit is contained in:
Jaco
2021-07-17 11:36:19 +02:00
committed by GitHub
parent 731846088c
commit 5ffcba6dd5
7 changed files with 119 additions and 157 deletions
+25 -11
View File
@@ -76,13 +76,17 @@ function createHashMap (value: TypeDef, Clazz: typeof BTreeMap | typeof HashMap)
}
const infoMapping: Record<TypeDefInfo, (registry: Registry, value: TypeDef) => Constructor> = {
[TypeDefInfo.BTreeMap]: (registry: Registry, value: TypeDef): Constructor => createHashMap(value, BTreeMap),
[TypeDefInfo.BTreeMap]: (registry: Registry, value: TypeDef): Constructor =>
createHashMap(value, BTreeMap),
[TypeDefInfo.BTreeSet]: (registry: Registry, value: TypeDef): Constructor => BTreeSet.with(getSubType(value)),
[TypeDefInfo.BTreeSet]: (registry: Registry, value: TypeDef): Constructor =>
BTreeSet.with(getSubType(value)),
[TypeDefInfo.Compact]: (registry: Registry, value: TypeDef): Constructor => Compact.with(getSubType(value)),
[TypeDefInfo.Compact]: (registry: Registry, value: TypeDef): Constructor =>
Compact.with(getSubType(value)),
[TypeDefInfo.DoNotConstruct]: (registry: Registry, value: TypeDef): Constructor => DoNotConstruct.with(value.displayName || value.type),
[TypeDefInfo.DoNotConstruct]: (registry: Registry, value: TypeDef): Constructor =>
DoNotConstruct.with(value.displayName || value.type),
[TypeDefInfo.Enum]: (registry: Registry, value: TypeDef): Constructor => {
const subs = getSubDefArray(value);
@@ -98,9 +102,11 @@ const infoMapping: Record<TypeDefInfo, (registry: Registry, value: TypeDef) => C
);
},
[TypeDefInfo.HashMap]: (registry: Registry, value: TypeDef): Constructor => createHashMap(value, HashMap),
[TypeDefInfo.HashMap]: (registry: Registry, value: TypeDef): Constructor =>
createHashMap(value, HashMap),
[TypeDefInfo.Int]: (registry: Registry, value: TypeDef): Constructor => createInt(value, Int),
[TypeDefInfo.Int]: (registry: Registry, value: TypeDef): Constructor =>
createInt(value, Int),
// We have circular deps between Linkage & Struct
[TypeDefInfo.Linkage]: (registry: Registry, value: TypeDef): Constructor => {
@@ -118,9 +124,11 @@ const infoMapping: Record<TypeDefInfo, (registry: Registry, value: TypeDef) => C
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
[TypeDefInfo.Null]: (registry: Registry, _: TypeDef): Constructor => createClass(registry, 'Null'),
[TypeDefInfo.Null]: (registry: Registry, _: TypeDef): Constructor =>
createClass(registry, 'Null'),
[TypeDefInfo.Option]: (registry: Registry, value: TypeDef): Constructor => Option.with(getSubType(value)),
[TypeDefInfo.Option]: (registry: Registry, value: TypeDef): Constructor =>
Option.with(getSubType(value)),
[TypeDefInfo.Plain]: (registry: Registry, value: TypeDef): Constructor =>
registry.getOrUnknown(value.type),
@@ -145,11 +153,17 @@ const infoMapping: Record<TypeDefInfo, (registry: Registry, value: TypeDef) => C
);
},
[TypeDefInfo.Struct]: (registry: Registry, value: TypeDef): Constructor => Struct.with(getTypeClassMap(value), value.alias),
[TypeDefInfo.Si]: (registry: Registry, value: TypeDef): Constructor =>
getTypeClass(registry, registry.lookup.getTypeDef(value.index as number)),
[TypeDefInfo.Tuple]: (registry: Registry, value: TypeDef): Constructor => Tuple.with(getTypeClassArray(value)),
[TypeDefInfo.Struct]: (registry: Registry, value: TypeDef): Constructor =>
Struct.with(getTypeClassMap(value), value.alias),
[TypeDefInfo.UInt]: (registry: Registry, value: TypeDef): Constructor => createInt(value, UInt),
[TypeDefInfo.Tuple]: (registry: Registry, value: TypeDef): Constructor =>
Tuple.with(getTypeClassArray(value)),
[TypeDefInfo.UInt]: (registry: Registry, value: TypeDef): Constructor =>
createInt(value, UInt),
[TypeDefInfo.Vec]: (registry: Registry, value: TypeDef): Constructor => {
const subType = getSubType(value);
+3 -2
View File
@@ -111,6 +111,7 @@ const encoders: Record<TypeDefInfo, (typeDef: TypeDef) => string> = {
[TypeDefInfo.Plain]: (typeDef: TypeDef): string => typeDef.displayName || typeDef.type,
[TypeDefInfo.Result]: (typeDef: TypeDef): string => encodeWithParams(typeDef, 'Result'),
[TypeDefInfo.Set]: (typeDef: TypeDef): string => typeDef.type,
[TypeDefInfo.Si]: (typeDef: TypeDef): string => typeDef.type,
[TypeDefInfo.Struct]: (typeDef: TypeDef): string => encodeStruct(typeDef),
[TypeDefInfo.Tuple]: (typeDef: TypeDef): string => encodeTuple(typeDef),
[TypeDefInfo.UInt]: (typeDef: TypeDef): string => encodeUInt(typeDef, 'UInt'),
@@ -121,13 +122,13 @@ const encoders: Record<TypeDefInfo, (typeDef: TypeDef) => string> = {
function encodeType (typeDef: TypeDef): string {
const encoder = encoders[typeDef.info];
assert(encoder, () => `Cannot encode type: ${stringify(typeDef)}`);
assert(encoder, () => `Cannot encode type: info=${typeDef.info}, typeDef=${stringify(typeDef)}`);
return encoder(typeDef);
}
export function encodeTypeDef (typeDef: TypeDef): string {
assert(!isUndefined(typeDef.info), () => `Invalid type definition with no instance info, ${stringify(typeDef)}`);
assert(!isUndefined(typeDef.info), () => `Invalid type definition with no instance info, typeDef=${stringify(typeDef)}`);
// In the case of contracts we do have the unfortunate situation where the displayName would
// refer to "Option" when it is an option. For these, string it out, only using when actually
+1
View File
@@ -26,6 +26,7 @@ export enum TypeDefInfo {
Int,
UInt,
DoNotConstruct,
Si,
// anything not fully supported (keep this as the last entry)
Null
}
+44 -73
View File
@@ -4,22 +4,15 @@
import type { Vec } from '../codec/Vec';
import type { PortableType } from '../interfaces/metadata';
import type { SiField, SiLookupTypeId, SiType, SiTypeDefArray, SiTypeDefCompact, SiTypeDefComposite, SiTypeDefSequence, SiTypeDefTuple, SiTypeDefVariant, SiVariant } from '../interfaces/scaleInfo';
import type { Codec, Constructor, Registry, TypeDef } from '../types';
import type { Registry, TypeDef } from '../types';
import { assert, isNumber, isString, stringCamelCase } from '@polkadot/util';
import { Option } from '../codec/Option';
import { Struct } from '../codec/Struct';
import { getTypeClass } from '../create/createClass';
import { withTypeString } from '../create/encodeTypes';
import { getTypeDef } from '../create/getTypeDef';
import { TypeDefInfo } from '../types';
interface CreateOptions {
blockHash?: Uint8Array | string | null;
isOptional?: boolean;
}
const PRIMITIVE_ALIAS: Record<string, string> = {
Char: 'u32', // Rust char is 4-bytes
Str: 'Text'
@@ -30,7 +23,6 @@ const INK_PRIMITIVE_ALWAYS = ['AccountId', 'AccountIndex', 'Address', 'Balance']
const LOOKUP_PREFIX = '__lookup_';
export class GenericPortableRegistry extends Struct {
#classes: Record<number, Constructor> = {};
#typeDefs: Record<number, TypeDef> = {};
constructor (registry: Registry, value?: Uint8Array) {
@@ -60,34 +52,6 @@ export class GenericPortableRegistry extends Struct {
return `${LOOKUP_PREFIX}${lookupId.toString()}`;
}
/**
* @description creates a type from the id
*/
public createType <T extends Codec> (lookupId: SiLookupTypeId | string | number, params: unknown[], { blockHash, isOptional }: CreateOptions = {}): T {
const Clazz = this.getClass<T>(lookupId);
const instance = new (isOptional ? Option.with(Clazz) : Clazz)(this.registry, ...params);
if (blockHash) {
instance.createdAtHash = this.registry.createType('Hash', blockHash);
}
return instance as T;
}
/**
* @description Returns a class from the specified id
*/
public getClass <T extends Codec = Codec> (lookupId: SiLookupTypeId | string | number): Constructor<T> {
const index = this.#getSiIndex(lookupId);
if (!this.#classes[index]) {
// since we may have recursive lookups, fill in empty details as a start
this.#classes[index] = getTypeClass(this.registry, this.getTypeDef(lookupId));
}
return this.#classes[index] as Constructor<T>;
}
/**
* @description Finds a specific type in the registry
*/
@@ -106,22 +70,21 @@ export class GenericPortableRegistry extends Struct {
const index = this.#getSiIndex(lookupId);
if (!this.#typeDefs[index]) {
// since we may have recursive lookups, fill in empty details as a start
this.#typeDefs[index] = { info: TypeDefInfo.DoNotConstruct, type: this.createSiString(index) };
const typeDef = this.#extract(this.getSiType(lookupId), lookupId);
const siType = this.getSiType(lookupId);
Object.entries(this.#extract(siType, lookupId)).forEach(([k, v]): void => {
/* eslint-disable */
// @ts-ignore Yes... these are crappy, however we do go through key/values, so all safe
this.#typeDefs[index][k] = v;
/* eslint-enable */
});
this.#typeDefs[index] = typeDef;
}
return this.#typeDefs[index];
}
#createSiDef (type: SiLookupTypeId): TypeDef {
return {
info: TypeDefInfo.Si,
type: this.createSiString(type)
};
}
#getSiIndex (lookupId: SiLookupTypeId | string | number): number {
if (isString(lookupId)) {
assert(this.isSiString(lookupId), () => `PortableRegistry: Expected a lookup string type, found ${lookupId}`);
@@ -181,10 +144,6 @@ export class GenericPortableRegistry extends Struct {
? { namespace: path.map((s) => s.toString()).join('::') }
: {}
),
...(type.params.length > 0
? { sub: type.params.filter(({ type }) => type.isSome).map(({ type }) => this.getTypeDef(type.unwrap())) }
: {}
),
...typeDef
});
}
@@ -195,14 +154,14 @@ export class GenericPortableRegistry extends Struct {
return {
info: TypeDefInfo.VecFixed,
length: length.toNumber(),
sub: this.getTypeDef(type)
sub: this.#createSiDef(type)
};
}
#extractCompact ({ type }: SiTypeDefCompact): Omit<TypeDef, 'type'> {
return {
info: TypeDefInfo.Compact,
sub: this.getTypeDef(type)
sub: this.#createSiDef(type)
};
}
@@ -219,23 +178,35 @@ export class GenericPortableRegistry extends Struct {
assert(isTuple || isStruct, 'PortableRegistry: Invalid fields type detected, expected either Tuple or Struct');
const sub = fields.map(({ name, type }) => ({
...this.getTypeDef(type),
...(name.isSome
? { name: stringCamelCase(name.unwrap()) }
: {}
)
}));
if (isTuple) {
if (fields.length === 0) {
return {
info: TypeDefInfo.Null
};
} else if (fields.length === 1) {
return {
...this.getTypeDef(fields[0].type),
...(fields[0].name.isSome
? { name: stringCamelCase(fields[0].name.unwrap()) }
: {}
)
};
}
}
return isTuple && sub.length === 1
? sub[0]
: {
// check for tuple first (no fields may be available)
info: isTuple
? TypeDefInfo.Tuple
: TypeDefInfo.Struct,
sub
};
return {
// check for tuple first (no fields may be available)
info: isTuple
? TypeDefInfo.Tuple
: TypeDefInfo.Struct,
sub: fields.map(({ name, type }) => ({
...this.#createSiDef(type),
...(name.isSome
? { name: stringCamelCase(name.unwrap()) }
: {}
)
}))
};
}
#extractPrimitive (type: SiType): TypeDef {
@@ -259,7 +230,7 @@ export class GenericPortableRegistry extends Struct {
return {
info: TypeDefInfo.Vec,
sub: this.getTypeDef(type)
sub: this.#createSiDef(type)
};
}
@@ -268,7 +239,7 @@ export class GenericPortableRegistry extends Struct {
? this.getTypeDef(ids[0])
: {
info: TypeDefInfo.Tuple,
sub: ids.map((type) => this.getTypeDef(type))
sub: ids.map((type) => this.#createSiDef(type))
};
}
@@ -279,14 +250,14 @@ export class GenericPortableRegistry extends Struct {
return specialVariant === 'Option'
? {
info: TypeDefInfo.Option,
sub: this.getTypeDef(params[0].type.unwrap())
sub: this.#createSiDef(params[0].type.unwrap())
}
: specialVariant === 'Result'
? {
info: TypeDefInfo.Result,
sub: params.map((p, index) => ({
name: ['Ok', 'Error'][index],
...this.getTypeDef(p.type.unwrap())
...this.#createSiDef(p.type.unwrap())
}))
}
: {
@@ -385,12 +385,8 @@
"docs": []
}
],
"index": 0,
"docs": [
"System digest item that contains the root of changes trie at given",
"block. It is created for every block iff runtime supports changes",
"trie creation."
]
"index": 2,
"docs": []
},
{
"name": "PreRuntime",
@@ -408,21 +404,8 @@
"docs": []
}
],
"index": 1,
"docs": [
"A pre-runtime digest.",
"",
"These are messages from the consensus engine to the runtime, although",
"the consensus engine can (and should) read them itself to avoid",
"code and state duplication. It is erroneous for a runtime to produce",
"these, but this is not (yet) checked.",
"",
"NOTE: the runtime is not allowed to panic or fail in an `on_initialize`",
"call if an expected `PreRuntime` digest is not present. It is the",
"responsibility of a external block verifier to check this. Runtime API calls",
"will initialize the block without pre-runtime digests, so initialization",
"cannot fail when they are missing."
]
"index": 6,
"docs": []
},
{
"name": "Consensus",
@@ -440,12 +423,8 @@
"docs": []
}
],
"index": 2,
"docs": [
"A message from the runtime to the consensus engine. This should *never*",
"be generated by the native code of any consensus engine, but this is not",
"checked (yet)."
]
"index": 4,
"docs": []
},
{
"name": "Seal",
@@ -463,11 +442,8 @@
"docs": []
}
],
"index": 3,
"docs": [
"Put a Seal on it. This is only used by native code, and is never seen",
"by runtimes."
]
"index": 5,
"docs": []
},
{
"name": "ChangesTrieSignal",
@@ -479,11 +455,8 @@
"docs": []
}
],
"index": 4,
"docs": [
"Digest item that contains signal from changes tries manager to the",
"native code."
]
"index": 7,
"docs": []
},
{
"name": "Other",
@@ -495,18 +468,13 @@
"docs": []
}
],
"index": 5,
"docs": [
"Some other thing. Unsupported and experimental."
]
"index": 0,
"docs": []
}
]
}
},
"docs": [
"Digest item that is able to encode/decode \\'system\\' digest items and",
"provide opaque access to other items."
]
"docs": []
}
},
{
File diff suppressed because one or more lines are too long