Mortal -> Extrinsic, expand for decoding start (#212)

* Mortal -> Extrinsic, expand for decoding start

* Method, Section -> Rpc{Method, Section}

* Only expose callIndex

* CallIndex -> MethodIndex in spec
This commit is contained in:
Jaco Greeff
2018-10-05 12:29:06 +02:00
committed by GitHub
parent ff8aeb02f7
commit 82dbc93f79
28 changed files with 312 additions and 333 deletions
+2 -2
View File
@@ -3,7 +3,7 @@
// of the ISC license. See the LICENSE file for details.
import { RxApiInterface, RxApiInterface$Method, RxApiInterface$Section } from '@polkadot/api-rx/types';
import { Method } from '@polkadot/jsonrpc/types';
import { RpcMethod } from '@polkadot/jsonrpc/types';
import { EMPTY, Observable, combineLatest, from } from 'rxjs';
import { defaultIfEmpty, map } from 'rxjs/operators';
@@ -83,7 +83,7 @@ export default class ApiBase {
return this._api.isConnected();
}
rawCall = <T> ({ method, section }: Method, ...params: Array<any>): Observable<T> => {
rawCall = <T> ({ method, section }: RpcMethod, ...params: Array<any>): Observable<T> => {
const apiSection = this._api[section as keyof RxApiInterface] as RxApiInterface$Section;
assert(apiSection, `Unable to find 'api.${section}'`);
+2 -2
View File
@@ -4,7 +4,7 @@
import { Observable } from 'rxjs';
import { defaultIfEmpty, map } from 'rxjs/operators';
import { BlockNumber, Hash, Header, SignedBlock, UncheckedMortalExtrinsic } from '@polkadot/types/index';
import { BlockNumber, Hash, Header, SignedBlock, Extrinsic } from '@polkadot/types/index';
import ApiQueries from './Queries';
@@ -44,7 +44,7 @@ export default class ApiCalls extends ApiQueries {
return this._api.system.version();
}
submitExtrinsic = (extrinsic: UncheckedMortalExtrinsic): Observable<Hash | undefined> => {
submitExtrinsic = (extrinsic: Extrinsic): Observable<Hash | undefined> => {
return this._api.author.submitExtrinsic(extrinsic);
}
}
+7 -7
View File
@@ -3,7 +3,7 @@
// of the ISC license. See the LICENSE file for details.
import { ProviderInterface, ProviderInterface$Callback } from '@polkadot/api-provider/types';
import { Section, Method } from '@polkadot/jsonrpc/types';
import { RpcSection, RpcMethod } from '@polkadot/jsonrpc/types';
import { ApiInterface, ApiInterface$Section, ApiInterface$Section$Method } from './types';
import { Base, Vector, createType } from '@polkadot/types/codec';
@@ -62,7 +62,7 @@ export default class Api implements ApiInterface {
* Api.signature({ name: 'test_method', params: [ { name: 'dest', type: 'Address' } ], type: 'Address' }); // => test_method (dest: Address): Address
* </CODE></PRE>
*/
static signature ({ method, params, type }: Method): string {
static signature ({ method, params, type }: RpcMethod): string {
const inputs = params.map(({ name, type }) =>
`${name}: ${type}`
).join(', ');
@@ -70,7 +70,7 @@ export default class Api implements ApiInterface {
return `${method} (${inputs}): ${type}`;
}
private createInterface ({ methods, section }: Section): ApiInterface$Section {
private createInterface ({ methods, section }: RpcSection): ApiInterface$Section {
return Object
.keys(methods)
.reduce((exposed, method) => {
@@ -85,7 +85,7 @@ export default class Api implements ApiInterface {
}, {} as ApiInterface$Section);
}
private createMethodSend (rpcName: string, method: Method): ApiInterface$Section$Method {
private createMethodSend (rpcName: string, method: RpcMethod): ApiInterface$Section$Method {
const call = async (...values: Array<any>): Promise<any> => {
// TODO Warn on deprecated methods
try {
@@ -106,7 +106,7 @@ export default class Api implements ApiInterface {
return call as ApiInterface$Section$Method;
}
private createMethodSubscribe (rpcName: string, method: Method): ApiInterface$Section$Method {
private createMethodSubscribe (rpcName: string, method: RpcMethod): ApiInterface$Section$Method {
const unsubscribe = (subscriptionId: any): Promise<any> =>
this._provider.unsubscribe(rpcName, method.subscribe[1], subscriptionId);
const _call = async (...values: Array<any>): Promise<any> => {
@@ -138,7 +138,7 @@ export default class Api implements ApiInterface {
return call;
}
private formatInputs (method: Method, inputs: Array<any>): Array<Base> {
private formatInputs (method: RpcMethod, inputs: Array<any>): Array<Base> {
assert(method.params.length === inputs.length, `Expected ${method.params.length} parameters, ${inputs.length} found instead`);
return method.params.map(({ type }, index) =>
@@ -146,7 +146,7 @@ export default class Api implements ApiInterface {
);
}
private formatOutput (method: Method, params: Array<Base>, result?: any): Base {
private formatOutput (method: RpcMethod, params: Array<Base>, result?: any): Base {
const base = createType(method.type as string).fromJSON(result);
if (method.type === 'StorageData') {
+2 -2
View File
@@ -2,11 +2,11 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { UncheckedMortalExtrinsic } from '@polkadot/types/index';
import { Extrinsic } from '@polkadot/types/index';
import { FunctionMetadata } from '@polkadot/types/Metadata';
export interface ExtrinsicFunction {
(...args: any[]): UncheckedMortalExtrinsic,
(...args: any[]): Extrinsic,
callIndex: Uint8Array,
meta: FunctionMetadata,
method: string;
@@ -5,7 +5,7 @@
import { ExtrinsicFunction } from '../types';
import { FunctionMetadata } from '@polkadot/types/Metadata';
import { Call, UncheckedMortalExtrinsic } from '@polkadot/types/index';
import { Extrinsic, Method } from '@polkadot/types/index';
/**
* From the metadata of a function in the module's storage, generate the function
@@ -28,14 +28,14 @@ export default function createDescriptor (
arg.type.toString() !== 'Origin'
);
extrinsicFn = (...args: any[]): UncheckedMortalExtrinsic => {
extrinsicFn = (...args: any[]): Extrinsic => {
if (expectedArgs.length.valueOf() !== args.length) {
throw new Error(`Extrinsic ${section}.${method} expects ${expectedArgs.length.valueOf()} arguments, got ${args.length}.`);
}
const call = new Call(callIndex, meta, args);
return new UncheckedMortalExtrinsic(call);
return new Extrinsic({
method: new Method(callIndex, meta, args)
});
};
extrinsicFn.callIndex = callIndex;
+4 -4
View File
@@ -2,18 +2,18 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { MethodOpt, Section } from './types';
import { RpcMethodOpt, RpcSection } from './types';
import createMethod from './create/method';
import createParam from './create/param';
const pendingExtrinsics: MethodOpt = {
const pendingExtrinsics: RpcMethodOpt = {
description: 'Returns all pending extrinsics, potentially grouped by sender',
params: [],
type: 'PendingExtrinsics'
};
const submitExtrinsic: MethodOpt = {
const submitExtrinsic: RpcMethodOpt = {
isSigned: true,
description: 'Submit a fully formatted extrinsic for block inclusion',
params: [
@@ -36,4 +36,4 @@ export default {
pendingExtrinsics: createMethod(section, 'pendingExtrinsics', pendingExtrinsics),
submitExtrinsic: createMethod(section, 'submitExtrinsic', submitExtrinsic)
}
} as Section;
} as RpcSection;
+9 -9
View File
@@ -2,12 +2,12 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { MethodOpt, Section } from './types';
import { RpcMethodOpt, RpcSection } from './types';
import createMethod from './create/method';
import createParam from './create/param';
const getBlock: MethodOpt = {
const getBlock: RpcMethodOpt = {
description: 'Get header and body of a relay chain block',
params: [
createParam('hash', 'Hash')
@@ -15,7 +15,7 @@ const getBlock: MethodOpt = {
type: 'SignedBlock'
};
const getBlockHash: MethodOpt = {
const getBlockHash: RpcMethodOpt = {
description: 'Get the block hash for a specific block',
params: [
createParam('blockNumber', 'BlockNumber', { isOptional: true })
@@ -23,13 +23,13 @@ const getBlockHash: MethodOpt = {
type: 'Hash'
};
const getHead: MethodOpt = {
const getHead: RpcMethodOpt = {
description: 'Retrieves the best headerHash',
params: [],
type: 'Hash'
};
const getHeader: MethodOpt = {
const getHeader: RpcMethodOpt = {
description: 'Retrieves the header for a specific block',
params: [
createParam('hash', 'Hash')
@@ -37,13 +37,13 @@ const getHeader: MethodOpt = {
type: 'Header'
};
const getRuntimeVersion: MethodOpt = {
const getRuntimeVersion: RpcMethodOpt = {
description: ' Get the runtime version',
params: [],
type: 'RuntimeVersion'
};
const getRuntimeVersionAt: MethodOpt = {
const getRuntimeVersionAt: RpcMethodOpt = {
description: ' Get the runtime version at a specific block',
params: [
createParam('hash', 'Hash')
@@ -51,7 +51,7 @@ const getRuntimeVersionAt: MethodOpt = {
type: 'RuntimeVersion'
};
const newHead: MethodOpt = {
const newHead: RpcMethodOpt = {
description: 'Retrieves the best header via subscription',
subscribe: [
'chain_subscribeNewHead',
@@ -80,4 +80,4 @@ export default {
getRuntimeVersionAt: createMethod(section, 'getRuntimeVersionAt', getRuntimeVersionAt),
newHead: createMethod(section, 'newHead', newHead)
}
} as Section;
} as RpcSection;
+2 -2
View File
@@ -2,11 +2,11 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { MethodOpt, Method } from '../types';
import { RpcMethodOpt, RpcMethod } from '../types';
import isUndefined from '@polkadot/util/is/undefined';
export default function createMethod (section: string, method: string, { description, params, type, isDeprecated = false, isHidden = false, isSigned = false, subscribe }: MethodOpt): Method {
export default function createMethod (section: string, method: string, { description, params, type, isDeprecated = false, isHidden = false, isSigned = false, subscribe }: RpcMethodOpt): RpcMethod {
return {
description,
isDeprecated,
+3 -3
View File
@@ -3,13 +3,13 @@
// of the ISC license. See the LICENSE file for details.
import { CodecTypes } from '@polkadot/types/types';
import { Param } from '../types';
import { RpcParam } from '../types';
type ParamOptions = {
type RpcParamOptions = {
isOptional?: boolean
};
export default function createParam (name: string, type: CodecTypes, { isOptional = false }: ParamOptions = { isOptional: false }): Param {
export default function createParam (name: string, type: CodecTypes, { isOptional = false }: RpcParamOptions = { isOptional: false }): RpcParam {
return {
isOptional,
name,
+13 -13
View File
@@ -2,12 +2,12 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { MethodOpt, Section } from './types';
import { RpcMethodOpt, RpcSection } from './types';
import createMethod from './create/method';
import createParam from './create/param';
const call: MethodOpt = {
const call: RpcMethodOpt = {
description: 'Perform a call to a builtin on the chain',
params: [
createParam('method', 'Text'),
@@ -16,7 +16,7 @@ const call: MethodOpt = {
type: 'Bytes'
};
const callAt: MethodOpt = {
const callAt: RpcMethodOpt = {
description: 'Perform a call to a builtin on the chain (At block)',
params: [
createParam('method', 'Text'),
@@ -26,13 +26,13 @@ const callAt: MethodOpt = {
type: 'Bytes'
};
const getMetadata: MethodOpt = {
const getMetadata: RpcMethodOpt = {
description: 'Returns the runtime metadata',
params: [],
type: 'Metadata'
};
const getMetadataAt: MethodOpt = {
const getMetadataAt: RpcMethodOpt = {
description: 'Returns the runtime metadata',
params: [
createParam('block', 'Hash')
@@ -40,7 +40,7 @@ const getMetadataAt: MethodOpt = {
type: 'Metadata'
};
const getStorage: MethodOpt = {
const getStorage: RpcMethodOpt = {
description: 'Retrieves the storage for a key',
params: [
createParam('key', 'StorageKey')
@@ -48,7 +48,7 @@ const getStorage: MethodOpt = {
type: 'StorageData'
};
const getStorageAt: MethodOpt = {
const getStorageAt: RpcMethodOpt = {
description: 'Retrieves the storage for a key at a specific block',
params: [
createParam('key', 'Bytes'),
@@ -57,7 +57,7 @@ const getStorageAt: MethodOpt = {
type: 'Bytes'
};
const getStorageHash: MethodOpt = {
const getStorageHash: RpcMethodOpt = {
description: 'Retrieves the storage hash',
params: [
createParam('key', 'Bytes')
@@ -65,7 +65,7 @@ const getStorageHash: MethodOpt = {
type: 'Hash'
};
const getStorageHashAt: MethodOpt = {
const getStorageHashAt: RpcMethodOpt = {
description: 'Retrieves the storage hash at a specific block',
params: [
createParam('key', 'Bytes'),
@@ -74,7 +74,7 @@ const getStorageHashAt: MethodOpt = {
type: 'Hash'
};
const getStorageSize: MethodOpt = {
const getStorageSize: RpcMethodOpt = {
description: 'Retrieves the storage size',
params: [
createParam('key', 'Bytes')
@@ -82,7 +82,7 @@ const getStorageSize: MethodOpt = {
type: 'u64'
};
const getStorageSizeAt: MethodOpt = {
const getStorageSizeAt: RpcMethodOpt = {
description: 'Retrieves the storage size at a specific block',
params: [
createParam('key', 'Bytes'),
@@ -91,7 +91,7 @@ const getStorageSizeAt: MethodOpt = {
type: 'u64'
};
const storage: MethodOpt = {
const storage: RpcMethodOpt = {
description: 'Subscribes to storage changes for the provided keys',
subscribe: [
'state_subscribeStorage',
@@ -127,4 +127,4 @@ export default {
getStorageSizeAt: createMethod(section, 'getStorageSizeAt', getStorageSizeAt),
storage: createMethod(section, 'storage', storage)
}
} as Section;
} as RpcSection;
+5 -5
View File
@@ -2,23 +2,23 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { MethodOpt, Section } from './types';
import { RpcMethodOpt, RpcSection } from './types';
import createMethod from './create/method';
const chain: MethodOpt = {
const chain: RpcMethodOpt = {
description: 'Retrieves the chain',
params: [],
type: 'Text'
};
const name: MethodOpt = {
const name: RpcMethodOpt = {
description: 'Retrieves the node name',
params: [],
type: 'Text'
};
const version: MethodOpt = {
const version: RpcMethodOpt = {
description: 'Retrieves the version of the node',
params: [],
type: 'Text'
@@ -39,4 +39,4 @@ export default {
name: createMethod(section, 'name', name),
version: createMethod(section, 'version', version)
}
} as Section;
} as RpcSection;
+7 -7
View File
@@ -4,24 +4,24 @@
import { CodecTypes } from '@polkadot/types/types';
export type Param = {
export type RpcParam = {
isOptional: boolean,
name: string,
type: CodecTypes
};
export type MethodOpt = {
export type RpcMethodOpt = {
description: string,
isDeprecated?: boolean,
isHidden?: boolean,
isSigned?: boolean,
isSubscription?: boolean,
params: Array<Param>,
params: Array<RpcParam>,
subscribe?: [string, string],
type: CodecTypes
};
export type Method = {
export type RpcMethod = {
alias?: string,
description: string,
isDeprecated: boolean,
@@ -29,18 +29,18 @@ export type Method = {
isSigned: boolean,
isSubscription: boolean,
method: string,
params: Array<Param>,
params: Array<RpcParam>,
section: string,
subscribe: [string, string],
type: CodecTypes
};
export type Section = {
export type RpcSection = {
isDeprecated: boolean,
isHidden: boolean,
description: string,
section: string,
methods: {
[index: string]: Method
[index: string]: RpcMethod
}
};
+1 -1
View File
@@ -46,7 +46,7 @@ export default class Address extends Base<AccountId | AccountIndex> {
}
byteLength (): number {
return super.byteLength() + (
return this.raw.byteLength() + (
this.raw instanceof AccountIndex
? 0
: 1
-72
View File
@@ -1,72 +0,0 @@
// Copyright 2017-2018 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import hexToU8a from '@polkadot/util/hex/toU8a';
import Block from './Block';
describe('Block', () => {
it('decodes a block properly', () => {
const block = new Block();
block.fromU8a(hexToU8a(
'0x' +
// parent_hash
'0900000000000000000000000000000000000000000000000000000000000009' +
// number
'4300000000000000' +
// state_root
'0800000000000000000000000000000000000000000000000000000000000008' +
// transaction_root
'0700000000000000000000000000000000000000000000000000000000000007' +
// digest
'08' + // 2 << 2
'0401' +
'0402' +
// transactions
'04' + // 1 << 2
// 111 bytes in compact encoding
'bd01' +
// prefix
'ff' +
'0000000000000000000000000000000000000000000000000000000000000000' +
'00000000' +
'0300' +
'7527000000000000' +
'0000000000000000000000000000000000000000000000000000000000000000' +
'0000000000000000000000000000000000000000000000000000000000000000'
));
expect(
block.header.blockNumber.toNumber()
).toEqual(67);
expect(
block.extrinsics.length
).toEqual(1); // eslint-disable-line
expect(
block.extrinsics.get(0).length
).toEqual(111); // eslint-disable-line
});
// FIXME check again when we have a valid chain/UI
it.skip('has a valid block hash', () => {
expect(
new Block({
extrinsics: [
[255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 139, 165, 170, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
],
header: {
digest: {
logs: []
},
extrinsicsRoot: '0x5ce1a2c55efaf371f06fd94672779daf130591a12f7513ae786bea4fa7bd499e',
number: 1556355,
parentHash: '0x09223919f63b5ded0c7bf7d953ba22bf30ea8a19e6be4de35f25163b2b268490',
stateRoot: '0xd45159e04f36955ecb9c73d6ca514ac2d0e3ce00f58588b7c142d36cf7d237ec'
}
}).hash.toHex()
).toEqual('0xd1b7e2c8f1ba2665d89dba334dcf45caa12e34d22587a09e2d03372366a81d8b');
});
});
+88 -7
View File
@@ -2,17 +2,50 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import u8aToHex from '@polkadot/util/u8a/toHex';
import { KeyringPair } from '@polkadot/util-keyring/types';
import { AnyNumber, AnyU8a } from './types';
import blake2Asu8a from '@polkadot/util-crypto/blake2/asU8a';
import u8aConcat from '@polkadot/util/u8a/concat';
import u8aToHex from '@polkadot/util/u8a/toHex';
import Bytes from './Bytes';
import Compact from './codec/Compact';
import Struct from './codec/Struct';
import ExtrinsicSignature from './ExtrinsicSignature';
import Hash from './Hash';
import { FunctionMetadata } from './Metadata';
import Method from './Method';
type ExtrinsicValue = {
method?: Method
signature?: ExtrinsicSignature
};
// console.error('ExtrinsicSignature', ExtrinsicSignature);
// console.error('Method', Method);
/**
* Representation of an Extrinsic in the system. It contains the actual call,
* (optional) signature and encodes with an actual length prefix
*
* {@link https://github.com/paritytech/wiki/blob/master/Extrinsic.md#the-extrinsic-format-for-node}.
*
* Can be:
* - signed, to create a transaction
* - left as is, to create an inherent
*/
export default class Extrinsic extends Struct {
constructor (value?: ExtrinsicValue) {
super({
signature: ExtrinsicSignature,
method: Method
}, value);
}
get method (): Method {
return this.raw.method as Method;
}
// Representation of an Extrinsic in the system.
//
// NOTE At this point we are just keeping to a bare-bones Vec<u8> structure,
// this should be expanded to do the automatic decoding of an extrinsic
export default class Extrinsic extends Bytes {
// convernience, encodes the extrinsic and returns the actual hash
get hash (): Hash {
return new Hash(
@@ -20,6 +53,54 @@ export default class Extrinsic extends Bytes {
);
}
get length (): number {
return this.toU8a(true).length;
}
get meta (): FunctionMetadata {
return this.method.meta;
}
// the actual [sectionIndex, methodIndex] as used
get callIndex (): Uint8Array {
return this.method.callIndex;
}
get signature (): ExtrinsicSignature {
return this.raw.signature as ExtrinsicSignature;
}
byteLength (): number {
const length = this.length;
return length + Compact.encode(length).length;
}
fromU8a (input: Uint8Array): Extrinsic {
const [offset, length] = Compact.decode(input);
super.fromU8a(input.subarray(offset, offset + length.toNumber()));
return this;
}
sign (signerPair: KeyringPair, nonce: AnyNumber, blockHash: AnyU8a): Extrinsic {
this.signature.addSignature(this.method, signerPair, nonce, blockHash);
return this;
}
toU8a (isBare?: boolean): Uint8Array {
const encoded = super.toU8a();
return isBare
? encoded
: u8aConcat(
Compact.encode(encoded.length),
encoded
);
}
toHex (): string {
return u8aToHex(this.toU8a());
}
@@ -8,7 +8,7 @@ import U8aFixed from './codec/U8aFixed';
// Description of a transaction era
// FIXME should be a enum?
export default class TransactionEra extends U8aFixed {
export default class ExtrinsicEra extends U8aFixed {
constructor (value?: AnyU8a) {
// FIXME this actually indicates an Immortal (i.e. 1 byte)
super(value, 8);
+118
View File
@@ -0,0 +1,118 @@
// Copyright 2017-2018 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { KeyringPair } from '@polkadot/util-keyring/types';
import { AnyNumber, AnyU8a } from './types';
import u8aConcat from '@polkadot/util/u8a/concat';
import Struct from './codec/Struct';
import Address from './Address';
import ExtrinsicEra from './ExtrinsicEra';
import Method from './Method';
import Nonce from './Nonce';
import Signature from './Signature';
import SignaturePayload from './SignaturePayload';
type ExtrinsicSignatureValue = {
signer?: AnyU8a | Address,
signature?: AnyU8a
nonce?: AnyNumber,
era?: AnyU8a
};
const EMPTY_U8A = new Uint8Array();
const IMMORTAL_ERA = new Uint8Array([0]);
const BIT_SIGNED = 0b10000000;
const BIT_UNSIGNED = 0;
const BIT_VERSION = 0b0000001;
// Signature Information.
// 1/3/5/9/33 bytes: The signing account identity, in Address format
// 64 bytes: The Ed25519 signature of the Signing Payload
// 8 bytes: The Transaction Index of the signing account
// 2 bytes: The Transaction Era
export default class ExtrinsicSignature extends Struct {
constructor (value?: ExtrinsicSignatureValue) {
super({
signer: Address,
signature: Signature,
nonce: Nonce,
era: ExtrinsicEra
}, value);
}
get isSigned (): boolean {
return this.signature.length !== 0;
}
get era (): ExtrinsicEra {
return this.raw.era as ExtrinsicEra;
}
get nonce (): Nonce {
return this.raw.nonce as Nonce;
}
get signature (): Signature {
return this.raw.signature as Signature;
}
get signer (): Address {
return this.raw.signer as Address;
}
get version (): number {
// Version Information.
// 1 byte: version information:
// - 7 low bits: version identifier (should be 0b0000001).
// - 1 high bit: signed flag: 1 if this is a transaction (e.g. contains a signature).
return BIT_VERSION | (
this.isSigned
? BIT_SIGNED
: BIT_UNSIGNED
);
}
addSignature (method: Method, signerPair: KeyringPair, nonce: AnyNumber, blockHash: AnyU8a, era: Uint8Array = IMMORTAL_ERA): ExtrinsicSignature {
const signer = new Address(signerPair.publicKey());
const signingPayload = new SignaturePayload({
nonce,
method,
era,
blockHash
});
const signature = new Signature(signingPayload.sign(signerPair));
this.raw.era = signingPayload.era;
this.raw.nonce = signingPayload.nonce;
this.raw.signer = signer;
this.raw.signature = signature;
return this;
}
fromU8a (input: Uint8Array): ExtrinsicSignature {
const version = input[0];
if ((version & BIT_SIGNED) === BIT_SIGNED) {
super.fromU8a(input.subarray(1));
} else {
// Just clear signature, leave others as-is
this.raw.signature = new Signature();
}
return this;
}
toU8a (isBare?: boolean): Uint8Array {
return u8aConcat(
new Uint8Array([this.version]),
this.isSigned
? super.toU8a(isBare)
: EMPTY_U8A
);
}
}
@@ -8,21 +8,21 @@ import u8aConcat from '@polkadot/util/u8a/concat';
import createType from './codec/createType';
import Base from './codec/Base';
import CallIndex from './CallIndex';
import MethodIndex from './MethodIndex';
import { FunctionMetadata } from './Metadata';
/**
* Extrinsic function descriptor, as defined in
* {@link https://github.com/paritytech/wiki/blob/master/Extrinsic.md#the-extrinsic-format-for-node}.
*/
export default class Call extends CallIndex {
export default class Method extends MethodIndex {
protected _args: Array<Base>;
protected _meta: FunctionMetadata;
constructor (index: Call | AnyU8a, meta: FunctionMetadata, args: Array<any>) {
constructor (index: Method | AnyU8a, meta: FunctionMetadata, args: Array<any>) {
super(index);
if (index instanceof Call) {
if (index instanceof Method) {
this._args = args || index.args;
this._meta = meta || index.meta;
} else {
@@ -44,13 +44,16 @@ export default class Call extends CallIndex {
}
toU8a (isBare?: boolean): Uint8Array {
const args = this._meta.arguments
.filter((arg) =>
arg.type.toString() !== 'Origin'
)
.map((argument, index) =>
createType(argument.type, this._args[index]).toU8a(isBare)
);
// FIXME Even when decoded, we will need access to the meta
const args = this.meta
? this.meta.arguments
.filter((arg) =>
arg.type.toString() !== 'Origin'
)
.map((argument, index) =>
createType(argument.type, this.args[index]).toU8a(isBare)
)
: [];
return u8aConcat(
super.toU8a(isBare),
@@ -2,17 +2,17 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import CallIndex from './CallIndex';
import MethodIndex from './MethodIndex';
describe('CallIndex', () => {
describe('MethodIndex', () => {
it('handles u8a encoding correctly', () => {
expect(
new CallIndex([1, 2]).toU8a()
new MethodIndex([1, 2]).toU8a()
).toEqual(new Uint8Array([1, 2]));
});
it('provides access to section/method indexes', () => {
const ci = new CallIndex([1, 2]);
const ci = new MethodIndex([1, 2]);
expect(ci.methodIndex).toEqual(2);
expect(ci.sectionIndex).toEqual(1);
@@ -6,7 +6,7 @@ import { AnyU8a } from './types';
import U8aFixed from './codec/U8aFixed';
export default class CallIndex extends U8aFixed {
export default class MethodIndex extends U8aFixed {
constructor (value?: AnyU8a) {
super(value, 16);
}
+10 -10
View File
@@ -6,15 +6,15 @@ import { KeyringPair } from '@polkadot/util-keyring/types';
import { AnyNumber, AnyU8a } from './types';
import Struct from './codec/Struct';
import Call from './Call';
import Method from './Method';
import Hash from './Hash';
import Nonce from './Nonce';
import TransactionEra from './TransactionEra';
import ExtrinsicEra from './ExtrinsicEra';
type SignaturePayloadValue = {
nonce?: AnyNumber,
call?: Call,
era?: AnyU8a | TransactionEra
method?: Method,
era?: AnyU8a | ExtrinsicEra
blockHash?: AnyU8a
};
@@ -29,8 +29,8 @@ export default class SignaturePayload extends Struct {
constructor (value?: SignaturePayloadValue) {
super({
nonce: Nonce,
call: Call,
era: TransactionEra,
method: Method,
era: ExtrinsicEra,
blockHash: Hash
}, value);
}
@@ -43,12 +43,12 @@ export default class SignaturePayload extends Struct {
return this.raw.blockHash as Hash;
}
get call (): Call {
return this.raw.call as Call;
get method (): Method {
return this.raw.method as Method;
}
get era (): TransactionEra {
return this.raw.era as TransactionEra;
get era (): ExtrinsicEra {
return this.raw.era as ExtrinsicEra;
}
get nonce (): Nonce {
+1 -1
View File
@@ -1 +1 @@
{"jsonrpc":"2.0","result":{"block":{"extrinsics":[[255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,64,95,171,91,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]],"header":{"digest":{"logs":[]},"extrinsicsRoot":"0x233cf65d6f67f628b694c7da2d08020a937bc272560df7dd5cc1f11a7f85dc69","number":1565249,"parentHash":"0x0b0f28f64fdef451b6e2636ef18e9670183e1e4841db5a32ce476186611f6baf","stateRoot":"0xc372e08fa6edddd0ee415742eb551b8475100a40fe47a3d6470ad46b9b7b460d"}},"justification":{"hash":"0x737f58ac7f964353c56160187821ef4d2295d196c8479c21c7ae37f708550b57","round_number":1,"signatures":[["0x65d2273adeb04478658e183dc5edf41f1d86e42255442af62e72dbf1e6c0b977","0x6bfe453137983a7ef474cf3108e7cc0260621fac893e92c41d17952430164a1ee1600fd3f8c9d2d8440d391ac1f1a488ac6fd0185fa0ba722698903753c8bb0c"],["0x8c25a8812190a86c0c086b1c4087e31d82e3375652f9a75e828a4301c98245be","0x9e0ecfe993846758fb15920a7103a5cb4199e03481eec1ebf7abb43cb35e9703baf04b606000c788244ccde5d6fb0b22fc940099ced61e8a404d71787757980b"],["0xbb8943a83c953cc96f927ca2e7062e4819f0b258107e495fb40a78d27c2e1ad7","0xa282bc57b27feccc752f194dfe6e2281986a46bac31d06b629c9b62401804ad00a400a67d3770b7470d207d349f167eda72436d6eae7b90469a61eefe1c99800"],["0xea1a15140e562d9f582b38d64d8d1f575b9f837556fdec40fcc3e564259471c7","0x45926cb65bb59595f447eaac5e782029f7fabd47579fe4fec7b79c1c554abf1c31067a500362c2d84aadca3686bfc455d780d2c3265674154d33ea0349db2307"],["0x432d983331d08c86121cd2c86456cf3eeeb5a2f37bab7f2969453b51b4eed783","0x3a09b440194aa714606a2a223422b7795f7d1ac75dca4b6bc5f5031d51b4e282b264d231ca73ee234a814ea5f60e135e7df6dfa02b363ca1204dfe56c2a4ad03"],["0x2e5baf22ac662c0a3883ccf3e17580e6c41f056642040ddc16a877176becdc53","0x02a5605a3f28f7ed9063a4f465601af8625a7fbb69b4757b9838a90ed3e5e1598671853ee106512a833ab1eb7db48f7915b7b48d0b5c2915898d05f54ce23205"],["0x10b9ddcb0d59f795ab64608bd22215b55868ee01efe6d690b8988c63859a6b27","0x04a3e8d9b12163b8c3488968d447bf8b4968bbce55c09ee18ba57f5b9e10dd8f3ae344551d9014e8a1efb0e7e6e83d7cedf440a1d512266a83d4d25d8759ee06"],["0x044b0abaa1854772c75282aa9187935a56926e9c5f5e0b30cf6993c530c09d26","0x3844d377dc958c96eefbb56c777439142af43aebb764c230b74c3f56a4df3207abf4a347d8fe88e5d2641e82c48522079000c02fd89748705d9efb843128e40d"],["0x1689d1d5c9c5b072034ed95aa1ee22c2dd60df78d82de93dc0e3872bfa4c715a","0x481613e2c0b16ae14adc2e463bda352ebb5a839836c4e384bb4770608cdb38ad02b5ca571d19853f6e706ebf880edf12dc5d448d8d331ea2b8926b03aa9bcd0c"],["0x22fdc8c92de49cd1a11f03f591e507b0827e1e19df35b3b0a2a90ee35df4b06c","0xac7802d1e824c656b3e094cf18d1ff4758d2d8df0874a0be123893a0d8c95ddbe9cf40a765b6cfeecb7756b9f18294f9dca35451499c8a7a99d7621d9cd5610b"],["0xcb7cc7b2f624c92c6fad8ee77458a35c7acaade2b03e5f0634509cef1db7ce09","0x5fc45de00c1291ea605c6929ebe77bd5d2d998c7ad44d1f88bce4b86ecefbd447adbbf0290ea12b2b8a34db2c71f266ddccb54f2074340825edd8f7b0b235503"],["0x5d87486121bcfdfbb7b6d8cbd91558059f798587e194c36efb8b379bb8b3dd8e","0x47f046ecb4aca2ba36994b7e11565306a382615f589d26e827de357623c22d271e0998c4617fa8be3902acdf33cb72538839ca731d4476a0f2b41a4c8297000d"],["0xd164f617a7b5144504166c305f9940fb17cd1656e46ba55f344ee4e5f716c12f","0x663502ec1e40325992d2b7307764f6bd524d30968acf2bd3b0a6d43c5f973eebd192a796a455127d6750f407d14650201b225dc107361c2d634e46c3e0c9340e"],["0x3005b5aab73dc485beecf1df7732876a61c81c8f13d7a4883f1243ecbb9497dd","0x4fdcbb79bd4f852ede225e9e90e5bc6c8b9c8760ac016c4f081f1dfc93ef055b8bbd545b459dd82ef107b20013fae2da55fc31784d933a29261d55466628cd01"],["0xfbf09129e1ca12face363366917add306357e8f6221e57edbfa4e820784cf712","0xc620ff8b940e9be7eaf0f5c8d034c8fec33d1fafc2ea5921660486eff5087027e44bb1b38641894aadb055b9bdece9e480f6042ad801a07588bf19f4ba87cb0e"]]}},"id":8}
{"jsonrpc":"2.0","result":{"block":{"extrinsics":["0x010200dd2bb75b00000000"],"header":{"digest":{"logs":[]},"extrinsicsRoot":"0x06811f569c9b822d10c659695ec4b1f6391472b3a65ecc78131485bc3fafe780","number":2769,"parentHash":"0x89c31b0f09782a2c27c0be0c7e2f096b13ce6541a5d14cc43a9553e50a89314a","stateRoot":"0x6e4e88dc766d13c8975e7806e30cc1627e9553a2ebe59ab123f985d1c925c5ff"}},"justification":{"hash":"0x7ac267bb86a5b674582fba9e84d3e3e9988621124c3e33358f7771224d176795","round_number":0,"signatures":[["0xd172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f","0xf9aa2cd44ee75a40ec230b589f847e1cf1a921f4a85e9825f9cd3892d29eeda628c7bc44b9f55ec305c1ae745d6b96e163338bbd9ae262c60626a50405868701"]]}},"id":16}
+2 -2
View File
@@ -11,12 +11,12 @@ describe('SignedBlock', () => {
it('has the correct stateRoot', () => {
expect(
block.block.header.stateRoot.toString()
).toEqual('0xc372e08fa6edddd0ee415742eb551b8475100a40fe47a3d6470ad46b9b7b460d');
).toEqual('0x6e4e88dc766d13c8975e7806e30cc1627e9553a2ebe59ab123f985d1c925c5ff');
});
it('has the justification', () => {
expect(
block.justification.hash.toHex()
).toEqual('0x737f58ac7f964353c56160187821ef4d2295d196c8479c21c7ae37f708550b57');
).toEqual('0x7ac267bb86a5b674582fba9e84d3e3e9988621124c3e33358f7771224d176795');
});
});
@@ -1,50 +0,0 @@
// Copyright 2017-2018 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { AnyNumber, AnyU8a } from './types';
import Struct from './codec/Struct';
import Address from './Address';
import TransactionEra from './TransactionEra';
import Nonce from './Nonce';
import Signature from './Signature';
type TransactionSignatureValue = {
signer?: AnyU8a | Address,
signature?: AnyU8a
nonce?: AnyNumber,
era?: AnyU8a
};
// Signature Information.
// 1/3/5/9/33 bytes: The signing account identity, in Address format
// 64 bytes: The Ed25519 signature of the Signing Payload
// 8 bytes: The Transaction Index of the signing account
// 2 bytes: The Transaction Era
export default class TransactionSignature extends Struct {
constructor (value?: TransactionSignatureValue) {
super({
signer: Address,
signature: Signature,
nonce: Nonce,
era: TransactionEra
}, value);
}
get era (): TransactionEra {
return this.raw.era as TransactionEra;
}
get nonce (): Nonce {
return this.raw.nonce as Nonce;
}
get signature (): Signature {
return this.raw.signature as Signature;
}
get signer (): Address {
return this.raw.signer as Address;
}
}
@@ -1,102 +0,0 @@
// Copyright 2017-2018 @polkadot/extrinsics authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { KeyringPair } from '@polkadot/util-keyring/types';
import { AnyNumber, AnyU8a } from './types';
import u8aConcat from '@polkadot/util/u8a/concat';
import Address from './Address';
import Call from './Call';
import Extrinsic from './Extrinsic';
import { FunctionMetadata } from './Metadata';
import SignaturePayload from './SignaturePayload';
import TransactionSignature from './TransactionSignature';
const EMPTY_U8A = new Uint8Array();
const IMMORTAL_ERA = new Uint8Array([0]);
/**
* Unchecked mortal extrinsic, as defined here:
* {@link https://github.com/paritytech/wiki/blob/master/Extrinsic.md#the-extrinsic-format-for-node}.
* Can be:
* - signed, to create a transaction
* - left as is, to create an inherent
*/
export default class UncheckedMortalExtrinsic extends Extrinsic {
protected _call: Call;
protected _signature?: Uint8Array;
constructor (call: Call) {
super();
this._call = call;
this.raw = UncheckedMortalExtrinsic.encode(call);
}
static encode (call: Call, signature?: TransactionSignature): Uint8Array {
// Version Information.
// 1 byte: version information:
// - 7 low bits: version identifier (should be 0b0000001).
// - 1 high bit: signed flag: 1 if this is a transaction (e.g. contains a signature).
const highBit = signature ? 0b10000000 : 0;
const version = 0b00000001;
return u8aConcat(
new Uint8Array([highBit | version]),
signature
? signature.toU8a()
: EMPTY_U8A,
call.toU8a()
);
}
get isSigned () {
return this._signature;
}
get meta (): FunctionMetadata {
return this._call.meta;
}
get callIndex (): Uint8Array {
return this._call.callIndex;
}
get methodIndex (): number {
return this._call.methodIndex;
}
get sectionIndex (): number {
return this._call.sectionIndex;
}
sign (signerPair: KeyringPair, nonce: AnyNumber, blockHash: AnyU8a): UncheckedMortalExtrinsic {
const signature = this.createSignature(this._call, signerPair, nonce, blockHash);
this._signature = signature.toU8a();
this.raw = UncheckedMortalExtrinsic.encode(this._call, signature);
return this;
}
private createSignature (call: Call, signerPair: KeyringPair, nonce: AnyNumber, blockHash: AnyU8a, era: Uint8Array = IMMORTAL_ERA): TransactionSignature {
const signer = new Address(signerPair.publicKey());
const signingPayload = new SignaturePayload({
nonce,
call,
era,
blockHash
});
const signature = signingPayload.sign(signerPair);
return new TransactionSignature({
era: signingPayload.era,
nonce: signingPayload.nonce,
signer,
signature
});
}
}
+4 -3
View File
@@ -2,11 +2,12 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import hexToU8a from '@polkadot/util/hex/toU8a';
import isHex from '@polkadot/util/is/hex';
import u8aConcat from '@polkadot/util/u8a/concat';
import u8aToU8a from '@polkadot/util/u8a/toU8a';
import Base from './Base';
import isU8a from '@polkadot/util/is/u8a';
// A Struct defines an Object with key/values - where the values are Base<T> values. It removes
// a lot of repetition from the actual coding, define a structure type, pass it the key/Base<T>
@@ -84,8 +85,8 @@ export default class Struct <
}
fromJSON (input: any): Struct<S, T, V, E> {
if (isHex(input)) {
return this.fromU8a(hexToU8a(input));
if (isHex(input) || isU8a(input)) {
return this.fromU8a(u8aToU8a(input));
}
Object.keys(this.raw).forEach((key) => {
+3 -2
View File
@@ -4,7 +4,6 @@
import assert from '@polkadot/util/assert';
import * as Types from '../index';
import Text from '../Text';
import Base from './Base';
import Tuple from './Tuple';
@@ -129,7 +128,9 @@ export function getTypeClass (value: TypeDef): Constructor {
);
}
const Type = (Types as any)[value.type];
// We are dynamically loading as to avoid circular dependencies
const Types = require('../index');
const Type = Types[value.type];
assert(Type, `Unable to determine type from '${value.type}'`);
+4 -5
View File
@@ -18,9 +18,9 @@ export { default as Block } from './Block';
export { default as BlockNumber } from './BlockNumber';
export { default as Bool } from './Bool';
export { default as Bytes } from './Bytes';
export { default as Call } from './Call';
export { default as CallIndex } from './CallIndex';
export { default as Extrinsic } from './Extrinsic';
export { default as ExtrinsicEra } from './ExtrinsicEra';
export { default as ExtrinsicSignature } from './ExtrinsicSignature';
// NOTE Only used internally, exported as PendingExtrinsics
// export { default as Extrinsics } from './Extrinsics';
export { default as Gas } from './Gas';
@@ -31,6 +31,8 @@ export { default as Hash } from './Hash';
export { default as Header } from './Header';
export { default as KeyValue } from './KeyValue';
export { default as Metadata } from './Metadata';
export { default as Method } from './Method';
export { default as MethodIndex } from './MethodIndex';
export { default as MisbehaviorReport } from './MisbehaviorReport';
export { default as Moment } from './Moment';
export { default as NewAccountOutcome } from './NewAccountOutcome';
@@ -52,8 +54,6 @@ export { default as StorageChangeSet } from './StorageChangeSet';
export { default as StorageData } from './StorageData';
export { default as StorageKey } from './StorageKey';
export { default as Text } from './Text';
export { default as TransactionEra } from './TransactionEra';
export { default as TransactionSignature } from './TransactionSignature';
// NOTE Type is currently only used internally (possibly in codec-related work)
// export { default as Type } from './Type';
// NOTE We are exporting the U* classes as lowercase, matching with Rust
@@ -63,7 +63,6 @@ export { default as u32 } from './U32';
export { default as u64 } from './U64';
export { default as u128 } from './U128';
export { default as u256 } from './U256';
export { default as UncheckedMortalExtrinsic } from './UncheckedMortalExtrinsic';
export { default as ValidatorPrefs } from './ValidatorPrefs';
export { default as VoteThreshold } from './VoteThreshold';
export { default as VoteIndex } from './VoteIndex';