Complexity cleanups (#1303)

* Complexity cleanups

* Revert spread

* Sig wrapping

* Use InterfaceTypes in rpc definitions

* Types for rpc definitions
This commit is contained in:
Jaco Greeff
2019-08-14 07:37:20 +02:00
committed by GitHub
parent f9148b4c18
commit a9c752a8d4
9 changed files with 170 additions and 194 deletions
+68 -61
View File
@@ -6,13 +6,77 @@ import { ApiInterfaceRx } from '@polkadot/api/types';
import { DerivedContractFees } from '../types';
import BN from 'bn.js';
import { combineLatest, Observable, of } from 'rxjs';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
import { drr } from '../util/drr';
const ZERO = new BN(0);
// Only for 1.0 support. rentByteFee, rentDepositOffset, tombstoneDeposit are not available in substrate 1.0.
// TODO remove this once 1.0 support is dropped
function queryV1 (api: ApiInterfaceRx): Observable<DerivedContractFees> {
return (
api.queryMulti([
api.query.contract.callBaseFee,
api.query.contract.contractFee,
api.query.contract.createBaseFee,
api.query.contract.creationFee,
api.query.contract.transactionBaseFee,
api.query.contract.transactionByteFee,
api.query.contract.transferFee
]) as unknown as Observable<BN[]>
).pipe(
map(([callBaseFee, contractFee, createBaseFee, creationFee, transactionBaseFee, transactionByteFee, transferFee]): DerivedContractFees => ({
callBaseFee,
contractFee,
createBaseFee,
creationFee,
rentByteFee: ZERO,
rentDepositOffset: ZERO,
tombstoneDeposit: ZERO,
transactionBaseFee,
transactionByteFee,
transferFee
} as unknown as DerivedContractFees)),
drr()
);
}
// query via query (early v2, non-current, support to be dropped])
function queryQuery (api: ApiInterfaceRx): Observable<BN[]> {
const queryBase = api.query.contracts || api.query.contract;
return api.queryMulti([
queryBase.callBaseFee,
queryBase.contractFee,
queryBase.createBaseFee,
queryBase.creationFee,
queryBase.rentByteFee,
queryBase.rentDepositOffset,
queryBase.tombstoneDeposit,
queryBase.transactionBaseFee,
queryBase.transactionByteFee,
queryBase.transferFee
]) as unknown as Observable<BN[]>;
}
// query via constants (current applicable path)
function queryConstants (api: ApiInterfaceRx): Observable<BN[]> {
return of([
api.consts.contracts.callBaseFee,
api.consts.contracts.contractFee,
api.consts.contracts.createBaseFee,
api.consts.contracts.creationFee,
api.consts.contracts.rentByteFee,
api.consts.contracts.rentDepositOffset,
api.consts.contracts.tombstoneDeposit,
api.consts.contracts.transactionBaseFee,
api.consts.contracts.transactionByteFee,
api.consts.contracts.transferFee
]) as unknown as Observable<BN[]>;
}
/**
* @name fees
* @returns An object containing the combined results of the queries for
@@ -28,71 +92,14 @@ const ZERO = new BN(0);
*/
export function fees (api: ApiInterfaceRx): () => Observable<DerivedContractFees> {
return (): Observable<DerivedContractFees> => {
const queryBase = api.query.contracts || api.query.contract;
if (api.query.contract && !api.query.contract.rentByteFee) {
// Only for 1.0 support. rentByteFee, rentDepositOffset, tombstoneDeposit are not available in substrate 1.0.
// @TODO remove this once 1.0 support is dropped
return (combineLatest([
of([ZERO, ZERO, ZERO]),
api.queryMulti([
queryBase.callBaseFee,
queryBase.contractFee,
queryBase.createBaseFee,
queryBase.creationFee,
queryBase.transactionBaseFee,
queryBase.transactionByteFee,
queryBase.transferFee
])
]) as unknown as Observable<BN[][]>).pipe(
map(([
[rentByteFee, rentDepositOffset, tombstoneDeposit],
[callBaseFee, contractFee, createBaseFee, creationFee, transactionBaseFee, transactionByteFee, transferFee]
]): DerivedContractFees => ({
callBaseFee,
contractFee,
createBaseFee,
creationFee,
rentByteFee,
rentDepositOffset,
tombstoneDeposit,
transactionBaseFee,
transactionByteFee,
transferFee
} as unknown as DerivedContractFees)),
drr()
);
return queryV1(api);
}
return (
api.consts.contracts
// get values from api.const for substrate versions post spec_version: 101
// https://github.com/paritytech/substrate/pull/2883/files#diff-5e5e1c3aec9ddfde0a9054d062ab3db9R131
? of([
api.consts.contracts.callBaseFee,
api.consts.contracts.contractFee,
api.consts.contracts.createBaseFee,
api.consts.contracts.creationFee,
api.consts.contracts.rentByteFee,
api.consts.contracts.rentDepositOffset,
api.consts.contracts.tombstoneDeposit,
api.consts.contracts.transactionBaseFee,
api.consts.contracts.transactionByteFee,
api.consts.contracts.transferFee
]) as unknown as Observable<BN[]>
// Support versions pre spec_version 101 and get values from storage
: api.queryMulti([
queryBase.callBaseFee,
queryBase.contractFee,
queryBase.createBaseFee,
queryBase.creationFee,
queryBase.rentByteFee,
queryBase.rentDepositOffset,
queryBase.tombstoneDeposit,
queryBase.transactionBaseFee,
queryBase.transactionByteFee,
queryBase.transferFee
]) as unknown as Observable<BN[]>
? queryConstants(api)
: queryQuery(api)
).pipe(
map(
([callBaseFee, contractFee, createBaseFee, creationFee, rentByteFee, rentDepositOffset, tombstoneDeposit, transactionBaseFee, transactionByteFee, transferFee]): DerivedContractFees => ({
-8
View File
@@ -12,7 +12,6 @@ import createParam from './create/param';
const getHeader: RpcMethodOpt = {
description: 'Retrieves the header for a specific block',
params: [
// @ts-ignore srml types
createParam('hash', 'Hash', { isOptional: true })
],
type: 'Header'
@@ -21,7 +20,6 @@ const getHeader: RpcMethodOpt = {
const getBlock: RpcMethodOpt = {
description: 'Get header and body of a relay chain block',
params: [
// @ts-ignore srml types
createParam('hash', 'Hash', { isOptional: true })
],
type: 'SignedBlock'
@@ -30,24 +28,20 @@ const getBlock: RpcMethodOpt = {
const getBlockHash: RpcMethodOpt = {
description: 'Get the block hash for a specific block',
params: [
// @ts-ignore This whould be fixed if we can get types from srml strings
createParam('blockNumber', 'BlockNumber', { isOptional: true })
],
// @ts-ignore srml types
type: 'Hash'
};
const getFinalizedHead: RpcMethodOpt = {
description: 'Get hash of the last finalized block in the canon chain',
params: [],
// @ts-ignore srml types
type: 'Hash'
};
const getRuntimeVersion: RpcMethodOpt = {
description: 'Get the runtime version (alias of state_getRuntimeVersion)',
params: [
// @ts-ignore srml types
createParam('hash', 'Hash', { isOptional: true })
],
type: 'RuntimeVersion'
@@ -99,11 +93,9 @@ export default {
methods: {
getBlock: createMethod(section, 'getBlock', getBlock),
getBlockHash: createMethod(section, 'getBlockHash', getBlockHash),
// TODO US spelling
getFinalizedHead: createMethod(section, 'getFinalizedHead', getFinalizedHead),
getHeader: createMethod(section, 'getHeader', getHeader),
getRuntimeVersion: createMethod(section, 'getRuntimeVersion', getRuntimeVersion),
// TODO US spelling
subscribeFinalizedHeads: createMethod(section, 'subscribeFinalizedHeads', subscribeFinalizedHeads),
subscribeRuntimeVersion: createMethod(section, 'subscribeRuntimeVersion', subscribeRuntimeVersion),
subscribeNewHead: createMethod(section, 'subscribeNewHead', subscribeNewHead)
+2 -3
View File
@@ -2,15 +2,14 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
// FIXME Can be one of the injected types (as type below)
// import { CodecTypes } from '@polkadot/types/classes';
import { InterfaceTypes } from '@polkadot/types/types';
import { RpcParam } from '../types';
interface RpcParamOptions {
isOptional?: boolean;
}
export default function createParam (name: string, type: string, { isOptional = false }: RpcParamOptions = { isOptional: false }): RpcParam {
export default function createParam (name: string, type: InterfaceTypes, { isOptional = false }: RpcParamOptions = { isOptional: false }): RpcParam {
return {
isOptional,
name,
+20 -56
View File
@@ -14,113 +14,82 @@ const call: RpcMethodOpt = {
params: [
createParam('method', 'Text'),
createParam('data', 'Bytes'),
// @ts-ignore srml types
createParam('block', 'Hash', { isOptional: true })
],
type: 'Bytes'
};
const KEY_QUERY_PARAMS = [
createParam('key', 'StorageKey'),
createParam('block', 'Hash', { isOptional: true })
];
const getKeys: RpcMethodOpt = {
description: 'Retrieves the keys with a certain prefix',
params: [
createParam('prefix', 'StorageKey'),
// @ts-ignore srml types
createParam('block', 'Hash', { isOptional: true })
],
// @ts-ignore The Vec<> wrap is fine
params: KEY_QUERY_PARAMS,
type: 'Vec<StorageKey>'
};
const getStorage: RpcMethodOpt = {
description: 'Retrieves the storage for a key',
params: [
createParam('key', 'StorageKey'),
// @ts-ignore srml types
createParam('block', 'Hash', { isOptional: true })
],
params: KEY_QUERY_PARAMS,
type: 'StorageData'
};
const getStorageHash: RpcMethodOpt = {
description: 'Retrieves the storage hash',
params: [
createParam('key', 'StorageKey'),
// @ts-ignore srml types
createParam('block', 'Hash', { isOptional: true })
],
// @ts-ignore srml types
params: KEY_QUERY_PARAMS,
type: 'Hash'
};
const getStorageSize: RpcMethodOpt = {
description: 'Retrieves the storage size',
params: [
createParam('key', 'StorageKey'),
// @ts-ignore srml types
createParam('block', 'Hash', { isOptional: true })
],
params: KEY_QUERY_PARAMS,
type: 'u64'
};
const CHILD_QUERY_PARAMS = [
createParam('childStorageKey', 'StorageKey'),
createParam('key', 'StorageKey'),
createParam('block', 'Hash', { isOptional: true })
];
const getChildKeys: RpcMethodOpt = {
description: 'Retrieves the keys with prefix of a specific child storage',
params: [
createParam('childStorageKey', 'StorageKey'),
createParam('prefix', 'StorageKey'),
// @ts-ignore srml types
createParam('block', 'Hash', { isOptional: true })
],
// @ts-ignore The Vec<> wrap is fine
params: CHILD_QUERY_PARAMS,
type: 'Vec<StorageKey>'
};
const getChildStorage: RpcMethodOpt = {
description: 'Retrieves the child storage for a key',
params: [
createParam('childStorageKey', 'StorageKey'),
createParam('key', 'StorageKey'),
// @ts-ignore srml types
createParam('block', 'Hash', { isOptional: true })
],
params: CHILD_QUERY_PARAMS,
type: 'StorageData'
};
const getChildStorageHash: RpcMethodOpt = {
description: 'Retrieves the child storage hash',
params: [
createParam('childStorageKey', 'StorageKey'),
createParam('key', 'StorageKey'),
// @ts-ignore srml types
createParam('block', 'Hash', { isOptional: true })
],
// @ts-ignore srml types
params: CHILD_QUERY_PARAMS,
type: 'Hash'
};
const getChildStorageSize: RpcMethodOpt = {
description: 'Retrieves the child storage size',
params: [
createParam('childStorageKey', 'StorageKey'),
createParam('key', 'StorageKey'),
// @ts-ignore srml types
createParam('block', 'Hash', { isOptional: true })
],
params: CHILD_QUERY_PARAMS,
type: 'u64'
};
const getMetadata: RpcMethodOpt = {
description: 'Returns the runtime metadata',
params: [
// @ts-ignore srml types
createParam('block', 'Hash', { isOptional: true })
],
// @ts-ignore This is not part of InterfaceTypes
type: 'Metadata'
};
const getRuntimeVersion: RpcMethodOpt = {
description: 'Get the runtime version',
params: [
// @ts-ignore srml types
createParam('hash', 'Hash', { isOptional: true })
],
type: 'RuntimeVersion'
@@ -129,21 +98,16 @@ const getRuntimeVersion: RpcMethodOpt = {
const queryStorage: RpcMethodOpt = {
description: 'Query historical storage entries (by key) starting from a start block',
params: [
// @ts-ignore The Vec<> wrap is fine
createParam('keys', 'Vec<StorageKey>'),
// @ts-ignore srml types
createParam('startBlock', 'Hash'),
// @ts-ignore srml types
createParam('block', 'Hash', { isOptional: true })
],
// @ts-ignore The Vec<> wrap is fine
type: 'Vec<StorageChangeSet>'
};
const subscribeStorage: RpcMethodOpt = {
description: 'Subscribes to storage changes for the provided keys',
params: [
// @ts-ignore The Vec<> wrap is fine
createParam('keys', 'Vec<StorageKey>')
],
pubsub: [
-1
View File
@@ -41,7 +41,6 @@ const health: RpcMethodOpt = {
const peers: RpcMethodOpt = {
description: 'Returns the currently connected peers',
params: [],
// @ts-ignore the Vec<> wrap is fine
type: 'Vec<PeerInfo>'
};
+4 -4
View File
@@ -2,14 +2,14 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
// import { CodecTypes } from '@polkadot/types/classes';
import { InterfaceTypes } from '@polkadot/types/types';
type PubSub = [string, string, string];
export interface RpcParam {
isOptional: boolean;
name: string;
type: string; // CodecTypes;
type: InterfaceTypes;
}
export interface RpcMethodOpt {
@@ -20,7 +20,7 @@ export interface RpcMethodOpt {
isSubscription?: boolean;
params: RpcParam[];
pubsub?: PubSub;
type: string; // CodecTypes;
type: InterfaceTypes;
}
export interface RpcMethod {
@@ -34,7 +34,7 @@ export interface RpcMethod {
params: RpcParam[];
pubsub: PubSub;
section: string;
type: string; // CodecTypes;
type: InterfaceTypes;
}
export interface RpcSection {
@@ -244,6 +244,12 @@ describe('createType', (): void => {
});
});
it('allows creation of a Tuple', (): void => {
expect(
createTypeUnsafe('(Balance,u32)', [[1234, 5678]]).toJSON()
).toEqual([1234, 5678]);
});
it('allows creation of a Enum (simple)', (): void => {
expect(
createTypeUnsafe('{"_enum": ["A", "B", "C"]}', [1]).toJSON()
+69 -58
View File
@@ -99,16 +99,65 @@ function _decodeSet (value: TypeDef, details: Record<string, number>): TypeDef {
return value;
}
// decode a struct, set or enum
function _decodeStruct (type: string, value: TypeDef): TypeDef {
const parsed = JSON.parse(type);
const keys = Object.keys(parsed);
if (keys.length === 1 && keys[0] === '_enum') {
return _decodeEnum(value, parsed[keys[0]]);
} else if (keys.length === 1 && keys[0] === '_set') {
return _decodeSet(value, parsed[keys[0]]);
}
value.info = TypeDefInfo.Struct;
value.sub = keys.map((name): TypeDef =>
// eslint-disable-next-line @typescript-eslint/no-use-before-define
getTypeDef(parsed[name], name)
);
return value;
}
// decode a fixed vector, e.g. [u8;32]
function _decodeFixedVec (type: string, value: TypeDef): TypeDef {
const [vecType, _vecLen] = type.substr(1, type.length - 2).split(';');
const vecLen = parseInt(_vecLen.trim(), 10);
// as a first round, only u8 via u8aFixed, we can add more support
assert(vecLen <= 256, `${type}: Only support for [Type; <length>], where length <= 256`);
value.info = TypeDefInfo.VecFixed;
value.ext = { length: vecLen, type: vecType } as unknown as TypeDefExtVecFixed;
return value;
}
// decode a tuple
function _decodeTuple (subType: string, value: TypeDef): TypeDef {
value.info = TypeDefInfo.Tuple;
value.sub = typeSplit(subType).map((inner): TypeDef =>
// eslint-disable-next-line @typescript-eslint/no-use-before-define
getTypeDef(inner)
);
return value;
}
const simpleExtraction: [string, string, TypeDefInfo][] = [
['Compact<', '>', TypeDefInfo.Compact],
['Option<', '>', TypeDefInfo.Option],
['Vec<', '>', TypeDefInfo.Vec],
['Linkage<', '>', TypeDefInfo.Linkage],
['DoubleMap<', '>', TypeDefInfo.DoubleMap]
];
export function getTypeDef (_type: string, name?: string): TypeDef {
const type = _type.toString().trim();
const value: TypeDef = {
info: TypeDefInfo.Plain,
name,
type
};
const value: TypeDef = { info: TypeDefInfo.Plain, name, type };
let subType = '';
const startingWith = (type: string, start: string, end: string): boolean => {
const _hasWrapper = (type: string, start: string, end: string): boolean => {
if (type.substr(0, start.length) !== start) {
return false;
}
@@ -120,45 +169,18 @@ export function getTypeDef (_type: string, name?: string): TypeDef {
return true;
};
if (startingWith(type, '(', ')')) {
value.info = TypeDefInfo.Tuple;
value.sub = typeSplit(subType).map((inner): TypeDef => getTypeDef(inner));
} else if (startingWith(type, '[', ']')) {
// this handles e.g. [u8;32]
const [vecType, _vecLen] = type.substr(1, type.length - 2).split(';');
const vecLen = parseInt(_vecLen.trim(), 10);
if (_hasWrapper(type, '[', ']')) {
return _decodeFixedVec(type, value);
} else if (_hasWrapper(type, '{', '}')) {
return _decodeStruct(type, value);
} else if (_hasWrapper(type, '(', ')')) {
return _decodeTuple(subType, value);
}
// as a first round, only u8 via u8aFixed, we can add more support
assert(vecLen <= 256, `${type}: Only support for [Type; <length>], where length <= 256`);
const extracted = simpleExtraction.find(([start, end]): boolean => _hasWrapper(type, start, end));
value.info = TypeDefInfo.VecFixed;
value.ext = { length: vecLen, type: vecType } as unknown as TypeDefExtVecFixed;
} else if (startingWith(type, '{', '}')) {
const parsed = JSON.parse(type);
const keys = Object.keys(parsed);
if (keys.length === 1 && keys[0] === '_enum') {
_decodeEnum(value, parsed[keys[0]]);
} else if (keys.length === 1 && keys[0] === '_set') {
_decodeSet(value, parsed[keys[0]]);
} else {
value.info = TypeDefInfo.Struct;
value.sub = keys.map((name): TypeDef => getTypeDef(parsed[name], name));
}
} else if (startingWith(type, 'Compact<', '>')) {
value.info = TypeDefInfo.Compact;
value.sub = getTypeDef(subType);
} else if (startingWith(type, 'Option<', '>')) {
value.info = TypeDefInfo.Option;
value.sub = getTypeDef(subType);
} else if (startingWith(type, 'Vec<', '>')) {
value.info = TypeDefInfo.Vec;
value.sub = getTypeDef(subType);
} else if (startingWith(type, 'Linkage<', '>')) {
value.info = TypeDefInfo.Linkage;
value.sub = getTypeDef(subType);
} else if (startingWith(type, 'DoubleMap<', '>')) {
value.info = TypeDefInfo.DoubleMap;
if (extracted) {
value.info = extracted[2];
value.sub = getTypeDef(subType);
}
@@ -168,7 +190,6 @@ export function getTypeDef (_type: string, name?: string): TypeDef {
export function createClass<T extends Codec = Codec, K extends string = string> (
type: K
): Constructor<FromReg<T, K>> {
// return memoizedCreateClass(type);
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return getTypeClass<FromReg<T, K>>(getTypeDef(type));
}
@@ -319,11 +340,7 @@ function initType<T extends Codec = Codec, K extends string = string> (Type: Con
// An unsafe version of the `createType` below. It's unsafe because the `type`
// argument here can be any string, which, if not parseable, will yield a
// runtime error.
export function createTypeUnsafe<T extends Codec = Codec, K extends string = string> (
type: K,
params: any[] = [],
isPedantic?: boolean
): FromReg<T, K> {
export function createTypeUnsafe<T extends Codec = Codec, K extends string = string> (type: K, params: any[] = [], isPedantic?: boolean): FromReg<T, K> {
try {
return initType(createClass<T, K>(type), params, isPedantic);
} catch (error) {
@@ -332,17 +349,11 @@ export function createTypeUnsafe<T extends Codec = Codec, K extends string = str
}
/**
* Create an instance of a `type` with a given `value`.
* Create an instance of a `type` with a given `params`.
* @param type - A recognizable string representing the type to create an
* instance from
* @param value - The value to instantiate the type with
* @param isPedantic - Double-check that the input's encoding matches the
* output's one. Slower, but ensures that we have a 100% grasp on the actual
* provided value
* @param params - The value to instantiate the type with
*/
export default function createType<K extends InterfaceTypes> (
type: K,
...params: any[]
): InterfaceRegistry[K] {
export default function createType<K extends InterfaceTypes> (type: K, ...params: any[]): InterfaceRegistry[K] {
return createTypeUnsafe<Codec, K>(type, params) as InterfaceRegistry[K];
}
+1 -3
View File
@@ -48,9 +48,7 @@ export default class Vote extends U8aFixed {
} else if (value instanceof Boolean) {
return Vote.decodeVote(value.valueOf());
} else if (isNumber(value)) {
return value < 0
? new Uint8Array([AYE_BITS | DEF_CONV])
: new Uint8Array([NAY_BITS]);
return Vote.decodeVote(value < 0);
} else if (isU8a(value)) {
return value.length
? value.subarray(0, 1)