new EventRecord with topics (#902)
* new EventRecord with topics * Add additional decoding checks (from #612) * Slightly rework storage parsing (future checks) * Split storage result formatting (set vs data) * Allow v3 event parsing * Max Vector length check * assert during Event parsing * Well, HACK-y fallback * Revert "Well, HACK-y fallback" This reverts commit edbf61b99da2753bec3a535126660e71bdcd9861. * Node overrides... * Add implVersion * Register types * Update with polkadot as well * inject compatibility types * EventRecord77 in testing * Conversion with DoubleMap * E2E tested against v3 & v4 * Filter with some * Split archive/remove alex tests * beforeAll -> beforeEach * Remove .only * EventRecordV0 -> 77
This commit is contained in:
@@ -14,6 +14,15 @@ import { ExtError, assert, isFunction, isNull, logger } from '@polkadot/util';
|
||||
|
||||
const l = logger('rpc-core');
|
||||
|
||||
const EMPTY_META = {
|
||||
fallback: undefined,
|
||||
modifier: { isOptional: true },
|
||||
type: {
|
||||
asMap: { isLinked: false },
|
||||
isMap: false
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @name Rpc
|
||||
* @summary The API may use a HTTP or WebSockets provider.
|
||||
@@ -187,64 +196,78 @@ export default class Rpc implements RpcInterface {
|
||||
const base = createType(method.type as string, result);
|
||||
|
||||
if (method.type === 'StorageData') {
|
||||
// single return value (via state.getStorage), decode the value based on the
|
||||
// outputType that we have specified. Fallback to Data on nothing
|
||||
const key = params[0] as StorageKey;
|
||||
const type = key.outputType || 'Data';
|
||||
const Clazz = createClass(type);
|
||||
const meta = key.meta || { fallback: undefined, modifier: { isOptional: true } };
|
||||
|
||||
if (key.meta && key.meta.type.isMap && key.meta.type.asMap.isLinked) {
|
||||
// linked map
|
||||
return new Clazz(base);
|
||||
} else {
|
||||
return meta.modifier.isOptional
|
||||
? new Option(Clazz, isNull(result) ? null : new Clazz(base))
|
||||
: new Clazz(base);
|
||||
}
|
||||
return this.formatStorageData(params[0] as StorageKey, base, isNull(result));
|
||||
} else if (method.type === 'StorageChangeSet') {
|
||||
// multiple return values (via state.storage subscription), decode the values
|
||||
// one at a time, all based on the query types. Three values can be returned -
|
||||
// - Base - There is a valid value, non-empty
|
||||
// - null - The storage key is empty (but in the resultset)
|
||||
// - undefined - The storage value is not in the resultset
|
||||
return (params[0] as Vector<StorageKey>).reduce((result, key: StorageKey) => {
|
||||
// Fallback to Data (i.e. just the encoding) if we don't have a specific type
|
||||
const type = key.outputType || 'Data';
|
||||
const Clazz = createClass(type);
|
||||
return (params[0] as Vector<StorageKey>).reduce((results, key: StorageKey) => {
|
||||
results.push(this.formatStorageSet(key, base as StorageChangeSet));
|
||||
|
||||
// see if we have a result value for this specific key
|
||||
const hexKey = key.toHex();
|
||||
const { value } = (base as StorageChangeSet).changes.find((item) =>
|
||||
item.key.toHex() === hexKey
|
||||
) || { value: null };
|
||||
const meta = key.meta || { fallback: undefined, modifier: { isOptional: true } };
|
||||
|
||||
if (!value) {
|
||||
// if we don't have a value, do not fill in the entry, it will be up to the
|
||||
// caller to sort this out, either ignoring or having a cache for older values
|
||||
result.push(undefined);
|
||||
} else {
|
||||
if (key.meta && key.meta.type.isMap && key.meta.type.asMap.isLinked) {
|
||||
// linked map
|
||||
result.push(new Clazz(value.unwrapOr(null)));
|
||||
} else {
|
||||
if (meta.modifier.isOptional) {
|
||||
// create option either with the existing value, or empty when
|
||||
// there is no value returned
|
||||
result.push(new Option(Clazz, value.isNone ? null : new Clazz(value.unwrap())));
|
||||
} else {
|
||||
// for `null` we fallback to the default value, or create an empty type,
|
||||
// otherwise we return the actual value as retrieved
|
||||
result.push(new Clazz(value.unwrapOr(meta.fallback)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}, [] as Array<Codec | null | undefined>);
|
||||
return results;
|
||||
}, [] as Array<Codec | undefined>);
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
private formatStorageData (key: StorageKey, base: Codec, isNull: boolean): Codec {
|
||||
// single return value (via state.getStorage), decode the value based on the
|
||||
// outputType that we have specified. Fallback to Data on nothing
|
||||
const type = key.outputType || 'Data';
|
||||
const Clazz = createClass(type);
|
||||
const meta = key.meta || EMPTY_META;
|
||||
let created: Codec;
|
||||
|
||||
if (meta.type.isMap && meta.type.asMap.isLinked) {
|
||||
// linked map
|
||||
created = new Clazz(base);
|
||||
} else {
|
||||
created = meta.modifier.isOptional
|
||||
? new Option(Clazz, isNull ? null : new Clazz(base))
|
||||
: new Clazz(base);
|
||||
}
|
||||
|
||||
// TODO Check that the encoded created value matches that what was supplied
|
||||
|
||||
return created;
|
||||
}
|
||||
|
||||
private formatStorageSet (key: StorageKey, base: StorageChangeSet): Codec | undefined {
|
||||
// Fallback to Data (i.e. just the encoding) if we don't have a specific type
|
||||
const type = key.outputType || 'Data';
|
||||
const Clazz = createClass(type);
|
||||
|
||||
// see if we have a result value for this specific key
|
||||
const hexKey = key.toHex();
|
||||
const { value } = base.changes.find((item) =>
|
||||
item.key.toHex() === hexKey
|
||||
) || { value: null };
|
||||
const meta = key.meta || EMPTY_META;
|
||||
|
||||
// if we don't have a value, do not fill in the entry, it will be up to the
|
||||
// caller to sort this out, either ignoring or having a cache for older values
|
||||
let created: Codec | undefined;
|
||||
|
||||
if (value) {
|
||||
if (meta.type.isMap && meta.type.asMap.isLinked) {
|
||||
// linked map
|
||||
created = new Clazz(value.unwrapOr(null));
|
||||
} else if (meta.modifier.isOptional) {
|
||||
// create option either with the existing value, or empty when
|
||||
// there is no value returned
|
||||
created = new Option(Clazz, value.isNone ? null : new Clazz(value.unwrap()));
|
||||
} else {
|
||||
// for `null` we fallback to the default value, or create an empty type,
|
||||
// otherwise we return the actual value as retrieved
|
||||
created = new Clazz(value.unwrapOr(meta.fallback));
|
||||
}
|
||||
|
||||
// TODO Check that the encoded created value matches that what was supplied
|
||||
}
|
||||
|
||||
return created;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user