Metadata v2 (#792)

* metadata v2

* Try to keep additional data form later metadata version
This commit is contained in:
Xiliang Chen
2019-03-23 12:40:55 +01:00
committed by Jaco Greeff
parent 4e88b831ff
commit 143cd2f9bb
14 changed files with 3491 additions and 18 deletions
@@ -11,14 +11,17 @@ import Struct from '../codec/Struct';
import Null from '../primitive/Null';
import MetadataV0 from './v0';
import MetadataV1 from './v1';
import MetadataV2 from './v2';
import v1ToV0 from './v1/toV0';
import v2ToV1 from './v2/toV1';
import MagicNumber from './MagicNumber';
class MetadataEnum extends EnumType<Null | MetadataV1> {
class MetadataEnum extends EnumType<Null | MetadataV1 | MetadataV2> {
constructor (value?: any) {
super({
MetadataV0, // once rolled-out, can replace this with Null
MetadataV1
MetadataV1, // once rolled-out, can replace this with Null
MetadataV2
}, value);
}
@@ -36,6 +39,13 @@ class MetadataEnum extends EnumType<Null | MetadataV1> {
return this.value as MetadataV1;
}
/**
* @description Returns the wrapped values as a V1 object
*/
get asV2 (): MetadataV2 {
return this.value as MetadataV2;
}
/**
* @description The version this metadata represents
*/
@@ -51,6 +61,7 @@ class MetadataEnum extends EnumType<Null | MetadataV1> {
*/
export default class MetadataVersioned extends Struct implements MetadataInterface {
private _convertedV0?: MetadataV0;
private _convertedV1?: MetadataV1;
constructor (value?: any) {
super({
@@ -88,10 +99,10 @@ export default class MetadataVersioned extends Struct implements MetadataInterfa
return this.metadata.asV0;
}
assert(this.metadata.version === 1, `Cannot convert metadata from v${this.metadata.version} to v0`);
let v1 = this.asV1;
if (isUndefined(this._convertedV0)) {
this._convertedV0 = v1ToV0(this.metadata.asV1);
this._convertedV0 = v1ToV0(v1);
}
return this._convertedV0;
@@ -101,9 +112,26 @@ export default class MetadataVersioned extends Struct implements MetadataInterfa
* @description Returns the wrapped values as a V1 object
*/
get asV1 (): MetadataV1 {
assert(this.metadata.version === 1, `Cannot convert metadata from v${this.metadata.version} to v1`);
if (this.metadata.version === 1) {
return this.metadata.asV1;
}
return this.metadata.asV1;
assert(this.metadata.version === 2, `Cannot convert metadata from v${this.metadata.version} to v1`);
if (isUndefined(this._convertedV1)) {
this._convertedV1 = v2ToV1(this.metadata.asV2);
}
return this._convertedV1;
}
/**
* @description Returns the wrapped values as a V1 object
*/
get asV2 (): MetadataV2 {
assert(this.metadata.version === 2, `Cannot convert metadata from v${this.metadata.version} to v2`);
return this.metadata.asV2;
}
getUniqTypes (throwError: boolean): Array<string> {
+1 -1
View File
@@ -2,6 +2,6 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import metadata from './v1/static';
import metadata from './v2/static';
export default metadata;
@@ -16,8 +16,8 @@ describe('Metadata', () => {
const metadata = new Metadata(rpcData);
const str = JSON.stringify(metadata.toJSON());
console.error(str);
console.error(metadata.getUniqTypes(true));
// console.error(str);
// console.error(metadata.getUniqTypes(true));
expect(metadata.events.length).not.toBe(0);
expect(str).toEqual(JSON.stringify(latestParsed));
+13
View File
@@ -136,11 +136,17 @@ export class StorageFunctionModifier extends Enum {
}
export class MapType extends Struct {
private _isLinked = false;
constructor (value?: any) {
super({
key: Type,
value: Type
}, value);
if (value && value.isLinked) {
this._isLinked = true;
}
}
/**
@@ -156,6 +162,13 @@ export class MapType extends Struct {
get value (): Type {
return this.get('value') as Type;
}
/**
* @description Is this an enumerable linked map
*/
get isLinked (): boolean {
return this._isLinked;
}
}
class PlainType extends Type {
@@ -4,21 +4,21 @@
import extrinsicsFromMeta from '@polkadot/extrinsics/fromMetadata';
import createType from '../codec/createType';
import Method from '../primitive/Method';
import createType from '../../codec/createType';
import Method from '../../primitive/Method';
import Metadata from './Metadata';
import latestParsed from './v1/latest.substrate.v1.json';
import Metadata from '../Metadata';
import latestParsed from './latest.substrate.v1.json';
import rpcData from './static';
describe('Metadata', () => {
describe('MetadataV1', () => {
const metadata = new Metadata(rpcData);
it('decodes latest properly', () => {
const str = JSON.stringify(metadata.toJSON());
console.error(str);
console.error(metadata.getUniqTypes(true));
// console.error(str);
// console.error(metadata.getUniqTypes(true));
expect(metadata.version).toBe(1);
expect(metadata.asV1.modules.length).not.toBe(0);
+13
View File
@@ -35,11 +35,17 @@ export class MetadataStorageModifier extends EnumType<Optional | Default> {
}
export class MapType extends Struct {
private _isLinked = false;
constructor (value?: any) {
super({
key: Type,
value: Type
}, value);
if (value && value.isLinked) {
this._isLinked = true;
}
}
/**
@@ -55,6 +61,13 @@ export class MapType extends Struct {
get value (): Type {
return this.get('value') as Type;
}
/**
* @description Is this an enumerable linked map
*/
get isLinked (): boolean {
return this._isLinked;
}
}
export class PlainType extends Type {
+2 -2
View File
@@ -4,7 +4,7 @@
import { OuterDispatchCall, OuterDispatchMetadata } from '../v0/Calls';
import { EventMetadata, OuterEventMetadata, OuterEventMetadataEvent } from '../v0/Events';
import { CallMetadata, FunctionMetadata, RuntimeModuleMetadata, ModuleMetadata, StorageFunctionMetadata, StorageMetadata } from '../v0/Modules';
import { CallMetadata, FunctionMetadata, RuntimeModuleMetadata, ModuleMetadata, StorageFunctionMetadata, StorageMetadata, StorageFunctionType } from '../v0/Modules';
import MetadataV0 from '../v0/Metadata';
import MetadataV1, { MetadataModuleV1 } from '.';
@@ -20,7 +20,7 @@ function storageV0 (mod: MetadataModuleV1): StorageMetadata | null {
new StorageFunctionMetadata({
name,
modifier: modifier.toNumber(),
type,
type: new StorageFunctionType(type),
default: fallback,
documentation: docs
})
@@ -0,0 +1,59 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import extrinsicsFromMeta from '@polkadot/extrinsics/fromMetadata';
import createType from '../../codec/createType';
import Method from '../../primitive/Method';
import Metadata from '../Metadata';
import latestParsed from './latest.substrate.v2.json';
import rpcData from './static';
describe('MetadataV2', () => {
const metadata = new Metadata(rpcData);
it('decodes latest properly', () => {
const str = JSON.stringify(metadata.toJSON());
console.error(str);
console.error(metadata.getUniqTypes(true));
expect(metadata.version).toBe(2);
expect(metadata.asV2.modules.length).not.toBe(0);
expect(str).toEqual(JSON.stringify(latestParsed));
});
it('converts v2 to V1', () => {
const v1 = metadata.asV1;
// console.error(JSON.stringify(v1.toJSON()));
expect(metadata.getUniqTypes(true)).toEqual(v1.getUniqTypes(true));
});
it('converts v2 to V0', () => {
const v1 = metadata.asV0;
// console.error(JSON.stringify(v0.toJSON()));
expect(metadata.getUniqTypes(true)).toEqual(v1.getUniqTypes(true));
});
describe('storage with default values', () => {
Method.injectMethods(extrinsicsFromMeta(metadata.asV0));
metadata.asV2.modules
.filter(({ storage }) => storage.isSome)
.map((mod) =>
mod.storage.unwrap().forEach(({ fallback, name, type }) => {
it(`creates default types for ${mod.prefix}.${name}, type ${type}`, () => {
expect(
() => createType(type.toString(), fallback)
).not.toThrow();
});
})
);
});
});
+130
View File
@@ -0,0 +1,130 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { MetadataInterface } from '../types';
import Option from '../../codec/Option';
import Struct from '../../codec/Struct';
import Vector from '../../codec/Vector';
import Text from '../../primitive/Text';
import { flattenUniq, validateTypes } from '../util';
import { MetadataCall } from '../v1/Calls';
import { MetadataEvent } from '../v1/Events';
import { MetadataStorage } from './Storage';
/**
* @name MetadataModule
* @description
* The definition of a module in the system
*/
export class MetadataModule extends Struct {
constructor (value?: any) {
super({
name: Text,
prefix: Text,
storage: Option.with(Vector.with(MetadataStorage)),
calls: Option.with(Vector.with(MetadataCall)),
events: Option.with(Vector.with(MetadataEvent))
}, value);
}
/**
* @description the module calls
*/
get calls (): Option<Vector<MetadataCall>> {
return this.get('calls') as Option<Vector<MetadataCall>>;
}
/**
* @description the module events
*/
get events (): Option<Vector<MetadataEvent>> {
return this.get('events') as Option<Vector<MetadataEvent>>;
}
/**
* @description the module name
*/
get name (): Text {
return this.get('name') as Text;
}
/**
* @description the module prefix
*/
get prefix (): Text {
return this.get('prefix') as Text;
}
/**
* @description the associated module storage
*/
get storage (): Option<Vector<MetadataStorage>> {
return this.get('storage') as Option<Vector<MetadataStorage>>;
}
}
/**
* @name MetadataV2
* @description
* The runtime metadata as a decoded structure
*/
export default class MetadataV2 extends Struct implements MetadataInterface {
constructor (value?: any) {
super({
modules: Vector.with(MetadataModule)
}, value);
}
/**
* @description The associated modules for this structure
*/
get modules (): Vector<MetadataModule> {
return this.get('modules') as Vector<MetadataModule>;
}
private get callNames () {
return this.modules.map((mod) =>
mod.calls.isNone
? []
: mod.calls.unwrap().map((fn) =>
fn.args.map((arg) => arg.type.toString())
)
);
}
private get eventNames () {
return this.modules.map((mod) =>
mod.events.isNone
? []
: mod.events.unwrap().map((event) =>
event.args.map((arg) => arg.toString())
)
);
}
private get storageNames () {
return this.modules.map((mod) =>
mod.storage.isNone
? []
: mod.storage.unwrap().map((fn) =>
fn.type.isMap
? [fn.type.asMap.key.toString(), fn.type.asMap.value.toString()]
: [fn.type.asType.toString()]
)
);
}
/**
* @description Helper to retrieve a list of all type that are found, sorted and de-deuplicated
*/
getUniqTypes (throwError: boolean): Array<string> {
const types = flattenUniq([this.callNames, this.eventNames, this.storageNames]);
validateTypes(types, throwError);
return types;
}
}
+142
View File
@@ -0,0 +1,142 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import EnumType from '../../codec/EnumType';
import Struct from '../../codec/Struct';
import Vector from '../../codec/Vector';
import Bool from '../../primitive/Bool';
import Bytes from '../../primitive/Bytes';
import Null from '../../primitive/Null';
import Text from '../../primitive/Text';
import Type from '../../primitive/Type';
import { MetadataStorageModifier } from '../v1/Storage';
export class Default extends Null {}
export class Optional extends Null {}
export class MapType extends Struct {
constructor (value?: any) {
super({
key: Type,
value: Type,
isLinked: Bool
}, value);
}
/**
* @description The mapped key as [[Type]]
*/
get key (): Type {
return this.get('key') as Type;
}
/**
* @description The mapped value as [[Type]]
*/
get value (): Type {
return this.get('value') as Type;
}
/**
* @description Is this an enumerable linked map
*/
get isLinked (): boolean {
return (this.get('isLinked') as Bool).valueOf();
}
}
export class PlainType extends Type {
}
export class MetadataStorageType extends EnumType<PlainType | MapType> {
constructor (value?: any, index?: number) {
super({
PlainType,
MapType
}, value, index);
}
/**
* @description `true` if the storage entry is a map
*/
get isMap (): boolean {
return this.toNumber() === 1;
}
/**
* @description The value as a mapped value
*/
get asMap (): MapType {
return this.value as MapType;
}
/**
* @description The value as a [[Type]] value
*/
get asType (): PlainType {
return this.value as PlainType;
}
/**
* @description Returns the string representation of the value
*/
toString (): string {
return this.isMap
? this.asMap.value.toString()
: this.asType.toString();
}
}
/**
* @name MetadataModule
* @description
* The definition of a storage function
*/
export class MetadataStorage extends Struct {
constructor (value?: any) {
super({
name: Text,
modifier: MetadataStorageModifier,
type: MetadataStorageType,
fallback: Bytes,
docs: Vector.with(Text)
}, value);
}
/**
* @description The [[Text]] documentation
*/
get docs (): Vector<Text> {
return this.get('docs') as Vector<Text>;
}
/**
* @description The [[Bytes]] fallback default
*/
get fallback (): Bytes {
return this.get('fallback') as Bytes;
}
/**
* @description The [[MetadataArgument]] for arguments
*/
get modifier (): MetadataStorageModifier {
return this.get('modifier') as MetadataStorageModifier;
}
/**
* @description The call name
*/
get name (): Text {
return this.get('name') as Text;
}
/**
* @description The [[MetadataStorageType]]
*/
get type (): MetadataStorageType {
return this.get('type') as MetadataStorageType;
}
}
+11
View File
@@ -0,0 +1,11 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { default as MetadataV2, MetadataModule as MetadataModuleV2 } from './Metadata';
export default MetadataV2;
export {
MetadataModuleV2
};
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+10
View File
@@ -0,0 +1,10 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import MetadataV1 from '../v1/Metadata';
import MetadataV2 from '.';
export default function toV1 (v2: MetadataV2): MetadataV1 {
return new MetadataV1(v2);
}