Raw decoding for extrinsics (#214)
This commit is contained in:
@@ -22,11 +22,7 @@ export default function createDescriptor (
|
||||
const callIndex = new Uint8Array([index, meta.id.toNumber()]);
|
||||
let extrinsicFn: any;
|
||||
|
||||
// If the extrinsic function has an argument of type `Origin`, we ignore it
|
||||
// FIXME should be `arg.type !== Origin`, but doesn't work...
|
||||
const expectedArgs = meta.arguments.filter((arg) =>
|
||||
arg.type.toString() !== 'Origin'
|
||||
);
|
||||
const expectedArgs = Method.filterOrigin(meta);
|
||||
|
||||
extrinsicFn = (...args: any[]): Extrinsic => {
|
||||
if (expectedArgs.length.valueOf() !== args.length) {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// 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 Extrinsic from './Extrinsic';
|
||||
|
||||
describe('Extrinsic', () => {
|
||||
it('decodes a non-signed properly via fromJSON', () => {
|
||||
const extrinsic = new Extrinsic().fromJSON('0x010200ea51b75b00000000');
|
||||
|
||||
expect(extrinsic.isSigned).toEqual(false);
|
||||
expect(extrinsic.callIndex).toEqual(new Uint8Array([2, 0]));
|
||||
expect(extrinsic.data).toEqual(new Uint8Array([234, 81, 183, 91, 0, 0, 0, 0]));
|
||||
});
|
||||
});
|
||||
@@ -6,6 +6,7 @@ import { KeyringPair } from '@polkadot/util-keyring/types';
|
||||
import { AnyNumber, AnyU8a } from './types';
|
||||
|
||||
import blake2Asu8a from '@polkadot/util-crypto/blake2/asU8a';
|
||||
import hexToU8a from '@polkadot/util/hex/toU8a';
|
||||
import u8aConcat from '@polkadot/util/u8a/concat';
|
||||
import u8aToHex from '@polkadot/util/u8a/toHex';
|
||||
|
||||
@@ -42,8 +43,13 @@ export default class Extrinsic extends Struct {
|
||||
}, value);
|
||||
}
|
||||
|
||||
get method (): Method {
|
||||
return this.raw.method as Method;
|
||||
// the actual [sectionIndex, methodIndex] as used
|
||||
get callIndex (): Uint8Array {
|
||||
return this.method.callIndex;
|
||||
}
|
||||
|
||||
get data (): Uint8Array {
|
||||
return this.method.data;
|
||||
}
|
||||
|
||||
// convernience, encodes the extrinsic and returns the actual hash
|
||||
@@ -53,6 +59,10 @@ export default class Extrinsic extends Struct {
|
||||
);
|
||||
}
|
||||
|
||||
get isSigned (): boolean {
|
||||
return this.signature.isSigned;
|
||||
}
|
||||
|
||||
get length (): number {
|
||||
return this.toU8a(true).length;
|
||||
}
|
||||
@@ -61,9 +71,8 @@ export default class Extrinsic extends Struct {
|
||||
return this.method.meta;
|
||||
}
|
||||
|
||||
// the actual [sectionIndex, methodIndex] as used
|
||||
get callIndex (): Uint8Array {
|
||||
return this.method.callIndex;
|
||||
get method (): Method {
|
||||
return this.raw.method as Method;
|
||||
}
|
||||
|
||||
get signature (): ExtrinsicSignature {
|
||||
@@ -76,6 +85,12 @@ export default class Extrinsic extends Struct {
|
||||
return length + Compact.encode(length).length;
|
||||
}
|
||||
|
||||
fromJSON (input: any): Extrinsic {
|
||||
super.fromU8a(hexToU8a(input));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): Extrinsic {
|
||||
const [offset, length] = Compact.decode(input);
|
||||
|
||||
@@ -102,7 +117,7 @@ export default class Extrinsic extends Struct {
|
||||
}
|
||||
|
||||
toHex (): string {
|
||||
return u8aToHex(this.toU8a());
|
||||
return u8aToHex(this.toU8a(true));
|
||||
}
|
||||
|
||||
toJSON (): any {
|
||||
|
||||
@@ -43,6 +43,15 @@ export default class ExtrinsicSignature extends Struct {
|
||||
}, value);
|
||||
}
|
||||
|
||||
byteLength (): number {
|
||||
// version has 1 byte, signature takes the rest
|
||||
return 1 + (
|
||||
this.isSigned
|
||||
? super.byteLength()
|
||||
: 0
|
||||
);
|
||||
}
|
||||
|
||||
get isSigned (): boolean {
|
||||
return this.signature.length !== 0;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import u8aConcat from '@polkadot/util/u8a/concat';
|
||||
import createType from './codec/createType';
|
||||
import Base from './codec/Base';
|
||||
import MethodIndex from './MethodIndex';
|
||||
import { FunctionMetadata } from './Metadata';
|
||||
import { FunctionMetadata, FunctionArgumentMetadata } from './Metadata';
|
||||
|
||||
/**
|
||||
* Extrinsic function descriptor, as defined in
|
||||
@@ -17,6 +17,7 @@ import { FunctionMetadata } from './Metadata';
|
||||
*/
|
||||
export default class Method extends MethodIndex {
|
||||
protected _args: Array<Base>;
|
||||
protected _data: Uint8Array;
|
||||
protected _meta: FunctionMetadata;
|
||||
|
||||
constructor (index: Method | AnyU8a, meta: FunctionMetadata, args: Array<any>) {
|
||||
@@ -29,35 +30,72 @@ export default class Method extends MethodIndex {
|
||||
this._args = args;
|
||||
this._meta = meta;
|
||||
}
|
||||
|
||||
this._data = Method.encode(this._meta, this._args);
|
||||
}
|
||||
|
||||
get args () {
|
||||
static decode (meta: FunctionMetadata, data: Uint8Array): Array<Base> {
|
||||
let offset = 0;
|
||||
|
||||
return Method.filterOrigin(meta).map(({ type }) => {
|
||||
const base = createType(type).fromU8a(data.subarray(offset));
|
||||
|
||||
offset += base.byteLength();
|
||||
|
||||
return base;
|
||||
});
|
||||
}
|
||||
|
||||
static encode (meta: FunctionMetadata, args: Array<any>): Uint8Array {
|
||||
const encoded = Method.filterOrigin(meta).map(({ type }, index) =>
|
||||
createType(type, args[index]).toU8a()
|
||||
);
|
||||
|
||||
return u8aConcat(...encoded);
|
||||
}
|
||||
|
||||
// If the extrinsic function has an argument of type `Origin`, we ignore it
|
||||
static filterOrigin (meta?: FunctionMetadata): Array<FunctionArgumentMetadata> {
|
||||
// FIXME should be `arg.type !== Origin`, but doesn't work...
|
||||
return meta
|
||||
? meta.arguments.filter(({ type }) =>
|
||||
type.toString() !== 'Origin'
|
||||
)
|
||||
: [];
|
||||
}
|
||||
|
||||
byteLength (): number {
|
||||
return super.byteLength() + this.data.length;
|
||||
}
|
||||
|
||||
get args (): Array<any> {
|
||||
return this._args;
|
||||
}
|
||||
|
||||
get index () {
|
||||
get data (): Uint8Array {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
get index (): Uint8Array {
|
||||
return this.raw;
|
||||
}
|
||||
|
||||
get meta () {
|
||||
get meta (): FunctionMetadata {
|
||||
return this._meta;
|
||||
}
|
||||
|
||||
toU8a (isBare?: boolean): Uint8Array {
|
||||
// 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)
|
||||
)
|
||||
: [];
|
||||
fromU8a (input: Uint8Array): Method {
|
||||
super.fromU8a(input);
|
||||
|
||||
this._data = input.subarray(super.byteLength());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
toU8a (isBare?: boolean): Uint8Array {
|
||||
return u8aConcat(
|
||||
super.toU8a(isBare),
|
||||
...args
|
||||
this.data
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,27 +18,31 @@ export default class U8aFixed extends U8a {
|
||||
|
||||
this._bitLength = bitLength;
|
||||
|
||||
this._trimLength();
|
||||
this._trim();
|
||||
}
|
||||
|
||||
private _trimLength (): void {
|
||||
this.raw = this.raw.subarray(0, this.byteLength());
|
||||
private _trimLength (): number {
|
||||
return this._bitLength / 8;
|
||||
}
|
||||
|
||||
private _trim (): void {
|
||||
this.raw = this.raw.subarray(0, this._trimLength());
|
||||
}
|
||||
|
||||
byteLength (): number {
|
||||
return this._bitLength / 8;
|
||||
return this._trimLength();
|
||||
}
|
||||
|
||||
fromJSON (input: any): U8aFixed {
|
||||
super.fromJSON(input);
|
||||
|
||||
this._trimLength();
|
||||
this._trim();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): U8aFixed {
|
||||
super.fromU8a(input.subarray(0, this.byteLength()));
|
||||
super.fromU8a(input.subarray(0, this._trimLength()));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user