From 3bb719fcfc2f96d835ee3ac0d3363bd620bcf7ed Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 24 Oct 2019 15:13:03 +0200 Subject: [PATCH] Extrinsic v4 (#1498) * Extrinsic v4 * Comments * V4 with MultiSignature * Bump keyring with signature type indicator * Return actual Signature type * Update with MultiSignature enum * Bump to stable common * De-dup definitions somewhat --- packages/api-derive/package.json | 2 +- packages/api-metadata/package.json | 6 +- packages/api/package.json | 6 +- packages/rpc-core/package.json | 2 +- packages/rpc-provider/package.json | 6 +- packages/types/package.json | 6 +- packages/types/src/interfaceRegistry.ts | 19 ++- .../src/interfaces/claims/definitions.ts | 1 - packages/types/src/interfaces/claims/types.ts | 4 - .../EdcsaSignature.spec.ts | 0 .../src/interfaces/runtime/definitions.ts | 12 +- .../types/src/interfaces/runtime/types.ts | 29 +++- .../src/primitive/Extrinsic/Extrinsic.ts | 16 +- .../primitive/Extrinsic/ExtrinsicPayload.ts | 13 +- .../types/src/primitive/Extrinsic/index.ts | 10 +- .../types/src/primitive/Extrinsic/util.ts | 5 +- .../Extrinsic/v1/ExtrinsicSignature.ts | 2 +- .../Extrinsic/v2/ExtrinsicPayload.ts | 22 ++- .../Extrinsic/v2/ExtrinsicSignature.ts | 2 +- .../Extrinsic/v3/ExtrinsicPayload.ts | 19 +-- .../Extrinsic/v3/ExtrinsicSignature.ts | 2 +- .../primitive/Extrinsic/v4/Extrinsic.spec.ts | 75 +++++++++ .../src/primitive/Extrinsic/v4/Extrinsic.ts | 101 ++++++++++++ .../Extrinsic/v4/ExtrinsicPayload.ts | 85 ++++++++++ .../Extrinsic/v4/ExtrinsicSignature.spec.ts | 31 ++++ .../Extrinsic/v4/ExtrinsicSignature.ts | 150 ++++++++++++++++++ packages/types/src/types.ts | 7 +- yarn.lock | 113 +++++++------ 28 files changed, 629 insertions(+), 117 deletions(-) rename packages/types/src/interfaces/{claims => runtime}/EdcsaSignature.spec.ts (100%) create mode 100644 packages/types/src/primitive/Extrinsic/v4/Extrinsic.spec.ts create mode 100644 packages/types/src/primitive/Extrinsic/v4/Extrinsic.ts create mode 100644 packages/types/src/primitive/Extrinsic/v4/ExtrinsicPayload.ts create mode 100644 packages/types/src/primitive/Extrinsic/v4/ExtrinsicSignature.spec.ts create mode 100644 packages/types/src/primitive/Extrinsic/v4/ExtrinsicSignature.ts diff --git a/packages/api-derive/package.json b/packages/api-derive/package.json index aca4cc5745..5ababe00e9 100644 --- a/packages/api-derive/package.json +++ b/packages/api-derive/package.json @@ -32,6 +32,6 @@ "@polkadot/types": "^0.95.0-beta.33" }, "devDependencies": { - "@polkadot/keyring": "^1.5.1" + "@polkadot/keyring": "^1.6.1" } } diff --git a/packages/api-metadata/package.json b/packages/api-metadata/package.json index d998b4bb32..9a19d7d606 100644 --- a/packages/api-metadata/package.json +++ b/packages/api-metadata/package.json @@ -28,10 +28,10 @@ "dependencies": { "@babel/runtime": "^7.6.3", "@polkadot/types": "^0.95.0-beta.33", - "@polkadot/util": "^1.5.1", - "@polkadot/util-crypto": "^1.5.1" + "@polkadot/util": "^1.6.1", + "@polkadot/util-crypto": "^1.6.1" }, "devDependencies": { - "@polkadot/keyring": "^1.5.1" + "@polkadot/keyring": "^1.6.1" } } diff --git a/packages/api/package.json b/packages/api/package.json index 80b31d1e88..9ec5636f8c 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -29,13 +29,13 @@ "@babel/runtime": "^7.6.3", "@polkadot/api-derive": "^0.95.0-beta.33", "@polkadot/api-metadata": "^0.95.0-beta.33", - "@polkadot/keyring": "^1.5.1", + "@polkadot/keyring": "^1.6.1", "@polkadot/rpc-core": "^0.95.0-beta.33", "@polkadot/rpc-provider": "^0.95.0-beta.33", "@polkadot/types": "^0.95.0-beta.33", - "@polkadot/util-crypto": "^1.5.1" + "@polkadot/util-crypto": "^1.6.1" }, "devDependencies": { - "@polkadot/keyring": "^1.5.1" + "@polkadot/keyring": "^1.6.1" } } diff --git a/packages/rpc-core/package.json b/packages/rpc-core/package.json index cc91a18855..d313ba07b5 100644 --- a/packages/rpc-core/package.json +++ b/packages/rpc-core/package.json @@ -30,7 +30,7 @@ "@polkadot/jsonrpc": "^0.95.0-beta.33", "@polkadot/rpc-provider": "^0.95.0-beta.33", "@polkadot/types": "^0.95.0-beta.33", - "@polkadot/util": "^1.5.1", + "@polkadot/util": "^1.6.1", "rxjs": "^6.5.3" } } diff --git a/packages/rpc-provider/package.json b/packages/rpc-provider/package.json index 4f668ecdc4..77d6dc2b2e 100644 --- a/packages/rpc-provider/package.json +++ b/packages/rpc-provider/package.json @@ -28,15 +28,15 @@ "dependencies": { "@babel/runtime": "^7.6.3", "@polkadot/api-metadata": "^0.95.0-beta.33", - "@polkadot/util": "^1.5.1", - "@polkadot/util-crypto": "^1.5.1", + "@polkadot/util": "^1.6.1", + "@polkadot/util-crypto": "^1.6.1", "@types/nock": "^11.1.0", "eventemitter3": "^4.0.0", "isomorphic-fetch": "^2.2.1", "websocket": "^1.0.30" }, "devDependencies": { - "@polkadot/keyring": "^1.5.1", + "@polkadot/keyring": "^1.6.1", "mock-socket": "^9.0.2", "nock": "^11.4.0" } diff --git a/packages/types/package.json b/packages/types/package.json index b0a31e765e..491d377f70 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -27,12 +27,12 @@ "homepage": "https://github.com/polkadot-js/api/tree/master/packages/types#readme", "dependencies": { "@babel/runtime": "^7.6.3", - "@polkadot/util": "^1.5.1", - "@polkadot/util-crypto": "^1.5.1", + "@polkadot/util": "^1.6.1", + "@polkadot/util-crypto": "^1.6.1", "@types/memoizee": "^0.4.3", "memoizee": "^0.4.14" }, "devDependencies": { - "@polkadot/keyring": "^1.5.1" + "@polkadot/keyring": "^1.6.1" } } diff --git a/packages/types/src/interfaceRegistry.ts b/packages/types/src/interfaceRegistry.ts index 8a70967532..37d79743b9 100644 --- a/packages/types/src/interfaceRegistry.ts +++ b/packages/types/src/interfaceRegistry.ts @@ -3,7 +3,7 @@ import { Compact, Option, Vec } from '@polkadot/types/codec'; import { Bytes, Data, Fixed64, H160, H256, H512, Null, StorageData, StorageHasher, StorageKey, Text, Type, U256, bool, i128, i16, i256, i32, i64, i8, u128, u16, u256, u32, u64, u8, usize } from '@polkadot/types/primitive'; -import { AccountId, AccountIdOf, AccountIndex, Address, AssetId, Balance, BalanceOf, Block, BlockNumber, Call, Consensus, ConsensusEngineId, Digest, DigestItem, Ed25519Signature, Extrinsic, ExtrinsicEra, ExtrinsicPayload, ExtrinsicPayloadUnknown, ExtrinsicPayloadV1, ExtrinsicPayloadV2, ExtrinsicPayloadV3, ExtrinsicUnknown, ExtrinsicV1, ExtrinsicV2, ExtrinsicV3, Hash, Header, ImmortalEra, Index, Justification, KeyTypeId, KeyValue, LockIdentifier, Moment, MortalEra, Origin, Perbill, Permill, Phantom, PhantomData, PreRuntime, Seal, SealV0, Signature, SignedBlock, SignerPayload, Sr25519Signature, ValidatorId, Weight, WeightMultiplier } from '@polkadot/types/interfaces/runtime'; +import { AccountId, AccountIdOf, AccountIndex, Address, AssetId, Balance, BalanceOf, Block, BlockNumber, Call, Consensus, ConsensusEngineId, Digest, DigestItem, EcdsaSignature, Ed25519Signature, Extrinsic, ExtrinsicEra, ExtrinsicPayload, ExtrinsicPayloadUnknown, ExtrinsicPayloadV1, ExtrinsicPayloadV2, ExtrinsicPayloadV3, ExtrinsicPayloadV4, ExtrinsicUnknown, ExtrinsicV1, ExtrinsicV2, ExtrinsicV3, ExtrinsicV4, Hash, Header, ImmortalEra, Index, Justification, KeyTypeId, KeyValue, LockIdentifier, Moment, MortalEra, MultiSignature, Origin, Perbill, Permill, Phantom, PhantomData, PreRuntime, Seal, SealV0, Signature, SignedBlock, SignerPayload, Sr25519Signature, ValidatorId, Weight, WeightMultiplier } from '@polkadot/types/interfaces/runtime'; import { InclusionHeight, Uncle, UncleEntryItem } from '@polkadot/types/interfaces/authorship'; import { RawAuraPreDigest } from '@polkadot/types/interfaces/aura'; import { BabeAuthorityWeight, BabeBlockWeight, BabeWeight, MaybeVrf, RawBabePreDigest, RawBabePreDigest0to159, RawBabePreDigestCompat, RawBabePreDigestPrimary, RawBabePreDigestPrimary0to159, RawBabePreDigestSecondary, RawBabePreDigestSecondary0to159, SlotNumber, VrfData, VrfProof } from '@polkadot/types/interfaces/babe'; @@ -25,7 +25,7 @@ import { TreasuryProposal } from '@polkadot/types/interfaces/treasury'; import { Multiplier } from '@polkadot/types/interfaces/txpayment'; import { DispatchResult, DispatchResultOf } from '@polkadot/types/interfaces/utility'; import { BlockAttestations, IncludedBlocks, MoreAttestations } from '@polkadot/types/interfaces/attestations'; -import { EcdsaSignature, EthereumAddress } from '@polkadot/types/interfaces/claims'; +import { EthereumAddress } from '@polkadot/types/interfaces/claims'; import { AttestedCandidate, AuctionIndex, BalanceUpload, Bidder, CandidateReceipt, CollatorId, CollatorSignature, EgressQueueRoot, HeadData, IncomingParachain, IncomingParachainDeploy, IncomingParachainFixed, LeasePeriod, LeasePeriodOf, NewBidder, ParaId, ParaIdOf, ParaInfo, ParaScheduling, ParachainDispatchOrigin, Retriable, SlotRange, SubId, UpwardMessage, ValidatorIndex, ValidityAttestation, ValidityVote, WinningData, WinningDataEntry } from '@polkadot/types/interfaces/parachains'; import { CallMetadataV0, DoubleMapTypeV3, DoubleMapTypeV4, DoubleMapTypeV5, DoubleMapTypeV6, DoubleMapTypeV7, DoubleMapTypeV8, ErrorMetadataV8, EventMetadataLatest, EventMetadataV0, EventMetadataV1, EventMetadataV2, EventMetadataV3, EventMetadataV4, EventMetadataV5, EventMetadataV6, EventMetadataV7, EventMetadataV8, FunctionArgumentMetadataLatest, FunctionArgumentMetadataV0, FunctionArgumentMetadataV1, FunctionArgumentMetadataV2, FunctionArgumentMetadataV3, FunctionArgumentMetadataV4, FunctionArgumentMetadataV5, FunctionArgumentMetadataV6, FunctionArgumentMetadataV7, FunctionArgumentMetadataV8, FunctionMetadataLatest, FunctionMetadataV0, FunctionMetadataV1, FunctionMetadataV2, FunctionMetadataV3, FunctionMetadataV4, FunctionMetadataV5, FunctionMetadataV6, FunctionMetadataV7, FunctionMetadataV8, MapTypeV0, MapTypeV2, MapTypeV3, MapTypeV4, MapTypeV5, MapTypeV6, MapTypeV7, MapTypeV8, MetadataV0, MetadataV1, ModuleConstantMetadataLatest, ModuleConstantMetadataV6, ModuleConstantMetadataV7, ModuleConstantMetadataV8, ModuleMetadataV0, ModuleMetadataV1, OuterDispatchCallV0, OuterDispatchMetadataV0, OuterEventEventMetadataEventsV0, OuterEventEventMetadataV0, OuterEventMetadataV0, PlainTypeLatest, PlainTypeV0, PlainTypeV2, PlainTypeV3, PlainTypeV4, PlainTypeV5, PlainTypeV6, PlainTypeV7, PlainTypeV8, RuntimeModuleMetadataV0, StorageEntryModifierLatest, StorageEntryModifierV6, StorageEntryModifierV7, StorageEntryModifierV8, StorageFunctionMetadataV0, StorageFunctionMetadataV1, StorageFunctionModifierV0, StorageFunctionModifierV1, StorageFunctionModifierV2, StorageFunctionModifierV3, StorageFunctionModifierV4, StorageFunctionModifierV5, StorageFunctionTypeV0, StorageFunctionTypeV1, StorageMetadataV0 } from '@polkadot/types/interfaces/metadata'; import { ApiId, ChainProperties, ExtrinsicOrHash, ExtrinsicStatus, Health, KeyValueOption, NetworkState, PeerInfo, RpcMethods, RuntimeVersion, RuntimeVersionApi, StorageChangeSet } from '@polkadot/types/interfaces/rpc'; @@ -185,6 +185,9 @@ export interface InterfaceRegistry { ExtrinsicPayloadV3: ExtrinsicPayloadV3; 'Option': Option; 'Vec': Vec; + ExtrinsicPayloadV4: ExtrinsicPayloadV4; + 'Option': Option; + 'Vec': Vec; ExtrinsicUnknown: ExtrinsicUnknown; 'Option': Option; 'Vec': Vec; @@ -197,6 +200,9 @@ export interface InterfaceRegistry { ExtrinsicV3: ExtrinsicV3; 'Option': Option; 'Vec': Vec; + ExtrinsicV4: ExtrinsicV4; + 'Option': Option; + 'Vec': Vec; Hash: Hash; 'Option': Option; 'Vec': Vec; @@ -266,6 +272,12 @@ export interface InterfaceRegistry { Sr25519Signature: Sr25519Signature; 'Option': Option; 'Vec': Vec; + EcdsaSignature: EcdsaSignature; + 'Option': Option; + 'Vec': Vec; + MultiSignature: MultiSignature; + 'Option': Option; + 'Vec': Vec; ImmortalEra: ImmortalEra; 'Option': Option; 'Vec': Vec; @@ -658,9 +670,6 @@ export interface InterfaceRegistry { MoreAttestations: MoreAttestations; 'Option': Option; 'Vec': Vec; - EcdsaSignature: EcdsaSignature; - 'Option': Option; - 'Vec': Vec; EthereumAddress: EthereumAddress; 'Option': Option; 'Vec': Vec; diff --git a/packages/types/src/interfaces/claims/definitions.ts b/packages/types/src/interfaces/claims/definitions.ts index 4b91924ad0..51b60d5425 100644 --- a/packages/types/src/interfaces/claims/definitions.ts +++ b/packages/types/src/interfaces/claims/definitions.ts @@ -4,7 +4,6 @@ export default { types: { - EcdsaSignature: '[u8; 65]', EthereumAddress: 'H160' } }; diff --git a/packages/types/src/interfaces/claims/types.ts b/packages/types/src/interfaces/claims/types.ts index 82594eaa32..f4ab7e7729 100644 --- a/packages/types/src/interfaces/claims/types.ts +++ b/packages/types/src/interfaces/claims/types.ts @@ -1,11 +1,7 @@ // Auto-generated via `yarn build:interfaces`, do not edit /* eslint-disable @typescript-eslint/no-empty-interface */ -import { Codec } from '@polkadot/types/types'; import { H160 } from '@polkadot/types/primitive'; -/** Uint8Array, Codec */ -export interface EcdsaSignature extends Uint8Array, Codec {} - /** H160 */ export interface EthereumAddress extends H160 {} diff --git a/packages/types/src/interfaces/claims/EdcsaSignature.spec.ts b/packages/types/src/interfaces/runtime/EdcsaSignature.spec.ts similarity index 100% rename from packages/types/src/interfaces/claims/EdcsaSignature.spec.ts rename to packages/types/src/interfaces/runtime/EdcsaSignature.spec.ts diff --git a/packages/types/src/interfaces/runtime/definitions.ts b/packages/types/src/interfaces/runtime/definitions.ts index aab00d9c35..8b17fed4dd 100644 --- a/packages/types/src/interfaces/runtime/definitions.ts +++ b/packages/types/src/interfaces/runtime/definitions.ts @@ -24,10 +24,12 @@ export default { ExtrinsicPayloadV1: 'GenericExtrinsicPayloadV1', ExtrinsicPayloadV2: 'GenericExtrinsicPayloadV2', ExtrinsicPayloadV3: 'GenericExtrinsicPayloadV3', + ExtrinsicPayloadV4: 'GenericExtrinsicPayloadV4', ExtrinsicUnknown: 'GenericExtrinsicUnknown', ExtrinsicV1: 'GenericExtrinsicV1', ExtrinsicV2: 'GenericExtrinsicV2', ExtrinsicV3: 'GenericExtrinsicV3', + ExtrinsicV4: 'GenericExtrinsicV4', Hash: 'H256', Header: { parentHash: 'Hash', @@ -60,12 +62,20 @@ export default { // signatures (used in block & extrinsics) Ed25519Signature: 'Signature', Sr25519Signature: 'Signature', + EcdsaSignature: '[u8; 65]', + MultiSignature: { + _enum: { + Ed25519: 'Ed25519Signature', + Sr25519: 'Sr25519Signature', + Ecdsa: 'EcdsaSignature' + } + }, // extrinsic definition ImmortalEra: 'GenericImmortalEra', MortalEra: 'GenericMortalEra', - // :: digest + // digest PreRuntime: '(ConsensusEngineId, Bytes)', SealV0: '(u64, Signature)', Seal: '(ConsensusEngineId, Bytes)', diff --git a/packages/types/src/interfaces/runtime/types.ts b/packages/types/src/interfaces/runtime/types.ts index 854588dee1..0428328860 100644 --- a/packages/types/src/interfaces/runtime/types.ts +++ b/packages/types/src/interfaces/runtime/types.ts @@ -2,8 +2,8 @@ /* eslint-disable @typescript-eslint/no-empty-interface */ import { Codec, ITuple } from '@polkadot/types/types'; -import { Compact, Struct } from '@polkadot/types/codec'; -import { Bytes, Fixed64, GenericAccountId, GenericAccountIndex, GenericAddress, GenericBlock, GenericCall, GenericConsensusEngineId, GenericDigest, GenericDigestItem, GenericExtrinsic, GenericExtrinsicEra, GenericExtrinsicPayload, GenericExtrinsicPayloadUnknown, GenericExtrinsicPayloadV1, GenericExtrinsicPayloadV2, GenericExtrinsicPayloadV3, GenericExtrinsicUnknown, GenericExtrinsicV1, GenericExtrinsicV2, GenericExtrinsicV3, GenericImmortalEra, GenericMortalEra, GenericOrigin, GenericSignerPayload, H256, H512, Null, StorageData, StorageKey, u128, u32, u64 } from '@polkadot/types/primitive'; +import { Compact, Enum, Struct } from '@polkadot/types/codec'; +import { Bytes, Fixed64, GenericAccountId, GenericAccountIndex, GenericAddress, GenericBlock, GenericCall, GenericConsensusEngineId, GenericDigest, GenericDigestItem, GenericExtrinsic, GenericExtrinsicEra, GenericExtrinsicPayload, GenericExtrinsicPayloadUnknown, GenericExtrinsicPayloadV1, GenericExtrinsicPayloadV2, GenericExtrinsicPayloadV3, GenericExtrinsicPayloadV4, GenericExtrinsicUnknown, GenericExtrinsicV1, GenericExtrinsicV2, GenericExtrinsicV3, GenericExtrinsicV4, GenericImmortalEra, GenericMortalEra, GenericOrigin, GenericSignerPayload, H256, H512, Null, StorageData, StorageKey, u128, u32, u64 } from '@polkadot/types/primitive'; /** GenericAccountId */ export interface AccountId extends GenericAccountId {} @@ -47,6 +47,9 @@ export interface Digest extends GenericDigest {} /** GenericDigestItem */ export interface DigestItem extends GenericDigestItem {} +/** Uint8Array, Codec */ +export interface EcdsaSignature extends Uint8Array, Codec {} + /** Signature */ export interface Ed25519Signature extends Signature {} @@ -71,6 +74,9 @@ export interface ExtrinsicPayloadV2 extends GenericExtrinsicPayloadV2 {} /** GenericExtrinsicPayloadV3 */ export interface ExtrinsicPayloadV3 extends GenericExtrinsicPayloadV3 {} +/** GenericExtrinsicPayloadV4 */ +export interface ExtrinsicPayloadV4 extends GenericExtrinsicPayloadV4 {} + /** GenericExtrinsicUnknown */ export interface ExtrinsicUnknown extends GenericExtrinsicUnknown {} @@ -83,6 +89,9 @@ export interface ExtrinsicV2 extends GenericExtrinsicV2 {} /** GenericExtrinsicV3 */ export interface ExtrinsicV3 extends GenericExtrinsicV3 {} +/** GenericExtrinsicV4 */ +export interface ExtrinsicV4 extends GenericExtrinsicV4 {} + /** H256 */ export interface Hash extends H256 {} @@ -124,6 +133,22 @@ export interface Moment extends u64 {} /** GenericMortalEra */ export interface MortalEra extends GenericMortalEra {} +/** Enum */ +export interface MultiSignature extends Enum { + /** 0:: Ed25519(Ed25519Signature) */ + readonly isEd25519: boolean; + /** Ed25519Signature */ + readonly asEd25519: Ed25519Signature; + /** 1:: Sr25519(Sr25519Signature) */ + readonly isSr25519: boolean; + /** Sr25519Signature */ + readonly asSr25519: Sr25519Signature; + /** 2:: Ecdsa(EcdsaSignature) */ + readonly isEcdsa: boolean; + /** EcdsaSignature */ + readonly asEcdsa: EcdsaSignature; +} + /** GenericOrigin */ export interface Origin extends GenericOrigin {} diff --git a/packages/types/src/primitive/Extrinsic/Extrinsic.ts b/packages/types/src/primitive/Extrinsic/Extrinsic.ts index c57904a889..6a7b33ccea 100644 --- a/packages/types/src/primitive/Extrinsic/Extrinsic.ts +++ b/packages/types/src/primitive/Extrinsic/Extrinsic.ts @@ -3,8 +3,8 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { FunctionMetadataLatest } from '../../interfaces/metadata/types'; -import { Address, Balance, Call, ExtrinsicUnknown, ExtrinsicV1, ExtrinsicV2, ExtrinsicV3, Index } from '../../interfaces/runtime'; -import { AnyU8a, ArgsDef, Codec, ExtrinsicPayloadValue, IExtrinsic, IHash, IKeyringPair, InterfaceTypes, SignatureOptions } from '../../types'; +import { Address, Balance, Call, EcdsaSignature, Ed25519Signature, ExtrinsicUnknown, ExtrinsicV1, ExtrinsicV2, ExtrinsicV3, ExtrinsicV4, Index, Sr25519Signature } from '../../interfaces/runtime'; +import { AnyU8a, ArgsDef, Codec, ExtrinsicPayloadValue, IExtrinsic, IKeyringPair, InterfaceTypes, SignatureOptions } from '../../types'; import { assert, isHex, isU8a, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; @@ -14,14 +14,15 @@ import Compact from '../../codec/Compact'; import { ExtrinsicValueV1 } from './v1/Extrinsic'; import { ExtrinsicValueV2 } from './v2/Extrinsic'; import { ExtrinsicValueV3 } from './v3/Extrinsic'; +import { ExtrinsicValueV4 } from './v4/Extrinsic'; import ExtrinsicEra from './ExtrinsicEra'; import { BIT_SIGNED, BIT_UNSIGNED, DEFAULT_VERSION, UNMASK_VERSION } from './constants'; // We use this type internally to cast the raw value since ExtrinsicUnknown does not actually properly // implement an Extrinsic - by design, it just throws on construction, allowing for overrides -type ExtrinsicVx = ExtrinsicV1 | ExtrinsicV2 | ExtrinsicV3; +type ExtrinsicVx = ExtrinsicV1 | ExtrinsicV2 | ExtrinsicV3 | ExtrinsicV4; -type ExtrinsicValue = ExtrinsicValueV1 | ExtrinsicValueV2 | ExtrinsicValueV3; +type ExtrinsicValue = ExtrinsicValueV1 | ExtrinsicValueV2 | ExtrinsicValueV3 | ExtrinsicValueV4; interface CreateOptions { version?: number; @@ -31,7 +32,8 @@ const VERSIONS: InterfaceTypes[] = [ 'ExtrinsicUnknown', // v0 is unknown 'ExtrinsicV1', 'ExtrinsicV2', - 'ExtrinsicV3' + 'ExtrinsicV3', + 'ExtrinsicV4' ]; /** @@ -195,9 +197,9 @@ export default class Extrinsic extends Base impl } /** - * @description The [[ExtrinsicSignature]] + * @description The actual [[EcdsaSignature]], [[Ed25519Signature]] or [[Sr25519Signature]] */ - public get signature (): IHash { + public get signature (): EcdsaSignature | Ed25519Signature | Sr25519Signature { return (this.raw as ExtrinsicVx).signature.signature; } diff --git a/packages/types/src/primitive/Extrinsic/ExtrinsicPayload.ts b/packages/types/src/primitive/Extrinsic/ExtrinsicPayload.ts index eadf5f0fe7..14b32697dc 100644 --- a/packages/types/src/primitive/Extrinsic/ExtrinsicPayload.ts +++ b/packages/types/src/primitive/Extrinsic/ExtrinsicPayload.ts @@ -2,7 +2,7 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { Balance, ExtrinsicPayloadV1, ExtrinsicPayloadV2, ExtrinsicPayloadV3, Hash, Index } from '../../interfaces/runtime'; +import { Balance, ExtrinsicPayloadV1, ExtrinsicPayloadV2, ExtrinsicPayloadV3, ExtrinsicPayloadV4, Hash, Index } from '../../interfaces/runtime'; import { ExtrinsicPayloadValue, IKeyringPair, InterfaceTypes } from '../../types'; import { u8aToHex } from '@polkadot/util'; @@ -20,13 +20,14 @@ interface ExtrinsicPayloadOptions { } // all our known types that can be returned -type ExtrinsicPayloadVx = ExtrinsicPayloadV1 | ExtrinsicPayloadV2 | ExtrinsicPayloadV3; +type ExtrinsicPayloadVx = ExtrinsicPayloadV1 | ExtrinsicPayloadV2 | ExtrinsicPayloadV3 | ExtrinsicPayloadV4; const VERSIONS: InterfaceTypes[] = [ 'ExtrinsicPayloadUnknown', // v0 is unknown 'ExtrinsicPayloadV1', 'ExtrinsicPayloadV2', - 'ExtrinsicPayloadV3' + 'ExtrinsicPayloadV3', + 'ExtrinsicPayloadV4' ]; /** @@ -68,7 +69,7 @@ export default class ExtrinsicPayload extends Base { * @description The genesis block [[Hash]] the signature applies to */ public get genesisHash (): Hash { - // NOTE only v3 + // NOTE only v3+ return (this.raw as ExtrinsicPayloadV3).genesisHash || createType('Hash'); } @@ -90,7 +91,7 @@ export default class ExtrinsicPayload extends Base { * @description The specVersion as a [[u32]] for this payload */ public get specVersion (): u32 { - // NOTE only v3 + // NOTE only v3+ return (this.raw as ExtrinsicPayloadV3).specVersion || createType('u32'); } @@ -98,7 +99,7 @@ export default class ExtrinsicPayload extends Base { * @description The [[Balance]] */ public get tip (): Compact { - // NOTE from v2 + // NOTE from v2+ return (this.raw as ExtrinsicPayloadV2).tip || createType('Compact'); } diff --git a/packages/types/src/primitive/Extrinsic/index.ts b/packages/types/src/primitive/Extrinsic/index.ts index ca0241dbbf..ae8acf9ab2 100644 --- a/packages/types/src/primitive/Extrinsic/index.ts +++ b/packages/types/src/primitive/Extrinsic/index.ts @@ -2,15 +2,23 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +// generic export { default as GenericExtrinsic } from './Extrinsic'; export { default as GenericExtrinsicEra, MortalEra as GenericMortalEra, ImmortalEra as GenericImmortalEra } from './ExtrinsicEra'; export { default as GenericExtrinsicPayload } from './ExtrinsicPayload'; export { default as GenericExtrinsicPayloadUnknown } from './ExtrinsicPayloadUnknown'; export { default as GenericExtrinsicUnknown } from './ExtrinsicUnknown'; +export { default as GenericSignerPayload } from './SignerPayload'; + +// versioned export { default as GenericExtrinsicV1 } from './v1/Extrinsic'; export { default as GenericExtrinsicPayloadV1 } from './v1/ExtrinsicPayload'; + export { default as GenericExtrinsicV2 } from './v2/Extrinsic'; export { default as GenericExtrinsicPayloadV2 } from './v2/ExtrinsicPayload'; + export { default as GenericExtrinsicV3 } from './v3/Extrinsic'; export { default as GenericExtrinsicPayloadV3 } from './v3/ExtrinsicPayload'; -export { default as GenericSignerPayload } from './SignerPayload'; + +export { default as GenericExtrinsicV4 } from './v4/Extrinsic'; +export { default as GenericExtrinsicPayloadV4 } from './v4/ExtrinsicPayload'; diff --git a/packages/types/src/primitive/Extrinsic/util.ts b/packages/types/src/primitive/Extrinsic/util.ts index 773117efeb..a0cd6b6ae2 100644 --- a/packages/types/src/primitive/Extrinsic/util.ts +++ b/packages/types/src/primitive/Extrinsic/util.ts @@ -2,15 +2,16 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { SignOptions } from '@polkadot/keyring/types'; import { IKeyringPair } from '../../types'; import { blake2AsU8a } from '@polkadot/util-crypto'; // a helper function for both types of payloads, Raw and metadata-known -export function sign (signerPair: IKeyringPair, u8a: Uint8Array): Uint8Array { +export function sign (signerPair: IKeyringPair, u8a: Uint8Array, options?: SignOptions): Uint8Array { const encoded = u8a.length > 256 ? blake2AsU8a(u8a) : u8a; - return signerPair.sign(encoded); + return signerPair.sign(encoded, options); } diff --git a/packages/types/src/primitive/Extrinsic/v1/ExtrinsicSignature.ts b/packages/types/src/primitive/Extrinsic/v1/ExtrinsicSignature.ts index ed3a68547a..b8e9c55093 100644 --- a/packages/types/src/primitive/Extrinsic/v1/ExtrinsicSignature.ts +++ b/packages/types/src/primitive/Extrinsic/v1/ExtrinsicSignature.ts @@ -13,7 +13,7 @@ import { EMPTY_U8A, IMMORTAL_ERA } from '../constants'; import ExtrinsicPayloadV1 from './ExtrinsicPayload'; /** - * @name ExtrinsicSignature + * @name ExtrinsicSignatureV1 * @description * A container for the [[Signature]] associated with a specific [[Extrinsic]] */ diff --git a/packages/types/src/primitive/Extrinsic/v2/ExtrinsicPayload.ts b/packages/types/src/primitive/Extrinsic/v2/ExtrinsicPayload.ts index b0cfeb148c..486f9cc477 100644 --- a/packages/types/src/primitive/Extrinsic/v2/ExtrinsicPayload.ts +++ b/packages/types/src/primitive/Extrinsic/v2/ExtrinsicPayload.ts @@ -19,6 +19,20 @@ const SignedExtraV2: Record = { // balances::TakeFees }; +// the base definition (excluding extras) +export const SignedPayloadBaseV2: Record = { + method: 'Bytes', + era: 'ExtrinsicEra', + nonce: 'Compact', + tip: 'Compact' +}; + +// the full definition for the payload +const SignedPayloadDefV2: Record = { + ...SignedPayloadBaseV2, + ...SignedExtraV2 +}; + /** * @name ExtrinsicPayloadV2 * @description @@ -27,13 +41,7 @@ const SignedExtraV2: Record = { */ export default class ExtrinsicPayloadV2 extends Struct { public constructor (value?: ExtrinsicPayloadValue | Uint8Array | string) { - super({ - method: 'Bytes', - era: 'ExtrinsicEra', - nonce: 'Compact', - tip: 'Compact', - ...SignedExtraV2 - }, value); + super(SignedPayloadDefV2, value); } /** diff --git a/packages/types/src/primitive/Extrinsic/v2/ExtrinsicSignature.ts b/packages/types/src/primitive/Extrinsic/v2/ExtrinsicSignature.ts index 12573c0de1..31282fb544 100644 --- a/packages/types/src/primitive/Extrinsic/v2/ExtrinsicSignature.ts +++ b/packages/types/src/primitive/Extrinsic/v2/ExtrinsicSignature.ts @@ -13,7 +13,7 @@ import { EMPTY_U8A, IMMORTAL_ERA } from '../constants'; import ExtrinsicPayloadV2 from './ExtrinsicPayload'; /** - * @name ExtrinsicSignature + * @name ExtrinsicSignatureV2 * @description * A container for the [[Signature]] associated with a specific [[Extrinsic]] */ diff --git a/packages/types/src/primitive/Extrinsic/v3/ExtrinsicPayload.ts b/packages/types/src/primitive/Extrinsic/v3/ExtrinsicPayload.ts index e869eafee0..a0d28391c3 100644 --- a/packages/types/src/primitive/Extrinsic/v3/ExtrinsicPayload.ts +++ b/packages/types/src/primitive/Extrinsic/v3/ExtrinsicPayload.ts @@ -10,6 +10,7 @@ import Struct from '../../../codec/Struct'; import Bytes from '../../../primitive/Bytes'; import u32 from '../../../primitive/U32'; import { sign } from '../util'; +import { SignedPayloadBaseV2 as SignedPayloadBaseV3 } from '../v2/ExtrinsicPayload'; // SignedExtra adds the following fields to the payload const SignedExtraV3: Record = { @@ -24,6 +25,12 @@ const SignedExtraV3: Record = { // balances::TakeFees }; +// the full definition for the payload +export const SignedPayloadDefV3: Record = { + ...SignedPayloadBaseV3, + ...SignedExtraV3 +}; + /** * @name ExtrinsicPayloadV3 * @description @@ -32,13 +39,7 @@ const SignedExtraV3: Record = { */ export default class ExtrinsicPayloadV3 extends Struct { public constructor (value?: ExtrinsicPayloadValue | Uint8Array | string) { - super({ - method: 'Bytes', - era: 'ExtrinsicEra', - nonce: 'Compact', - tip: 'Compact', - ...SignedExtraV3 - }, value); + super(SignedPayloadDefV3, value); } /** @@ -94,10 +95,6 @@ export default class ExtrinsicPayloadV3 extends Struct { * @description Sign the payload with the keypair */ public sign (signerPair: IKeyringPair): Uint8Array { - // NOTE The `toU8a(true)` argument is absolutely critical - we don't want the method (Bytes) - // to have the length prefix included. This means that the data-as-signed is un-decodable, - // but is also doesn't need the extra information, only the pure data (and is not decoded) - // ... The same applies to V1 & V1, if we have a V4, carry move this comment to latest return sign(signerPair, this.toU8a(true)); } } diff --git a/packages/types/src/primitive/Extrinsic/v3/ExtrinsicSignature.ts b/packages/types/src/primitive/Extrinsic/v3/ExtrinsicSignature.ts index 6e5859c996..bacd6269d4 100644 --- a/packages/types/src/primitive/Extrinsic/v3/ExtrinsicSignature.ts +++ b/packages/types/src/primitive/Extrinsic/v3/ExtrinsicSignature.ts @@ -30,7 +30,7 @@ export default class ExtrinsicSignatureV3 extends ExtrinsicSignatureV2 { /** * @description Generate a payload and pplies the signature from a keypair */ - public sign (method: Call, account: IKeyringPair, { blockHash, era, genesisHash, nonce, tip, runtimeVersion: { specVersion } }: SignatureOptions): IExtrinsicSignature { + public sign (method: Call, account: IKeyringPair, { blockHash, era, genesisHash, nonce, runtimeVersion: { specVersion }, tip }: SignatureOptions): IExtrinsicSignature { const signer = createType('Address', account.publicKey); const payload = new ExtrinsicPayloadV3({ blockHash, diff --git a/packages/types/src/primitive/Extrinsic/v4/Extrinsic.spec.ts b/packages/types/src/primitive/Extrinsic/v4/Extrinsic.spec.ts new file mode 100644 index 0000000000..813293b806 --- /dev/null +++ b/packages/types/src/primitive/Extrinsic/v4/Extrinsic.spec.ts @@ -0,0 +1,75 @@ +// 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 '../../../injector'; + +import BN from 'bn.js'; +import extrinsics from '@polkadot/api-metadata/extrinsics/static'; +import testingPairs from '@polkadot/keyring/testingPairs'; + +import Metadata from '../../../Metadata'; +import metadataStatic from '../../../Metadata/static'; +import Call from '../../Generic/Call'; +import Extrinsic from './Extrinsic'; + +const keyring = testingPairs({ type: 'ed25519' }, false); + +describe('ExtrinsicV4', (): void => { + beforeEach((): void => { + Call.injectMetadata(new Metadata(metadataStatic)); + }); + + it('constructs a sane Uint8Array (default)', (): void => { + expect( + new Extrinsic().toU8a() + ).toEqual(new Uint8Array([0, 0])); + }); + + it('creates a unsigned extrinsic', (): void => { + expect( + new Extrinsic( + extrinsics.balances.transfer(keyring.bob.publicKey, 6969) + ).toHex() + ).toEqual( + '0x' + + '0600' + // balance.transfer + 'ff' + + 'd7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9' + + 'e56c' + ); + }); + + it('creates a signed extrinsic', (): void => { + expect( + new Extrinsic( + extrinsics.balances.transfer(keyring.bob.publicKey, 6969) + ).sign(keyring.alice, { + blockHash: '0xec7afaf1cca720ce88c1d1b689d81f0583cc15a97d621cf046dd9abf605ef22f', + genesisHash: '0xdcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025b', + nonce: 1, + runtimeVersion: { + apis: [], + authoringVersion: new BN(123), + implName: 'test', + implVersion: new BN(123), + specName: 'test', + specVersion: new BN(123) + }, + tip: 2 + }).toHex() + ).toEqual( + '0x' + + 'ff' + + 'd172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f' + + '00' + // ed25519 + 'b8065808da3d11ddb4167afb156eafb51e8104ba792589bb443653a7fab82b90' + + 'c6530e838df06bfc8befcbbcfca7e219350cff865439b815b10b8e64ae1e9b01' + + '000408' + // era. nonce, tip + '0600' + + 'ff' + + 'd7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9' + + 'e56c' + ); + }); +}); diff --git a/packages/types/src/primitive/Extrinsic/v4/Extrinsic.ts b/packages/types/src/primitive/Extrinsic/v4/Extrinsic.ts new file mode 100644 index 0000000000..a56d8f8fa8 --- /dev/null +++ b/packages/types/src/primitive/Extrinsic/v4/Extrinsic.ts @@ -0,0 +1,101 @@ +// 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 { Address, Call } from '../../../interfaces/runtime'; +import { ExtrinsicPayloadValue, IExtrinsicImpl, IKeyringPair, SignatureOptions } from '../../../types'; +import { ExtrinsicOptions } from '../types'; + +import { isU8a } from '@polkadot/util'; + +import { createType, ClassOf } from '../../../codec/create'; +import Struct from '../../../codec/Struct'; +import ExtrinsicSignatureV4 from './ExtrinsicSignature'; + +const TRANSACTION_VERSION = 4; + +export interface ExtrinsicValueV4 { + method?: Call; + signature?: ExtrinsicSignatureV4; +} + +/** + * @name ExtrinsicV4 + * @description + * The third generation of compact extrinsics + */ +export default class ExtrinsicV4 extends Struct implements IExtrinsicImpl { + public constructor (value?: Uint8Array | ExtrinsicValueV4 | Call, { isSigned }: Partial = {}) { + super({ + signature: ExtrinsicSignatureV4, + method: 'Call' + }, ExtrinsicV4.decodeExtrinsic(value, isSigned)); + } + + public static decodeExtrinsic (value?: Call | Uint8Array | ExtrinsicValueV4, isSigned = false): ExtrinsicValueV4 { + if (!value) { + return {}; + } else if (value instanceof ExtrinsicV4) { + return value; + } else if (value instanceof ClassOf('Call')) { + return { method: value }; + } else if (isU8a(value)) { + // here we decode manually since we need to pull through the version information + const signature = new ExtrinsicSignatureV4(value, { isSigned }); + const method = createType('Call', value.subarray(signature.encodedLength)); + + return { + method, + signature + }; + } + + return value; + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + public get encodedLength (): number { + return this.toU8a().length; + } + + /** + * @description The [[Call]] this extrinsic wraps + */ + public get method (): Call { + return this.get('method') as Call; + } + + /** + * @description The [[ExtrinsicSignatureV4]] + */ + public get signature (): ExtrinsicSignatureV4 { + return this.get('signature') as ExtrinsicSignatureV4; + } + + /** + * @description The version for the signature + */ + public get version (): number { + return TRANSACTION_VERSION; + } + + /** + * @description Add an [[ExtrinsicSignatureV4]] to the extrinsic (already generated) + */ + public addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | string, payload: ExtrinsicPayloadValue | Uint8Array | string): ExtrinsicV4 { + this.signature.addSignature(signer, signature, payload); + + return this; + } + + /** + * @description Sign the extrinsic with a specific keypair + */ + public sign (account: IKeyringPair, options: SignatureOptions): ExtrinsicV4 { + this.signature.sign(this.method, account, options); + + return this; + } +} diff --git a/packages/types/src/primitive/Extrinsic/v4/ExtrinsicPayload.ts b/packages/types/src/primitive/Extrinsic/v4/ExtrinsicPayload.ts new file mode 100644 index 0000000000..2d968d29e6 --- /dev/null +++ b/packages/types/src/primitive/Extrinsic/v4/ExtrinsicPayload.ts @@ -0,0 +1,85 @@ +// 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 { Balance, ExtrinsicEra, Hash, Index } from '../../../interfaces/runtime'; +import { ExtrinsicPayloadValue, IKeyringPair } from '../../../types'; + +import Compact from '../../../codec/Compact'; +import Struct from '../../../codec/Struct'; +import Bytes from '../../../primitive/Bytes'; +import u32 from '../../../primitive/U32'; +import { sign } from '../util'; +import { SignedPayloadDefV3 as SignedPayloadDefV4 } from '../v3/ExtrinsicPayload'; + +/** + * @name ExtrinsicPayloadV4 + * @description + * A signing payload for an [[Extrinsic]]. For the final encoding, it is variable length based + * on the contents included + */ +export default class ExtrinsicPayloadV4 extends Struct { + public constructor (value?: ExtrinsicPayloadValue | Uint8Array | string) { + super(SignedPayloadDefV4, value); + } + + /** + * @description The block [[Hash]] the signature applies to (mortal/immortal) + */ + public get blockHash (): Hash { + return this.get('blockHash') as Hash; + } + + /** + * @description The [[ExtrinsicEra]] + */ + public get era (): ExtrinsicEra { + return this.get('era') as ExtrinsicEra; + } + + /** + * @description The genesis [[Hash]] the signature applies to (mortal/immortal) + */ + public get genesisHash (): Hash { + return this.get('genesisHash') as Hash; + } + + /** + * @description The [[Bytes]] contained in the payload + */ + public get method (): Bytes { + return this.get('method') as Bytes; + } + + /** + * @description The [[Index]] + */ + public get nonce (): Compact { + return this.get('nonce') as Compact; + } + + /** + * @description The specVersion for this signature + */ + public get specVersion (): u32 { + return this.get('specVersion') as u32; + } + + /** + * @description The tip [[Balance]] + */ + public get tip (): Compact { + return this.get('tip') as Compact; + } + + /** + * @description Sign the payload with the keypair + */ + public sign (signerPair: IKeyringPair): Uint8Array { + // NOTE The `toU8a(true)` argument is absolutely critical - we don't want the method (Bytes) + // to have the length prefix included. This means that the data-as-signed is un-decodable, + // but is also doesn't need the extra information, only the pure data (and is not decoded) + // ... The same applies to V1 & V1, if we have a V4, carry move this comment to latest + return sign(signerPair, this.toU8a(true), { withType: true }); + } +} diff --git a/packages/types/src/primitive/Extrinsic/v4/ExtrinsicSignature.spec.ts b/packages/types/src/primitive/Extrinsic/v4/ExtrinsicSignature.spec.ts new file mode 100644 index 0000000000..f016c60079 --- /dev/null +++ b/packages/types/src/primitive/Extrinsic/v4/ExtrinsicSignature.spec.ts @@ -0,0 +1,31 @@ +// 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 '../../../injector'; + +import ExtrinsicSignature from './ExtrinsicSignature'; + +describe('ExtrinsicSignatureV4', (): void => { + it('encodes to a sane Uint8Array', (): void => { + const u8a = new Uint8Array([ + // signer as an AccountIndex + 0x09, + // signature type + 0x01, + // signature + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + // extra stuff + 0x00, // immortal, + 0x04, // nonce, compact + 0x08 // tip, compact + ]); + + expect( + new ExtrinsicSignature(u8a, { isSigned: true }).toU8a() + ).toEqual(u8a); + }); +}); diff --git a/packages/types/src/primitive/Extrinsic/v4/ExtrinsicSignature.ts b/packages/types/src/primitive/Extrinsic/v4/ExtrinsicSignature.ts new file mode 100644 index 0000000000..3752202a97 --- /dev/null +++ b/packages/types/src/primitive/Extrinsic/v4/ExtrinsicSignature.ts @@ -0,0 +1,150 @@ +// 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 { Address, Balance, Call, EcdsaSignature, Ed25519Signature, ExtrinsicEra, Index, MultiSignature, Sr25519Signature } from '../../../interfaces/runtime'; +import { ExtrinsicPayloadValue, IExtrinsicSignature, IKeyringPair, SignatureOptions } from '../../../types'; +import { ExtrinsicSignatureOptions } from '../types'; + +import { createType } from '../../../codec/create'; +import Compact from '../../../codec/Compact'; +import Struct from '../../../codec/Struct'; +import { EMPTY_U8A, IMMORTAL_ERA } from '../constants'; +import ExtrinsicPayloadV4 from './ExtrinsicPayload'; + +/** + * @name ExtrinsicSignatureV4 + * @description + * A container for the [[Signature]] associated with a specific [[Extrinsic]] + */ +export default class ExtrinsicSignatureV4 extends Struct implements IExtrinsicSignature { + public constructor (value: ExtrinsicSignatureV4 | Uint8Array | undefined, { isSigned }: ExtrinsicSignatureOptions = {}) { + super({ + signer: 'Address', + signature: 'MultiSignature', + era: 'ExtrinsicEra', + nonce: 'Compact', + tip: 'Compact' + }, ExtrinsicSignatureV4.decodeExtrinsicSignature(value, isSigned)); + } + + public static decodeExtrinsicSignature (value: ExtrinsicSignatureV4 | Uint8Array | undefined, isSigned = false): ExtrinsicSignatureV4 | Uint8Array { + if (!value) { + return EMPTY_U8A; + } else if (value instanceof ExtrinsicSignatureV4) { + return value; + } + + return isSigned + ? value + : EMPTY_U8A; + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + public get encodedLength (): number { + return this.isSigned + ? super.encodedLength + : 0; + } + + /** + * @description `true` if the signature is valid + */ + public get isSigned (): boolean { + return !this.signature.isEmpty; + } + + /** + * @description The [[ExtrinsicEra]] (mortal or immortal) this signature applies to + */ + public get era (): ExtrinsicEra { + return this.get('era') as ExtrinsicEra; + } + + /** + * @description The [[Index]] for the signature + */ + public get nonce (): Compact { + return this.get('nonce') as Compact; + } + + /** + * @description The actual [[EcdsaSignature]], [[Ed25519Signature]] or [[Sr25519Signature]] + */ + public get signature (): EcdsaSignature | Ed25519Signature | Sr25519Signature { + return this.multiSignature.value as Sr25519Signature; + } + + /** + * @description The raw [[MultiSignature]] + */ + public get multiSignature (): MultiSignature { + return this.get('signature') as MultiSignature; + } + + /** + * @description The [[Address]] that signed + */ + public get signer (): Address { + return this.get('signer') as Address; + } + + /** + * @description The [[Balance]] tip + */ + public get tip (): Compact { + return this.get('tip') as Compact; + } + + protected injectSignature (signer: Address, signature: MultiSignature, { era, nonce, tip }: ExtrinsicPayloadV4): IExtrinsicSignature { + this.set('era', era); + this.set('nonce', nonce); + this.set('signer', signer); + this.set('signature', signature); + this.set('tip', tip); + + return this; + } + + /** + * @description Adds a raw signature + */ + public addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | string, payload: ExtrinsicPayloadValue | Uint8Array | string): IExtrinsicSignature { + return this.injectSignature( + createType('Address', signer), + createType('MultiSignature', signature), + new ExtrinsicPayloadV4(payload) + ); + } + + /** + * @description Generate a payload and applies the signature from a keypair + */ + public sign (method: Call, account: IKeyringPair, { blockHash, era, genesisHash, nonce, runtimeVersion: { specVersion }, tip }: SignatureOptions): IExtrinsicSignature { + const signer = createType('Address', account.publicKey); + const payload = new ExtrinsicPayloadV4({ + blockHash, + era: era || IMMORTAL_ERA, + genesisHash, + method: method.toHex(), + nonce, + specVersion, + tip: tip || 0 + }); + const signature = createType('MultiSignature', payload.sign(account)); + + return this.injectSignature(signer, signature, payload); + } + + /** + * @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) + */ + public toU8a (isBare?: boolean): Uint8Array { + return this.isSigned + ? super.toU8a(isBare) + : EMPTY_U8A; + } +} diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index a3ace95bb2..8aa02784d5 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -2,8 +2,9 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { SignOptions } from '@polkadot/keyring/types'; import { FunctionMetadataLatest } from './interfaces/metadata'; -import { Balance, Index } from './interfaces/runtime'; +import { Balance, EcdsaSignature, Ed25519Signature, Index, Sr25519Signature } from './interfaces/runtime'; import BN from 'bn.js'; @@ -34,7 +35,7 @@ export type ModulesWithCalls = Record; export interface IKeyringPair { address: string; publicKey: Uint8Array; - sign: (data: Uint8Array) => Uint8Array; + sign: (data: Uint8Array, options?: SignOptions) => Uint8Array; } // eslint-disable-next-line @typescript-eslint/no-empty-interface @@ -174,7 +175,7 @@ interface ExtrinsicSignatureBase { readonly isSigned: boolean; readonly era: IExtrinsicEra; readonly nonce: Compact; - readonly signature: IHash; + readonly signature: EcdsaSignature | Ed25519Signature | Sr25519Signature; readonly signer: Address; readonly tip: Compact; } diff --git a/yarn.lock b/yarn.lock index 34bd408e03..551433042b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -781,7 +781,7 @@ core-js "^2.6.5" regenerator-runtime "^0.13.2" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.6.3": version "7.6.3" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.6.3.tgz#935122c74c73d2240cafd32ddb5fc2a6cd35cf1f" integrity sha512-kq6anf9JGjW8Nt5rYfEuGRaEAaH1mkv3Bbu6rYvLOpPh/RusSJXuKPEAoZ7L7gybZkchE8+NV5g9vKF4AGAtsA== @@ -1730,10 +1730,11 @@ integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== "@octokit/endpoint@^5.1.0": - version "5.4.1" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-5.4.1.tgz#8f4c747d6cf8f352683d35a7fe8664db487cb730" - integrity sha512-iwn46orWg3F4iqIzAVRfbzhnROyx7BQ7zJE0B7SEeaMIBvk3qmWtswtRk14QkMNUuNiCHQ6mAM00VJxWqrdM1g== + version "5.5.0" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-5.5.0.tgz#d7e7960ffe39096cb67062f07efa84db52b20edb" + integrity sha512-TXYS6zXeBImNB9BVj+LneMDqXX+H0exkOpyXobvp92O3B1348QsKnNioISFKgOMsb3ibZvQGwCdpiwQd3KAjIA== dependencies: + "@octokit/types" "^1.0.0" is-plain-object "^3.0.0" universal-user-agent "^4.0.0" @@ -1764,9 +1765,9 @@ universal-user-agent "^4.0.0" "@octokit/rest@^16.28.4": - version "16.33.1" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.33.1.tgz#19229f5fd28d8e071644d37c249775ee40add433" - integrity sha512-lOQ+fJZwkeJ/1PRTdnY1uNja01aKOMioRhQfZtei64gZMXIX3EAfF4koMQMvoLFwsnVBu3ifj1JW1WAAKdXcnA== + version "16.34.0" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.34.0.tgz#8703e46d7e9f6aec24a7e591b073f325ca13f6e2" + integrity sha512-EBe5qMQQOZRuezahWCXCnSe0J6tAqrW2hrEH9U8esXzKor1+HUDf8jgImaZf5lkTyWCQA296x9kAH5c0pxEgVQ== dependencies: "@octokit/request" "^5.2.0" "@octokit/request-error" "^1.0.2" @@ -1781,6 +1782,13 @@ once "^1.4.0" universal-user-agent "^4.0.0" +"@octokit/types@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-1.0.0.tgz#13d2361123cb06bead36ba836b0639c5cbd15add" + integrity sha512-u51RhPTdCJgZQnU4TuKiqHcAxINsvIkQDZdbF4wSJy3g+DH7X/SmYp1kJE6INRD8hh2wEeFmRke7h1j6Ed3e+w== + dependencies: + "@types/node" "^12.11.1" + "@polkadot/dev@^0.32.0-beta.11": version "0.32.0-beta.11" resolved "https://registry.yarnpkg.com/@polkadot/dev/-/dev-0.32.0-beta.11.tgz#930332443807db0c0f692d959ca18c7a84037be1" @@ -1827,14 +1835,14 @@ typescript "^3.6.4" vuepress "^1.2.0" -"@polkadot/keyring@^1.5.1": - version "1.5.1" - resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-1.5.1.tgz#74091e246c9ce4bc15960f15afffdb3bb8e208c7" - integrity sha512-sp1uNK5FKs8Vnlvl/0NvlRZfu4KcCWtyIL6YFa3oH1iYnAmwEV+XekQwTNe4wXWJVXdjLi1Xvjxx2yGxQkWolw== +"@polkadot/keyring@^1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-1.6.1.tgz#35966afba142b647b31767e3a349652947c57a90" + integrity sha512-ortPbweoaANKTKZR0JDbO796U6B3JGjj87yZD+ghlFRUWjMjlbP+LOWnfNyXHcI87yxNC23nK3YDv1KCl0eDUQ== dependencies: - "@babel/runtime" "^7.6.2" - "@polkadot/util" "^1.5.1" - "@polkadot/util-crypto" "^1.5.1" + "@babel/runtime" "^7.6.3" + "@polkadot/util" "^1.6.1" + "@polkadot/util-crypto" "^1.6.1" "@polkadot/ts@^0.1.82": version "0.1.82" @@ -1843,13 +1851,13 @@ dependencies: "@types/chrome" "^0.0.91" -"@polkadot/util-crypto@^1.5.1": - version "1.5.1" - resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-1.5.1.tgz#18de631265b5cf13ff8b9a00b747de56b72eb38d" - integrity sha512-m7pzRW1ltmgTMyFnpnDq0SIaE+2pyT0yzzjswNLJzo8r1MAI7eT7N5s89KoPLmbsi37Nxuc0T/JKs1Du6KvMrg== +"@polkadot/util-crypto@^1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-1.6.1.tgz#6d924c5eaecbf98f971da1aac69fb60b17a4bf55" + integrity sha512-Dbo5qsIGKO82HpkysFKh9GDY0w6O6tX9h5HPRDy4KIaTaCo3H5QTmxit+hPU9n3PIHsxGhnyPq8q1i2wD/iWYQ== dependencies: - "@babel/runtime" "^7.6.2" - "@polkadot/util" "^1.5.1" + "@babel/runtime" "^7.6.3" + "@polkadot/util" "^1.6.1" "@polkadot/wasm-crypto" "^0.14.1" "@types/bip39" "^2.4.2" "@types/bs58" "^4.0.0" @@ -1865,12 +1873,12 @@ tweetnacl "^1.0.1" xxhashjs "^0.2.2" -"@polkadot/util@^1.5.1": - version "1.5.1" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-1.5.1.tgz#acc6d1fd041959a1a781ee2a4d0fc8dae99d4e1a" - integrity sha512-eK0FT251GZ/pZY4HjVm4E14w02cLzJLriY8FAqM+hKoNkkqN/0CIijZWMS89e91emAxQxFbQnJqFQyA6e8e3Rw== +"@polkadot/util@^1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-1.6.1.tgz#8110c12a1670e564c5d6a1c19347cbb0dba8a3ce" + integrity sha512-uKRfapAXwZMcWQ24MdV+zg8jrYtidbKxJhUbq0G77Yy27d45OID8d+JcI3Q+d6d2pmC/h733iSeCqdiLYCKqMw== dependencies: - "@babel/runtime" "^7.6.2" + "@babel/runtime" "^7.6.3" "@types/bn.js" "^4.11.5" bn.js "^4.11.8" camelcase "^5.3.1" @@ -2041,6 +2049,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.11.2.tgz#75ba3beda30d690b89a5089ca1c6e8e386150b76" integrity sha512-dsfE4BHJkLQW+reOS6b17xhZ/6FB1rB8eRRvO08nn5o+voxf3i74tuyFWNH6djdfgX7Sm5s6LD8t6mJug4dpDw== +"@types/node@^12.11.1": + version "12.11.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.11.6.tgz#2f8d551aef252de78f42acdccd53f5a8ce0cac4d" + integrity sha512-4uPUyY1Aofo1YzoypalYHNd2SnKYxH2b6LzXwpryZCJKA2XlagZSynXx5C8sfPH0r1cSltUpaVHV2q5sYXschQ== + "@types/pbkdf2@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.0.0.tgz#5d9ca5f12a78a08cc89ad72883ad4a30af359229" @@ -3293,9 +3306,9 @@ bs58@^4.0.1: base-x "^3.0.2" bser@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.0.tgz#65fc784bf7f87c009b973c12db6546902fa9c7b5" - integrity sha512-8zsjWrQkkBoLK6uxASk1nJ2SKv97ltiGDo6A3wA0/yRPz+CwmEyDo0hUrhIuukG2JHpAl3bvFIixw2/3Hi0DOg== + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== dependencies: node-int64 "^0.4.0" @@ -6009,9 +6022,9 @@ good-listener@^1.2.2: delegate "^3.1.2" graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02" - integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q== + version "4.2.3" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" + integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== gray-matter@^4.0.1: version "4.0.2" @@ -6335,7 +6348,7 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= -https-proxy-agent@^2.2.1: +https-proxy-agent@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.3.tgz#fb6cd98ed5b9c35056b5a73cd01a8a721d7193d1" integrity sha512-Ytgnz23gm2DVftnzqRRz2dOXZbGd2uiajSw/95bPp6v53zPRspQjLm/AfBgqbJ2qfeRXWIOMVLpp86+/5yX39Q== @@ -7840,9 +7853,9 @@ lru-queue@0.1: es5-ext "~0.10.2" lunr@^2.3.6: - version "2.3.7" - resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.7.tgz#05ccf3af9d0e169b8f432c97e02fc1bdf3f36343" - integrity sha512-HjFSiy0Y0qZoW5OA1I6qBi7OnsDdqQnaUr03jhorh30maQoaP+4lQCKklYE3Nq3WJMSUfuBl6N+bKY5wxCb9hw== + version "2.3.8" + resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.8.tgz#a8b89c31f30b5a044b97d2d28e2da191b6ba2072" + integrity sha512-oxMeX/Y35PNFuZoHp+jUj5OSEmLCaIH4KTFJh7a93cHBoFmpw2IoPs22VIz7vyO2YUnx2Tn9dzIwO2P/4quIRg== macos-release@^2.2.0: version "2.3.0" @@ -7865,15 +7878,15 @@ make-dir@^2.0.0, make-dir@^2.1.0: semver "^5.6.0" make-fetch-happen@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-5.0.0.tgz#a8e3fe41d3415dd656fe7b8e8172e1fb4458b38d" - integrity sha512-nFr/vpL1Jc60etMVKeaLOqfGjMMb3tAHFVJWxHOFCFS04Zmd7kGlMxo0l1tzfhoQje0/UPnd0X8OeGUiXXnfPA== + version "5.0.1" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-5.0.1.tgz#fac65400ab5f7a9c001862a3e9b0f417f0840175" + integrity sha512-b4dfaMvUDR67zxUq1+GN7Ke9rH5WvGRmoHuMH7l+gmUCR2tCXFP6mpeJ9Dp+jB6z8mShRopSf1vLRBhRs8Cu5w== dependencies: agentkeepalive "^3.4.1" cacache "^12.0.0" http-cache-semantics "^3.8.1" http-proxy-agent "^2.1.0" - https-proxy-agent "^2.2.1" + https-proxy-agent "^2.2.3" lru-cache "^5.1.1" mississippi "^3.0.0" node-fetch-npm "^2.0.2" @@ -8444,9 +8457,9 @@ no-case@^2.2.0: lower-case "^1.1.1" nock@*, nock@^11.4.0: - version "11.4.0" - resolved "https://registry.yarnpkg.com/nock/-/nock-11.4.0.tgz#68c2f9cf533f13ecb1a43e956823dbfba5593d17" - integrity sha512-UrVEbEAvhyDoUttrS0fv3znhZ5nEJvlxqgmrC6Gb2Mf9cFci65RMK17e6EjDDQB57g5iwZw1TFnVvyeL0eUlhQ== + version "11.6.0" + resolved "https://registry.yarnpkg.com/nock/-/nock-11.6.0.tgz#481961e0cf78794f200ea71e25d948d668446717" + integrity sha512-9ocFR68CxS6nf2XtQNpdSh5n4QQSKl87DhXgLnHO/RD4CsGThFtu8/QG6myHTnrUHRE6JSKpiGjLJdRe2ZSlIA== dependencies: chai "^4.1.2" debug "^4.1.0" @@ -9308,7 +9321,7 @@ pn@^1.1.0: resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== -portfinder@^1.0.13, portfinder@^1.0.24: +portfinder@^1.0.13, portfinder@^1.0.25: version "1.0.25" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.25.tgz#254fd337ffba869f4b9d37edc298059cb4d35eca" integrity sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg== @@ -9975,9 +9988,9 @@ rc@^1.2.7: strip-json-comments "~2.0.1" react-is@^16.8.4: - version "16.10.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.10.2.tgz#984120fd4d16800e9a738208ab1fba422d23b5ab" - integrity sha512-INBT1QEgtcCCgvccr5/86CfD71fw9EPmDxgiJX4I2Ddr6ZsV6iFXsuby+qWJPtmNuMY0zByTsG4468P7nHuNWA== + version "16.11.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.11.0.tgz#b85dfecd48ad1ce469ff558a882ca8e8313928fa" + integrity sha512-gbBVYR2p8mnriqAwWx9LbuUrShnAuSCNnuPGyc7GJrMVQtPDAh8iLpv7FRuMPFb56KkaVZIYSz1PrjI9q0QPCw== read-cmd-shim@^1.0.1: version "1.0.4" @@ -12099,9 +12112,9 @@ webpack-dev-middleware@^3.7.2: webpack-log "^2.0.0" webpack-dev-server@^3.5.1: - version "3.8.2" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.8.2.tgz#3292427bf6510da9a3ac2d500b924a4197667ff9" - integrity sha512-0xxogS7n5jHDQWy0WST0q6Ykp7UGj4YvWh+HVN71JoE7BwPxMZrwgraBvmdEMbDVMBzF0u+mEzn8TQzBm5NYJQ== + version "3.9.0" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.9.0.tgz#27c3b5d0f6b6677c4304465ac817623c8b27b89c" + integrity sha512-E6uQ4kRrTX9URN9s/lIbqTAztwEPdvzVrcmHE8EQ9YnuT9J8Es5Wrd8n9BKg1a0oZ5EgEke/EQFgUsp18dSTBw== dependencies: ansi-html "0.0.7" bonjour "^3.5.0" @@ -12121,7 +12134,7 @@ webpack-dev-server@^3.5.1: loglevel "^1.6.4" opn "^5.5.0" p-retry "^3.0.1" - portfinder "^1.0.24" + portfinder "^1.0.25" schema-utils "^1.0.0" selfsigned "^1.10.7" semver "^6.3.0"