Adjust type encoding (#3805)

* Adjust type encoding

* Adjust

* displayName adjustments
This commit is contained in:
Jaco
2021-07-24 08:32:36 +02:00
committed by GitHub
parent 1fed997080
commit 81a2a2c688
2 changed files with 101 additions and 81 deletions
+59 -56
View File
@@ -20,7 +20,7 @@ export function paramsNotation <T> (outer: string, inner?: T | T[], transform: (
}`;
}
function encodeWithParams (registry: Registry, typeDef: TypeDef, lookupCounter: number, outer: string): string {
function encodeWithParams (registry: Registry, typeDef: TypeDef, outer: string): string {
const { info, sub } = typeDef;
switch (info) {
@@ -32,25 +32,25 @@ function encodeWithParams (registry: Registry, typeDef: TypeDef, lookupCounter:
case TypeDefInfo.Option:
case TypeDefInfo.Result:
case TypeDefInfo.Vec:
return paramsNotation(outer, sub, (param) => encodeTypeDef(registry, param, lookupCounter));
return paramsNotation(outer, sub, (param) => encodeTypeDef(registry, param));
}
throw new Error(`Unable to encode ${stringify(typeDef)} with params`);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function encodeDoNotConstruct (registry: Registry, { displayName }: TypeDef, lookupCounter: number): string {
function encodeDoNotConstruct (registry: Registry, { displayName }: TypeDef): string {
return `DoNotConstruct<${displayName || 'Unknown'}>`;
}
function encodeSubTypes (registry: Registry, sub: TypeDef[], lookupCounter: number, asEnum?: boolean): string {
function encodeSubTypes (registry: Registry, sub: TypeDef[], asEnum?: boolean): string {
const names = sub.map(({ name }) => name);
assert(names.every((n) => !!n), () => `Subtypes does not have consistent names, ${names.join(', ')}`);
const inner = sub.reduce< Record<string, string>>((result, type) => ({
...result,
[type.name as string]: encodeTypeDef(registry, type, lookupCounter)
[type.name as string]: encodeTypeDef(registry, type)
}), {});
return stringify(
@@ -60,7 +60,7 @@ function encodeSubTypes (registry: Registry, sub: TypeDef[], lookupCounter: numb
);
}
function encodeEnum (registry: Registry, typeDef: TypeDef, lookupCounter: number): string {
function encodeEnum (registry: Registry, typeDef: TypeDef): string {
assert(typeDef.sub && Array.isArray(typeDef.sub), 'Unable to encode Enum type');
const sub = typeDef.sub;
@@ -69,29 +69,29 @@ function encodeEnum (registry: Registry, typeDef: TypeDef, lookupCounter: number
// TODO We need to take the disciminant into account and auto-add empty entries
return sub.every(({ type }) => type === 'Null')
? stringify({ _enum: sub.map(({ name }, index) => `${name || `Empty${index}`}`) })
: encodeSubTypes(registry, sub, lookupCounter, true);
: encodeSubTypes(registry, sub, true);
}
function encodeStruct (registry: Registry, typeDef: TypeDef, lookupCounter: number): string {
function encodeStruct (registry: Registry, typeDef: TypeDef): string {
assert(typeDef.sub && Array.isArray(typeDef.sub), 'Unable to encode Struct type');
return encodeSubTypes(registry, typeDef.sub, lookupCounter);
return encodeSubTypes(registry, typeDef.sub);
}
function encodeTuple (registry: Registry, typeDef: TypeDef, lookupCounter: number): string {
function encodeTuple (registry: Registry, typeDef: TypeDef): string {
assert(typeDef.sub && Array.isArray(typeDef.sub), 'Unable to encode Tuple type');
return `(${typeDef.sub.map((type) => encodeTypeDef(registry, type, lookupCounter)).join(',')})`;
return `(${typeDef.sub.map((type) => encodeTypeDef(registry, type)).join(',')})`;
}
function encodeUInt (registry: Registry, { length }: TypeDef, lookupCounter: number, type: 'Int' | 'UInt'): string {
function encodeUInt (registry: Registry, { length }: TypeDef, type: 'Int' | 'UInt'): string {
assert(isNumber(length), 'Unable to encode VecFixed type');
return `${type}<${length}>`;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function encodeVecFixed (registry: Registry, { length, sub }: TypeDef, lookupCounter: number): string {
function encodeVecFixed (registry: Registry, { length, sub }: TypeDef): string {
assert(isNumber(length) && !isUndefined(sub) && !Array.isArray(sub), 'Unable to encode VecFixed type');
return `[${sub.type};${length}]`;
@@ -99,62 +99,60 @@ function encodeVecFixed (registry: Registry, { length, sub }: TypeDef, lookupCou
// We setup a record here to ensure we have comprehensive coverage (any item not covered will result
// in a compile-time error with the missing index)
const encoders: Record<TypeDefInfo, (registry: Registry, typeDef: TypeDef, lookupCounter: number) => string> = {
[TypeDefInfo.BTreeMap]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
encodeWithParams(registry, typeDef, lookupCounter, 'BTreeMap'),
[TypeDefInfo.BTreeSet]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
encodeWithParams(registry, typeDef, lookupCounter, 'BTreeSet'),
[TypeDefInfo.Compact]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
encodeWithParams(registry, typeDef, lookupCounter, 'Compact'),
[TypeDefInfo.DoNotConstruct]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
encodeDoNotConstruct(registry, typeDef, lookupCounter),
[TypeDefInfo.Enum]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
encodeEnum(registry, typeDef, lookupCounter),
[TypeDefInfo.HashMap]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
encodeWithParams(registry, typeDef, lookupCounter, 'HashMap'),
[TypeDefInfo.Int]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
encodeUInt(registry, typeDef, lookupCounter, 'Int'),
[TypeDefInfo.Linkage]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
encodeWithParams(registry, typeDef, lookupCounter, 'Linkage'),
const encoders: Record<TypeDefInfo, (registry: Registry, typeDef: TypeDef) => string> = {
[TypeDefInfo.BTreeMap]: (registry: Registry, typeDef: TypeDef) =>
encodeWithParams(registry, typeDef, 'BTreeMap'),
[TypeDefInfo.BTreeSet]: (registry: Registry, typeDef: TypeDef) =>
encodeWithParams(registry, typeDef, 'BTreeSet'),
[TypeDefInfo.Compact]: (registry: Registry, typeDef: TypeDef) =>
encodeWithParams(registry, typeDef, 'Compact'),
[TypeDefInfo.DoNotConstruct]: (registry: Registry, typeDef: TypeDef) =>
encodeDoNotConstruct(registry, typeDef),
[TypeDefInfo.Enum]: (registry: Registry, typeDef: TypeDef) =>
encodeEnum(registry, typeDef),
[TypeDefInfo.HashMap]: (registry: Registry, typeDef: TypeDef) =>
encodeWithParams(registry, typeDef, 'HashMap'),
[TypeDefInfo.Int]: (registry: Registry, typeDef: TypeDef) =>
encodeUInt(registry, typeDef, 'Int'),
[TypeDefInfo.Linkage]: (registry: Registry, typeDef: TypeDef) =>
encodeWithParams(registry, typeDef, 'Linkage'),
// eslint-disable-next-line @typescript-eslint/no-unused-vars
[TypeDefInfo.Null]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
[TypeDefInfo.Null]: (registry: Registry, typeDef: TypeDef) =>
'Null',
[TypeDefInfo.Option]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
encodeWithParams(registry, typeDef, lookupCounter, 'Option'),
[TypeDefInfo.Option]: (registry: Registry, typeDef: TypeDef) =>
encodeWithParams(registry, typeDef, 'Option'),
// eslint-disable-next-line @typescript-eslint/no-unused-vars
[TypeDefInfo.Plain]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
[TypeDefInfo.Plain]: (registry: Registry, typeDef: TypeDef) =>
typeDef.displayName || typeDef.type,
[TypeDefInfo.Result]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
encodeWithParams(registry, typeDef, lookupCounter, 'Result'),
[TypeDefInfo.Result]: (registry: Registry, typeDef: TypeDef) =>
encodeWithParams(registry, typeDef, 'Result'),
// eslint-disable-next-line @typescript-eslint/no-unused-vars
[TypeDefInfo.Set]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
[TypeDefInfo.Set]: (registry: Registry, typeDef: TypeDef) =>
typeDef.type,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
[TypeDefInfo.Si]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
[TypeDefInfo.Si]: (registry: Registry, typeDef: TypeDef) =>
typeDef.lookupName || typeDef.type,
[TypeDefInfo.Struct]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
encodeStruct(registry, typeDef, lookupCounter),
[TypeDefInfo.Tuple]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
encodeTuple(registry, typeDef, lookupCounter),
[TypeDefInfo.UInt]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
encodeUInt(registry, typeDef, lookupCounter, 'UInt'),
[TypeDefInfo.Vec]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
encodeWithParams(registry, typeDef, lookupCounter, 'Vec'),
[TypeDefInfo.VecFixed]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
encodeVecFixed(registry, typeDef, lookupCounter)
[TypeDefInfo.Struct]: (registry: Registry, typeDef: TypeDef) =>
encodeStruct(registry, typeDef),
[TypeDefInfo.Tuple]: (registry: Registry, typeDef: TypeDef) =>
encodeTuple(registry, typeDef),
[TypeDefInfo.UInt]: (registry: Registry, typeDef: TypeDef) =>
encodeUInt(registry, typeDef, 'UInt'),
[TypeDefInfo.Vec]: (registry: Registry, typeDef: TypeDef) =>
encodeWithParams(registry, typeDef, 'Vec'),
[TypeDefInfo.VecFixed]: (registry: Registry, typeDef: TypeDef) =>
encodeVecFixed(registry, typeDef)
};
function encodeType (registry: Registry, typeDef: TypeDef, lookupCounter: number): string {
function encodeType (registry: Registry, typeDef: TypeDef): string {
const encoder = encoders[typeDef.info];
assert(encoder, () => `Cannot encode type ${stringify(typeDef)}`);
return lookupCounter < 0 && isNumber(typeDef.lookupIndex)
? typeDef.lookupName || registry.createLookupType(typeDef.lookupIndex)
: encoder(registry, typeDef, lookupCounter--);
return typeDef.lookupName || encoder(registry, typeDef);
}
export function encodeTypeDef (registry: Registry, typeDef: TypeDef, lookupCounter = 0): string {
export function encodeTypeDef (registry: Registry, typeDef: TypeDef): string {
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
@@ -164,12 +162,17 @@ export function encodeTypeDef (registry: Registry, typeDef: TypeDef, lookupCount
return typeDef.displayName;
}
return encodeType(registry, typeDef, lookupCounter);
return encodeType(registry, typeDef);
}
export function withTypeString (registry: Registry, typeDef: Omit<TypeDef, 'type'>, lookupCounter = 0): TypeDef {
export function withTypeString (registry: Registry, typeDef: Omit<TypeDef, 'type'>): TypeDef {
const partial = { ...typeDef } as TypeDef;
// for the outer-most type, we ignore the lookupName
delete partial.lookupName;
return {
...typeDef,
type: encodeType(registry, typeDef as TypeDef, lookupCounter)
type: encodeType(registry, partial)
};
}
+42 -25
View File
@@ -23,11 +23,18 @@ const PRIMITIVE_INK = ['AccountId', 'AccountIndex', 'Address', 'Balance'];
// These are types where we have a specific decoding/encoding override + helpers
const PRIMITIVE_SP = [
'BTreeMap',
'BTreeSet',
'HashMap',
'HashSet',
'sp_core::crypto::AccountId32',
'sp_runtime::generic::era::Era',
'sp_runtime::multiaddress::MultiAddress',
'pallet_democracy::vote::Vote',
'pallet_identity::types::Data'
'pallet_identity::types::Data',
'primitive_types::H160',
'primitive_types::H256',
'primitive_types::H512'
];
function removeDuplicateNames (names: (string | null)[]): (string | null)[] {
@@ -53,7 +60,7 @@ function extractNames (types: PortableType[]): Record<number, string> {
const typeName = parts.join('');
return ['BTreeMap', 'Cow', 'Result', 'Option'].includes(typeName) || ['frame_support'].includes(path[0].toString())
return ['Cow', 'Result', 'Option'].includes(typeName) || ['frame_support'].includes(path[0].toString())
? null
: typeName;
})
@@ -126,15 +133,15 @@ export class GenericPortableRegistry extends Struct {
const typeDef = this.getTypeDef(lookupId);
const lookupIndex = lookupId.toNumber();
// Flatten Plain types immediately, otherwise setup for a lookup
return typeDef.info === TypeDefInfo.Plain
? typeDef
: {
// Setup for a lookup on complex types
return [TypeDefInfo.Enum, TypeDefInfo.Struct].includes(typeDef.info)
? {
info: TypeDefInfo.Si,
lookupIndex,
lookupName: this.#names[lookupIndex],
type: this.registry.createLookupType(lookupId)
};
}
: typeDef;
}
#getLookupId (lookupId: SiLookupTypeId | string | number): number {
@@ -190,17 +197,8 @@ export class GenericPortableRegistry extends Struct {
throw new Error(`PortableRegistry: Error extracting ${stringify(type)}: ${(error as Error).message}`);
}
const displayName = path.pop()?.toString();
return {
...(displayName
? { displayName }
: {}
),
...(path.length > 1
? { namespace: path.map((s) => s.toString()).join('::') }
: {}
),
namespace: path.join('::'),
...typeDef
};
}
@@ -226,7 +224,7 @@ export class GenericPortableRegistry extends Struct {
return this.#extractFields(lookupIndex, fields);
}
#extractFields (_: number, fields: SiField[]): TypeDef {
#extractFields (lookupIndex: number, fields: SiField[]): TypeDef {
const [isStruct, isTuple] = fields.reduce(([isAllNamed, isAllUnnamed], { name }) => ([
isAllNamed && name.isSome,
isAllUnnamed && name.isNone
@@ -262,14 +260,24 @@ export class GenericPortableRegistry extends Struct {
info: isTuple // Tuple check first
? TypeDefInfo.Tuple
: TypeDefInfo.Struct,
...(
lookupIndex === -1
? {}
: {
lookupIndex,
lookupName: this.#names[lookupIndex]
}
),
sub
});
}
#extractHistoric (_: number, type: Type): TypeDef {
const inner = getTypeDef(type);
return { ...inner, displayName: type.toString(), isFromSi: true };
return {
...getTypeDef(type),
displayName: type.toString(),
isFromSi: true
};
}
#extractPrimitive (_: number, type: SiType): TypeDef {
@@ -282,22 +290,27 @@ export class GenericPortableRegistry extends Struct {
}
#extractPrimitivePath (_: number, type: SiType): TypeDef {
const displayName = type.path[type.path.length - 1].toString();
return {
displayName,
info: TypeDefInfo.Plain,
type: type.path[type.path.length - 1].toString()
type: displayName
};
}
#extractSequence (_: number, { type }: SiTypeDefSequence): TypeDef {
#extractSequence (lookupIndex: number, { type }: SiTypeDefSequence): TypeDef {
const sub = this.#createSiDef(type);
return withTypeString(this.registry, {
info: TypeDefInfo.Vec,
lookupIndex,
lookupName: this.#names[lookupIndex],
sub
});
}
#extractTuple (_: number, ids: SiTypeDefTuple): TypeDef {
#extractTuple (lookupIndex: number, ids: SiTypeDefTuple): TypeDef {
if (ids.length === 0) {
return {
info: TypeDefInfo.Null,
@@ -311,6 +324,8 @@ export class GenericPortableRegistry extends Struct {
return withTypeString(this.registry, {
info: TypeDefInfo.Tuple,
lookupIndex,
lookupName: this.#names[lookupIndex],
sub
});
}
@@ -360,7 +375,7 @@ export class GenericPortableRegistry extends Struct {
}
sub.push({
...this.#extractFields(lookupIndex, fields),
...this.#extractFields(-1, fields),
index: index.toNumber(),
name: name.toString()
});
@@ -368,6 +383,8 @@ export class GenericPortableRegistry extends Struct {
return withTypeString(this.registry, {
info: TypeDefInfo.Enum,
lookupIndex,
lookupName: this.#names[lookupIndex],
sub
});
}