From 4db33b6caf10b807d110d20fa504a705db29eede Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 12 Nov 2020 19:31:55 +0100 Subject: [PATCH] Configurable extrinsic signature (#2845) * Configurable extrinsic signature * With Address override as well --- .../scripts/polkadot-types-chain-info.js | 7 +- .../scripts/polkadot-types-from-chain.js | 5 +- .../scripts/polkadot-types-from-defs.js | 5 +- .../polkadot-types-internal-interfaces.js | 5 +- .../polkadot-types-internal-metadata.js | 5 +- packages/types/src/augment/registry.ts | 8 +- .../src/extrinsic/v4/ExtrinsicPayload.ts | 12 ++- .../extrinsic/v4/ExtrinsicSignature.spec.ts | 80 ++++++++++++++++++- .../src/extrinsic/v4/ExtrinsicSignature.ts | 31 ++++--- .../src/interfaces/extrinsics/definitions.ts | 2 + .../types/src/interfaces/extrinsics/types.ts | 6 ++ 11 files changed, 140 insertions(+), 26 deletions(-) diff --git a/packages/typegen/scripts/polkadot-types-chain-info.js b/packages/typegen/scripts/polkadot-types-chain-info.js index bb5467786b..66d5e7986d 100755 --- a/packages/typegen/scripts/polkadot-types-chain-info.js +++ b/packages/typegen/scripts/polkadot-types-chain-info.js @@ -1,7 +1,8 @@ #!/usr/bin/env node // Copyright 2017-2020 @polkadot/typegen authors & contributors // SPDX-License-Identifier: Apache-2.0 -/* eslint-disable @typescript-eslint/no-var-requires */ + +/* eslint-disable @typescript-eslint/no-var-requires,sort-keys */ let main; @@ -14,13 +15,13 @@ try { ['module-resolver', { alias: { '^@polkadot/api-derive(.*)': './packages/api-derive/src\\1', - // eslint-disable-next-line sort-keys + '^@polkadot/api/package.json': './packages/api/package.json', '^@polkadot/api(.*)': './packages/api/src/\\1', '^@polkadot/metadata(.*)': './packages/metadata/src\\1', '^@polkadot/rpc-core(.*)': './packages/rpc-core/src\\1', '^@polkadot/rpc-provider(.*)': './packages/rpc-provider/src\\1', '^@polkadot/types-known(.*)': './packages/types-known/src\\1', - // eslint-disable-next-line sort-keys + '^@polkadot/types/package.json': './packages/types/package.json', '^@polkadot/types(.*)': './packages/types/src\\1' } }] diff --git a/packages/typegen/scripts/polkadot-types-from-chain.js b/packages/typegen/scripts/polkadot-types-from-chain.js index 44425f8d35..612cdc1a20 100755 --- a/packages/typegen/scripts/polkadot-types-from-chain.js +++ b/packages/typegen/scripts/polkadot-types-from-chain.js @@ -1,7 +1,8 @@ #!/usr/bin/env node // Copyright 2017-2020 @polkadot/typegen authors & contributors // SPDX-License-Identifier: Apache-2.0 -/* eslint-disable @typescript-eslint/no-var-requires */ + +/* eslint-disable @typescript-eslint/no-var-requires,sort-keys */ let main; @@ -15,7 +16,7 @@ try { alias: { '^@polkadot/metadata(.*)': './packages/metadata/src\\1', '^@polkadot/types-known(.*)': './packages/types-known/src\\1', - // eslint-disable-next-line sort-keys + '^@polkadot/types/package.json': './packages/types/package.json', '^@polkadot/types(.*)': './packages/types/src\\1' } }] diff --git a/packages/typegen/scripts/polkadot-types-from-defs.js b/packages/typegen/scripts/polkadot-types-from-defs.js index 79bd726ecf..7931a949fd 100755 --- a/packages/typegen/scripts/polkadot-types-from-defs.js +++ b/packages/typegen/scripts/polkadot-types-from-defs.js @@ -1,7 +1,8 @@ #!/usr/bin/env node // Copyright 2017-2020 @polkadot/typegen authors & contributors // SPDX-License-Identifier: Apache-2.0 -/* eslint-disable @typescript-eslint/no-var-requires */ + +/* eslint-disable @typescript-eslint/no-var-requires,sort-keys */ let main; @@ -16,7 +17,7 @@ try { '^@polkadot/metadata(.*)': './packages/metadata/src\\1', '^@polkadot/typegen(.*)': './packages/typegen/src\\1', '^@polkadot/types-known(.*)': './packages/types-known/src\\1', - // eslint-disable-next-line sort-keys + '^@polkadot/types/package.json': './packages/types/package.json', '^@polkadot/types(.*)': './packages/types/src\\1' } }] diff --git a/packages/typegen/scripts/polkadot-types-internal-interfaces.js b/packages/typegen/scripts/polkadot-types-internal-interfaces.js index 43529280bd..0ba3d6dbe4 100755 --- a/packages/typegen/scripts/polkadot-types-internal-interfaces.js +++ b/packages/typegen/scripts/polkadot-types-internal-interfaces.js @@ -1,7 +1,8 @@ #!/usr/bin/env node // Copyright 2017-2020 @polkadot/typegen authors & contributors // SPDX-License-Identifier: Apache-2.0 -/* eslint-disable @typescript-eslint/no-var-requires */ + +/* eslint-disable @typescript-eslint/no-var-requires,sort-keys */ let main; @@ -15,7 +16,7 @@ try { alias: { '^@polkadot/metadata(.*)': './packages/metadata/src\\1', '^@polkadot/types-known(.*)': './packages/types-known/src\\1', - // eslint-disable-next-line sort-keys + '^@polkadot/types/package.json': './packages/types/package.json', '^@polkadot/types(.*)': './packages/types/src\\1' } }] diff --git a/packages/typegen/scripts/polkadot-types-internal-metadata.js b/packages/typegen/scripts/polkadot-types-internal-metadata.js index 29e3e62249..6c7ac3099e 100755 --- a/packages/typegen/scripts/polkadot-types-internal-metadata.js +++ b/packages/typegen/scripts/polkadot-types-internal-metadata.js @@ -1,7 +1,8 @@ #!/usr/bin/env node // Copyright 2017-2020 @polkadot/typegen authors & contributors // SPDX-License-Identifier: Apache-2.0 -/* eslint-disable @typescript-eslint/no-var-requires */ + +/* eslint-disable @typescript-eslint/no-var-requires,sort-keys */ let main; @@ -15,7 +16,7 @@ try { alias: { '^@polkadot/metadata(.*)': './packages/metadata/src\\1', '^@polkadot/types-known(.*)': './packages/types-known/src\\1', - // eslint-disable-next-line sort-keys + '^@polkadot/types/package.json': './packages/types/package.json', '^@polkadot/types(.*)': './packages/types/src\\1' } }] diff --git a/packages/types/src/augment/registry.ts b/packages/types/src/augment/registry.ts index 1ea8185baa..0ab5f65a15 100644 --- a/packages/types/src/augment/registry.ts +++ b/packages/types/src/augment/registry.ts @@ -21,7 +21,7 @@ import { ApprovalFlag, DefunctVoter, Renouncing, SetIndex, Vote, VoteIndex, Vote import { CreatedBlock, ImportedAux } from '@polkadot/types/interfaces/engine'; import { EthereumAccountId, EthereumLookupSource } from '@polkadot/types/interfaces/ethereum'; import { Account, Log, Vicinity } from '@polkadot/types/interfaces/evm'; -import { EcdsaSignature, Ed25519Signature, Extrinsic, ExtrinsicEra, ExtrinsicPayload, ExtrinsicPayloadUnknown, ExtrinsicPayloadV4, ExtrinsicSignatureV4, ExtrinsicUnknown, ExtrinsicV4, ImmortalEra, MortalEra, MultiSignature, Signature, SignerPayload, Sr25519Signature } from '@polkadot/types/interfaces/extrinsics'; +import { AnySignature, EcdsaSignature, Ed25519Signature, Extrinsic, ExtrinsicEra, ExtrinsicPayload, ExtrinsicPayloadUnknown, ExtrinsicPayloadV4, ExtrinsicSignature, ExtrinsicSignatureV4, ExtrinsicUnknown, ExtrinsicV4, ImmortalEra, MortalEra, MultiSignature, Signature, SignerPayload, Sr25519Signature } from '@polkadot/types/interfaces/extrinsics'; import { AssetOptions, Owner, PermissionLatest, PermissionVersions, PermissionsV1 } from '@polkadot/types/interfaces/genericAsset'; import { AuthorityIndex, AuthorityList, AuthorityWeight, EncodedFinalityProofs, GrandpaEquivocation, GrandpaEquivocationProof, GrandpaEquivocationValue, GrandpaPrevote, JustificationNotification, KeyOwnerProof, NextAuthority, PendingPause, PendingResume, Precommits, Prevotes, ReportedRoundStates, RoundState, SetId, StoredPendingChange, StoredState } from '@polkadot/types/interfaces/grandpa'; import { IdentityFields, IdentityInfo, IdentityInfoAdditional, IdentityJudgement, RegistrarIndex, RegistrarInfo, Registration, RegistrationJudgement } from '@polkadot/types/interfaces/identity'; @@ -715,6 +715,9 @@ declare module '@polkadot/types/types/registry' { ExtrinsicPayload: ExtrinsicPayload; 'Option': Option; 'Vec': Vec; + ExtrinsicSignature: ExtrinsicSignature; + 'Option': Option; + 'Vec': Vec; ExtrinsicV4: ExtrinsicV4; 'Option': Option; 'Vec': Vec; @@ -736,6 +739,9 @@ declare module '@polkadot/types/types/registry' { MortalEra: MortalEra; 'Option': Option; 'Vec': Vec; + AnySignature: AnySignature; + 'Option': Option; + 'Vec': Vec; MultiSignature: MultiSignature; 'Option': Option; 'Vec': Vec; diff --git a/packages/types/src/extrinsic/v4/ExtrinsicPayload.ts b/packages/types/src/extrinsic/v4/ExtrinsicPayload.ts index 2c7e283ba1..9c1fde3fb4 100644 --- a/packages/types/src/extrinsic/v4/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/v4/ExtrinsicPayload.ts @@ -1,11 +1,13 @@ // Copyright 2017-2020 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { SignOptions } from '@polkadot/keyring/types'; import { ExtrinsicEra } from '../../interfaces/extrinsics'; import { Balance, Hash, Index } from '../../interfaces/runtime'; import { ExtrinsicPayloadValue, IKeyringPair, Registry } from '../../types'; import { Compact } from '../../codec/Compact'; +import { Enum } from '../../codec/Enum'; import { Struct } from '../../codec/Struct'; import { Bytes } from '../../primitive/Bytes'; import { u32 } from '../../primitive/U32'; @@ -18,12 +20,20 @@ import { sign } from '../util'; * on the contents included */ export class GenericExtrinsicPayloadV4 extends Struct { + #signOptions: SignOptions; + constructor (registry: Registry, value?: ExtrinsicPayloadValue | Uint8Array | string) { super(registry, { method: 'Bytes', ...registry.getSignedExtensionTypes(), ...registry.getSignedExtensionExtra() }, value); + + // Do detection for the type of extrinsic, in the case of MultiSignature this is an + // enum, in the case of AnySignature, this is a Hash only (may be 64 or 65 bytes) + this.#signOptions = { + withType: registry.createType('ExtrinsicSignature') instanceof Enum + }; } /** @@ -90,6 +100,6 @@ export class GenericExtrinsicPayloadV4 extends Struct { // 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..V3, if we have a V5, carry move this comment to latest - return sign(this.registry, signerPair, this.toU8a({ method: true }), { withType: true }); + return sign(this.registry, signerPair, this.toU8a({ method: true }), this.#signOptions); } } diff --git a/packages/types/src/extrinsic/v4/ExtrinsicSignature.spec.ts b/packages/types/src/extrinsic/v4/ExtrinsicSignature.spec.ts index b3d4630537..e6632fda62 100644 --- a/packages/types/src/extrinsic/v4/ExtrinsicSignature.spec.ts +++ b/packages/types/src/extrinsic/v4/ExtrinsicSignature.spec.ts @@ -1,13 +1,35 @@ // Copyright 2017-2020 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { createTestPairs } from '@polkadot/keyring/testingPairs'; +import { Metadata } from '@polkadot/metadata'; +import metadataStatic from '@polkadot/metadata/static'; +import { BN_ZERO } from '@polkadot/util'; + import { TypeRegistry } from '../../create'; import { GenericExtrinsicSignatureV4 as ExtrinsicSignature } from '.'; -describe('ExtrinsicSignatureV4', (): void => { - const registry = new TypeRegistry(); +const signOptions = { + blockHash: '0x1234567890123456789012345678901234567890123456789012345678901234', + genesisHash: '0x1234567890123456789012345678901234567890123456789012345678901234', + nonce: '0x69', + runtimeVersion: { + apis: [], + authoringVersion: BN_ZERO, + implName: String('test'), + implVersion: BN_ZERO, + specName: String('test'), + specVersion: BN_ZERO, + transactionVersion: BN_ZERO + } +}; + +describe('ExtrinsicSignatureV4', (): void => { + const pairs = createTestPairs({ type: 'ed25519' }); + + it('encodes to a sane Uint8Array (default)', (): void => { + const registry = new TypeRegistry(); - it('encodes to a sane Uint8Array', (): void => { const u8a = new Uint8Array([ // signer as an AccountIndex 0x09, @@ -28,4 +50,56 @@ describe('ExtrinsicSignatureV4', (): void => { new ExtrinsicSignature(registry, u8a, { isSigned: true }).toU8a() ).toEqual(u8a); }); + + it('fake signs default', (): void => { + const registry = new TypeRegistry(); + const metadata = new Metadata(registry, metadataStatic); + + registry.setMetadata(metadata); + + expect( + new ExtrinsicSignature(registry, undefined).signFake( + registry.createType('Call'), + pairs.alice.address, + signOptions + ).toHex() + ).toEqual( + '0x' + + 'ff' + + 'd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d' + + '01' + + '4242424242424242424242424242424242424242424242424242424242424242' + + '4242424242424242424242424242424242424242424242424242424242424242' + + '00a50100' + ); + }); + + it('fake signs default', (): void => { + const registry = new TypeRegistry(); + const metadata = new Metadata(registry, metadataStatic); + + registry.setMetadata(metadata); + registry.register({ + Address: 'AccountId', + ExtrinsicSignature: 'AnySignature' + }); + + expect( + new ExtrinsicSignature(registry, undefined).signFake( + registry.createType('Call'), + pairs.alice.address, + signOptions + ).toHex() + ).toEqual( + '0x' + + // Address = AccountId + // 'ff' + + 'd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d' + + // This is a prefix-less signature, anySignture as opposed to Multi above + // '01' + + '4242424242424242424242424242424242424242424242424242424242424242' + + '4242424242424242424242424242424242424242424242424242424242424242' + + '00a50100' + ); + }); }); diff --git a/packages/types/src/extrinsic/v4/ExtrinsicSignature.ts b/packages/types/src/extrinsic/v4/ExtrinsicSignature.ts index 4b27e1f5d6..d868769d36 100644 --- a/packages/types/src/extrinsic/v4/ExtrinsicSignature.ts +++ b/packages/types/src/extrinsic/v4/ExtrinsicSignature.ts @@ -1,7 +1,7 @@ // Copyright 2017-2020 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { EcdsaSignature, Ed25519Signature, ExtrinsicEra, MultiSignature, Sr25519Signature } from '../../interfaces/extrinsics'; +import { EcdsaSignature, Ed25519Signature, ExtrinsicEra, ExtrinsicSignature, Sr25519Signature } from '../../interfaces/extrinsics'; import { Address, Balance, Call, Index } from '../../interfaces/runtime'; import { ExtrinsicPayloadValue, IExtrinsicSignature, IKeyringPair, Registry, SignatureOptions } from '../../types'; import { ExtrinsicSignatureOptions } from '../types'; @@ -9,23 +9,33 @@ import { ExtrinsicSignatureOptions } from '../types'; import { u8aConcat } from '@polkadot/util'; import { Compact } from '../../codec/Compact'; +import { Enum } from '../../codec/Enum'; import { Struct } from '../../codec/Struct'; import { EMPTY_U8A, IMMORTAL_ERA } from '../constants'; import { GenericExtrinsicPayloadV4 } from './ExtrinsicPayload'; +const FAKE_NONE = new Uint8Array(); +const FAKE_SOME = new Uint8Array([1]); + /** * @name GenericExtrinsicSignatureV4 * @description * A container for the [[Signature]] associated with a specific [[Extrinsic]] */ export class GenericExtrinsicSignatureV4 extends Struct implements IExtrinsicSignature { + #fakePrefix: Uint8Array; + constructor (registry: Registry, value: GenericExtrinsicSignatureV4 | Uint8Array | undefined, { isSigned }: ExtrinsicSignatureOptions = {}) { super(registry, { signer: 'Address', // eslint-disable-next-line sort-keys - signature: 'MultiSignature', + signature: 'ExtrinsicSignature', ...registry.getSignedExtensionTypes() }, GenericExtrinsicSignatureV4.decodeExtrinsicSignature(value, isSigned)); + + this.#fakePrefix = registry.createType('ExtrinsicSignature') instanceof Enum + ? FAKE_SOME + : FAKE_NONE; } /** @internal */ @@ -75,14 +85,15 @@ export class GenericExtrinsicSignatureV4 extends Struct implements IExtrinsicSig * @description The actual [[EcdsaSignature]], [[Ed25519Signature]] or [[Sr25519Signature]] */ public get signature (): EcdsaSignature | Ed25519Signature | Sr25519Signature { - return this.multiSignature.value as Sr25519Signature; + // the second case here is when we don't have an enum signature, treat as raw + return (this.multiSignature.value || this.multiSignature) as Sr25519Signature; } /** - * @description The raw [[MultiSignature]] + * @description The raw [[ExtrinsicSignature]] */ - public get multiSignature (): MultiSignature { - return this.get('signature') as MultiSignature; + public get multiSignature (): ExtrinsicSignature { + return this.get('signature') as ExtrinsicSignature; } /** @@ -99,7 +110,7 @@ export class GenericExtrinsicSignatureV4 extends Struct implements IExtrinsicSig return this.get('tip') as Compact; } - protected _injectSignature (signer: Address, signature: MultiSignature, { era, nonce, tip }: GenericExtrinsicPayloadV4): IExtrinsicSignature { + protected _injectSignature (signer: Address, signature: ExtrinsicSignature, { era, nonce, tip }: GenericExtrinsicPayloadV4): IExtrinsicSignature { this.set('era', era); this.set('nonce', nonce); this.set('signer', signer); @@ -115,7 +126,7 @@ export class GenericExtrinsicSignatureV4 extends Struct implements IExtrinsicSig public addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | string, payload: ExtrinsicPayloadValue | Uint8Array | string): IExtrinsicSignature { return this._injectSignature( this.registry.createType('Address', signer), - this.registry.createType('MultiSignature', signature), + this.registry.createType('ExtrinsicSignature', signature), new GenericExtrinsicPayloadV4(this.registry, payload) ); } @@ -142,7 +153,7 @@ export class GenericExtrinsicSignatureV4 extends Struct implements IExtrinsicSig public sign (method: Call, account: IKeyringPair, options: SignatureOptions): IExtrinsicSignature { const signer = this.registry.createType('Address', account.addressRaw); const payload = this.createPayload(method, options); - const signature = this.registry.createType('MultiSignature', payload.sign(account)); + const signature = this.registry.createType('ExtrinsicSignature', payload.sign(account)); return this._injectSignature(signer, signature, payload); } @@ -153,7 +164,7 @@ export class GenericExtrinsicSignatureV4 extends Struct implements IExtrinsicSig public signFake (method: Call, address: Address | Uint8Array | string, options: SignatureOptions): IExtrinsicSignature { const signer = this.registry.createType('Address', address); const payload = this.createPayload(method, options); - const signature = this.registry.createType('MultiSignature', u8aConcat(new Uint8Array([1]), new Uint8Array(64).fill(0x42))); + const signature = this.registry.createType('ExtrinsicSignature', u8aConcat(this.#fakePrefix, new Uint8Array(64).fill(0x42))); return this._injectSignature(signer, signature, payload); } diff --git a/packages/types/src/interfaces/extrinsics/definitions.ts b/packages/types/src/interfaces/extrinsics/definitions.ts index d6b5f7af14..76cfcf5c89 100644 --- a/packages/types/src/interfaces/extrinsics/definitions.ts +++ b/packages/types/src/interfaces/extrinsics/definitions.ts @@ -12,6 +12,7 @@ export default { Extrinsic: 'GenericExtrinsic', ExtrinsicEra: 'GenericExtrinsicEra', ExtrinsicPayload: 'GenericExtrinsicPayload', + ExtrinsicSignature: 'MultiSignature', ExtrinsicV4: 'GenericExtrinsicV4', ExtrinsicPayloadV4: 'GenericExtrinsicPayloadV4', ExtrinsicSignatureV4: 'GenericExtrinsicSignatureV4', @@ -23,6 +24,7 @@ export default { MortalEra: 'GenericMortalEra', // signatures & signer + AnySignature: 'H512', MultiSignature: { _enum: { Ed25519: 'Ed25519Signature', diff --git a/packages/types/src/interfaces/extrinsics/types.ts b/packages/types/src/interfaces/extrinsics/types.ts index 3fdeced18f..23ef938f26 100644 --- a/packages/types/src/interfaces/extrinsics/types.ts +++ b/packages/types/src/interfaces/extrinsics/types.ts @@ -5,6 +5,9 @@ import { Enum, U8aFixed } from '@polkadot/types/codec'; import { GenericExtrinsic, GenericExtrinsicEra, GenericExtrinsicPayload, GenericExtrinsicPayloadUnknown, GenericExtrinsicPayloadV4, GenericExtrinsicSignatureV4, GenericExtrinsicUnknown, GenericExtrinsicV4, GenericImmortalEra, GenericMortalEra, GenericSignerPayload } from '@polkadot/types/extrinsic'; import { H512 } from '@polkadot/types/interfaces/runtime'; +/** @name AnySignature */ +export interface AnySignature extends H512 {} + /** @name EcdsaSignature */ export interface EcdsaSignature extends U8aFixed {} @@ -26,6 +29,9 @@ export interface ExtrinsicPayloadUnknown extends GenericExtrinsicPayloadUnknown /** @name ExtrinsicPayloadV4 */ export interface ExtrinsicPayloadV4 extends GenericExtrinsicPayloadV4 {} +/** @name ExtrinsicSignature */ +export interface ExtrinsicSignature extends MultiSignature {} + /** @name ExtrinsicSignatureV4 */ export interface ExtrinsicSignatureV4 extends GenericExtrinsicSignatureV4 {}