Extrinsics with version decoding handler (#1090)

This commit is contained in:
Jaco Greeff
2019-07-10 16:37:40 +02:00
committed by GitHub
parent 7aae71d2d3
commit 2f157cfb27
3 changed files with 238 additions and 41 deletions
+2 -1
View File
@@ -2,11 +2,12 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Codec } from '../types';
import BN from 'bn.js';
import { hexToU8a, isBn, isHex, isNumber, isU8a, u8aConcat, u8aToHex, u8aToU8a, u8aToBn } from '@polkadot/util';
import { decodeAddress } from '@polkadot/util-crypto';
import { Codec } from '../types';
import Base from '../codec/Base';
import AccountId from './AccountId';
import AccountIndex from './AccountIndex';
+61 -40
View File
@@ -4,21 +4,20 @@
import { AnyNumber, AnyU8a, ArgsDef, Codec, IExtrinsic, IKeyringPair, SignatureOptions } from '../types';
import { assert, isHex, isU8a, u8aToHex, u8aToU8a } from '@polkadot/util';
import { blake2AsU8a } from '@polkadot/util-crypto';
import { assert, isHex, isU8a, u8aToU8a } from '@polkadot/util';
import Base from '../codec/Base';
import Compact from '../codec/Compact';
import Struct from '../codec/Struct';
import { FunctionMetadata } from '../Metadata/v6/Calls';
import Method from '../primitive/Method';
import Address from '../primitive/Address';
import Hash from '../primitive/Hash';
import ExtrinsicSignature from './ExtrinsicSignature';
import ExtrinsicV1, { ExtrinsicValueV1 } from './ExtrinsicV1';
type ExtrinsicValue = {
method?: Method
signature?: ExtrinsicSignature
};
type ExtrinsicValue = ExtrinsicValueV1;
const UNMASK_VERSION = 0b01111111;
/**
* @name Extrinsic
@@ -32,15 +31,12 @@ type ExtrinsicValue = {
* - signed, to create a transaction
* - left as is, to create an inherent
*/
export default class Extrinsic extends Struct implements IExtrinsic {
export default class Extrinsic extends Base<ExtrinsicV1> implements IExtrinsic, Codec {
constructor (value?: ExtrinsicValue | AnyU8a | Method) {
super({
signature: ExtrinsicSignature,
method: Method
}, Extrinsic.decodeExtrinsic(value));
super(Extrinsic.decodeExtrinsic(value));
}
static decodeExtrinsic (value: ExtrinsicValue | AnyU8a | Method = new Uint8Array()): ExtrinsicValue | Array<number> | Uint8Array {
static decodeExtrinsic (value?: ExtrinsicValue | AnyU8a | Method): ExtrinsicV1 {
if (Array.isArray(value) || isHex(value)) {
// Instead of the block below, it should simply be:
// return Extrinsic.decodeExtrinsic(hexToU8a(value as string));
@@ -58,7 +54,7 @@ export default class Extrinsic extends Struct implements IExtrinsic {
);
} else if (isU8a(value)) {
if (!value.length) {
return new Uint8Array();
return new ExtrinsicV1(new Uint8Array());
}
const [offset, length] = Compact.decodeU8a(value);
@@ -66,14 +62,27 @@ export default class Extrinsic extends Struct implements IExtrinsic {
assert(total <= value.length, `Extrinsic: required length less than remainder, expected at least ${total}, found ${value.length}`);
return value.subarray(offset, total);
return Extrinsic.decodeU8a(value.subarray(offset, total));
} else if (value instanceof Method) {
return {
return new ExtrinsicV1({
method: value
};
});
}
return value;
return new ExtrinsicV1(value);
}
static decodeU8a (value: Uint8Array): ExtrinsicV1 {
// decode the actual version string
const version = value[0] & UNMASK_VERSION;
switch (version) {
case 1:
return new ExtrinsicV1(value);
default:
throw new Error(`Unsupported extrinsic version ${version}`);
}
}
/**
@@ -108,67 +117,70 @@ export default class Extrinsic extends Struct implements IExtrinsic {
* @description The length of the value when encoded as a Uint8Array
*/
get encodedLength (): number {
const length = this.length;
return length + Compact.encodeU8a(length).length;
return this.raw.encodedLength;
}
/**
* @description Convernience function, encodes the extrinsic and returns the actual hash
*/
get hash (): Hash {
return new Hash(
blake2AsU8a(this.toU8a(), 256)
);
return this.raw.hash;
}
/**
* @description `true` is method has `Origin` argument (compatibility with [[Method]])
*/
get hasOrigin (): boolean {
return this.method.hasOrigin;
return this.raw.hasOrigin;
}
/**
* @description Checks if the value is an empty value
*/
get isEmpty (): boolean {
return this.raw.isEmpty;
}
/**
* @description `true` id the extrinsic is signed
*/
get isSigned (): boolean {
return this.signature.isSigned;
return this.raw.isSigned;
}
/**
* @description The length of the encoded value
*/
get length (): number {
return this.toU8a(true).length;
return this.raw.length;
}
/**
* @description The [[FunctionMetadata]] that describes the extrinsic
*/
get meta (): FunctionMetadata {
return this.method.meta;
return this.raw.meta;
}
/**
* @description The [[Method]] this extrinsic wraps
*/
get method (): Method {
return this.get('method') as Method;
return this.raw.method;
}
/**
* @description The [[ExtrinsicSignature]]
*/
get signature (): ExtrinsicSignature {
return this.get('signature') as ExtrinsicSignature;
return this.raw.signature;
}
/**
* @description Add an [[ExtrinsicSignature]] to the extrinsic (already generated)
*/
addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | string, nonce: AnyNumber, era?: Uint8Array): Extrinsic {
this.signature.addSignature(signer, signature, nonce, era);
this.raw.addSignature(signer, signature, nonce, era);
return this;
}
@@ -177,30 +189,43 @@ export default class Extrinsic extends Struct implements IExtrinsic {
* @description Sign the extrinsic with a specific keypair
*/
sign (account: IKeyringPair, options: SignatureOptions): Extrinsic {
this.signature.sign(this.method, account, options);
this.raw.sign(account, options);
return this;
}
/**
* @description Compares the value of the input to see if there is a match
*/
eq (other?: any): boolean {
return this.raw.eq(other);
}
/**
* @description Returns a hex string representation of the value
*/
toHex (): string {
return u8aToHex(this.toU8a());
return this.raw.toHex();
}
/**
* @description Converts the Object to JSON, typically used for RPC transfers
*/
toJSON (): string {
return this.toHex();
return this.raw.toJSON();
}
/**
* @description Returns the string representation of the value
*/
toString (): string {
return this.raw.toString();
}
/**
* @description Returns the base runtime type name for this instance
*/
toRawType (): string {
// We are treating this in the same way we do a primitive, this is known
return 'Extrinsic';
}
@@ -209,10 +234,6 @@ export default class Extrinsic extends Struct implements IExtrinsic {
* @param isBare true when the value has none of the type-specific prefixes (internal)
*/
toU8a (isBare?: boolean): Uint8Array {
const encoded = super.toU8a();
return isBare
? encoded
: Compact.addLengthPrefix(encoded);
return this.raw.toU8a(isBare);
}
}
+175
View File
@@ -0,0 +1,175 @@
// 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 { AnyNumber, ArgsDef, Codec, IExtrinsic, IKeyringPair, SignatureOptions } from '../types';
import { u8aToHex } from '@polkadot/util';
import { blake2AsU8a } from '@polkadot/util-crypto';
import Compact from '../codec/Compact';
import Struct from '../codec/Struct';
import { FunctionMetadata } from '../Metadata/v6/Calls';
import Method from '../primitive/Method';
import Address from '../primitive/Address';
import Hash from '../primitive/Hash';
import ExtrinsicSignature from './ExtrinsicSignature';
export type ExtrinsicValueV1 = {
method?: Method
signature?: ExtrinsicSignature
};
/**
* @name ExtrinsicV1
* @description
* The first generation of compact extrinsics
*/
export default class ExtrinsicV1 extends Struct implements IExtrinsic {
constructor (value?: Uint8Array | ExtrinsicValueV1) {
super({
signature: ExtrinsicSignature,
method: Method
}, value);
}
/**
* @description The arguments passed to for the call, exposes args so it is compatible with [[Method]]
*/
get args (): Array<Codec> {
return this.method.args;
}
/**
* @description Thge argument defintions, compatible with [[Method]]
*/
get argsDef (): ArgsDef {
return this.method.argsDef;
}
/**
* @description The actual `[sectionIndex, methodIndex]` as used in the Method
*/
get callIndex (): Uint8Array {
return this.method.callIndex;
}
/**
* @description The actual data for the Method
*/
get data (): Uint8Array {
return this.method.data;
}
/**
* @description The length of the value when encoded as a Uint8Array
*/
get encodedLength (): number {
const length = this.length;
return length + Compact.encodeU8a(length).length;
}
/**
* @description Convernience function, encodes the extrinsic and returns the actual hash
*/
get hash (): Hash {
return new Hash(
blake2AsU8a(this.toU8a(), 256)
);
}
/**
* @description `true` is method has `Origin` argument (compatibility with [[Method]])
*/
get hasOrigin (): boolean {
return this.method.hasOrigin;
}
/**
* @description `true` id the extrinsic is signed
*/
get isSigned (): boolean {
return this.signature.isSigned;
}
/**
* @description The length of the encoded value
*/
get length (): number {
return this.toU8a(true).length;
}
/**
* @description The [[FunctionMetadata]] that describes the extrinsic
*/
get meta (): FunctionMetadata {
return this.method.meta;
}
/**
* @description The [[Method]] this extrinsic wraps
*/
get method (): Method {
return this.get('method') as Method;
}
/**
* @description The [[ExtrinsicSignature]]
*/
get signature (): ExtrinsicSignature {
return this.get('signature') as ExtrinsicSignature;
}
/**
* @description Add an [[ExtrinsicSignature]] to the extrinsic (already generated)
*/
addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | string, nonce: AnyNumber, era?: Uint8Array): ExtrinsicV1 {
this.signature.addSignature(signer, signature, nonce, era);
return this;
}
/**
* @description Sign the extrinsic with a specific keypair
*/
sign (account: IKeyringPair, options: SignatureOptions): ExtrinsicV1 {
this.signature.sign(this.method, account, options);
return this;
}
/**
* @description Returns a hex string representation of the value
*/
toHex (): string {
return u8aToHex(this.toU8a());
}
/**
* @description Converts the Object to JSON, typically used for RPC transfers
*/
toJSON (): string {
return this.toHex();
}
/**
* @description Returns the base runtime type name for this instance
*/
toRawType (): string {
// We are treating this in the same way we do a primitive, this is known
return 'Extrinsic';
}
/**
* @description Encodes the value as a Uint8Array as per the SCALE specifications
* @param isBare true when the value has none of the type-specific prefixes (internal)
*/
toU8a (isBare?: boolean): Uint8Array {
const encoded = super.toU8a();
return isBare
? encoded
: Compact.addLengthPrefix(encoded);
}
}