From 1684e53c2276b60c3908a7d2119463c114a39cda Mon Sep 17 00:00:00 2001 From: tarikgul Date: Fri, 6 Sep 2024 13:35:50 -0400 Subject: [PATCH 01/71] TransactionExtension Start --- packages/types/src/extrinsic/Extrinsic.ts | 33 ++++++++++++------- .../types/src/extrinsic/ExtrinsicPayload.ts | 1 + 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 6695ff56aaa8..75299b6e5c33 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -19,6 +19,7 @@ import { BIT_SIGNED, BIT_UNSIGNED, DEFAULT_VERSION, UNMASK_VERSION } from './con interface CreateOptions { version?: number; + subVersionV5?: SubVersionV5; } // NOTE The following 2 types, as well as the VERSION structure and the latest export @@ -35,16 +36,26 @@ const VERSIONS = [ 'ExtrinsicV4' ]; +const DEFAULT_V5_VERSION = 'signed'; + +const V5_VERSIONS = { + bare: 'ExtrinsicV4', + general: 'GeneralExtrinsicV5', + signed: 'ExtrinsicV4' +}; + +type SubVersionV5 = 'signed' | 'bare' | 'general'; + export { LATEST_EXTRINSIC_VERSION }; /** @internal */ -function newFromValue (registry: Registry, value: any, version: number): ExtrinsicVx | ExtrinsicUnknown { +function newFromValue (registry: Registry, value: any, version: number, subVersionV5: SubVersionV5): ExtrinsicVx | ExtrinsicUnknown { if (value instanceof GenericExtrinsic) { return value.unwrap(); } const isSigned = (version & BIT_SIGNED) === BIT_SIGNED; - const type = VERSIONS[version & UNMASK_VERSION] || VERSIONS[0]; + const type = (version & UNMASK_VERSION) === 5 ? V5_VERSIONS[subVersionV5] : VERSIONS[version & UNMASK_VERSION] || VERSIONS[0]; // we cast here since the VERSION definition is incredibly broad - we don't have a // slice for "only add extrinsic types", and more string definitions become unwieldy @@ -52,20 +63,20 @@ function newFromValue (registry: Registry, value: any, version: number): Extrins } /** @internal */ -function decodeExtrinsic (registry: Registry, value?: GenericExtrinsic | ExtrinsicValue | AnyU8a | Call, version: number = DEFAULT_VERSION): ExtrinsicVx | ExtrinsicUnknown { +function decodeExtrinsic (registry: Registry, value?: GenericExtrinsic | ExtrinsicValue | AnyU8a | Call, version: number = DEFAULT_VERSION, subVersionV5: SubVersionV5 = DEFAULT_V5_VERSION): ExtrinsicVx | ExtrinsicUnknown { if (isU8a(value) || Array.isArray(value) || isHex(value)) { - return decodeU8a(registry, u8aToU8a(value), version); + return decodeU8a(registry, u8aToU8a(value), version, subVersionV5); } else if (value instanceof registry.createClassUnsafe('Call')) { - return newFromValue(registry, { method: value }, version); + return newFromValue(registry, { method: value }, version, subVersionV5); } - return newFromValue(registry, value, version); + return newFromValue(registry, value, version, subVersionV5); } /** @internal */ -function decodeU8a (registry: Registry, value: Uint8Array, version: number): ExtrinsicVx | ExtrinsicUnknown { +function decodeU8a (registry: Registry, value: Uint8Array, version: number, subVersionV5: SubVersionV5): ExtrinsicVx | ExtrinsicUnknown { if (!value.length) { - return newFromValue(registry, new Uint8Array(), version); + return newFromValue(registry, new Uint8Array(), version, subVersionV5); } const [offset, length] = compactFromU8a(value); @@ -77,7 +88,7 @@ function decodeU8a (registry: Registry, value: Uint8Array, version: number): Ext const data = value.subarray(offset, total); - return newFromValue(registry, data.subarray(1), data[0]); + return newFromValue(registry, data.subarray(1), data[0], subVersionV5); } abstract class ExtrinsicBase extends AbstractBase { @@ -260,8 +271,8 @@ export class GenericExtrinsic extends ExtrinsicBa static LATEST_EXTRINSIC_VERSION = LATEST_EXTRINSIC_VERSION; - constructor (registry: Registry, value?: GenericExtrinsic | ExtrinsicValue | AnyU8a | Call, { version }: CreateOptions = {}) { - super(registry, decodeExtrinsic(registry, value, version)); + constructor (registry: Registry, value?: GenericExtrinsic | ExtrinsicValue | AnyU8a | Call, { subVersionV5, version }: CreateOptions = {}) { + super(registry, decodeExtrinsic(registry, value, version, subVersionV5)); } /** diff --git a/packages/types/src/extrinsic/ExtrinsicPayload.ts b/packages/types/src/extrinsic/ExtrinsicPayload.ts index c5b38fbe25ea..c2463127b3f0 100644 --- a/packages/types/src/extrinsic/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/ExtrinsicPayload.ts @@ -27,6 +27,7 @@ const VERSIONS = [ 'ExtrinsicPayloadUnknown', 'ExtrinsicPayloadUnknown', 'ExtrinsicPayloadUnknown', + 'ExtrinsicPayloadV4', 'ExtrinsicPayloadV4' ]; From 90787544f6f71bb2de279df986792350372cae9e Mon Sep 17 00:00:00 2001 From: tarikgul Date: Fri, 6 Sep 2024 14:00:53 -0400 Subject: [PATCH 02/71] Add V5_VERSION logic to necessary classes --- packages/types/src/extrinsic/Extrinsic.ts | 9 +++---- .../types/src/extrinsic/ExtrinsicPayload.ts | 24 +++++++++++++------ packages/types/src/extrinsic/constants.ts | 5 ++++ packages/types/src/extrinsic/types.ts | 2 ++ packages/types/src/extrinsic/v4/Extrinsic.ts | 2 +- 5 files changed, 28 insertions(+), 14 deletions(-) diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 75299b6e5c33..b805e59827f9 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -9,13 +9,13 @@ import type { Address, Call, CodecHash, Hash } from '../interfaces/runtime/index import type { MultiLocation } from '../interfaces/types.js'; import type { CallBase, ExtrinsicPayloadValue, ICompact, IExtrinsic, IKeyringPair, INumber, Registry, SignatureOptions } from '../types/index.js'; import type { GenericExtrinsicEra } from './ExtrinsicEra.js'; +import type { SubVersionV5 } from './types.js'; import type { ExtrinsicValueV4 } from './v4/Extrinsic.js'; import { AbstractBase } from '@polkadot/types-codec'; import { compactAddLength, compactFromU8a, compactToU8a, isHex, isU8a, objectProperty, objectSpread, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; -import { EXTRINSIC_VERSION as LATEST_EXTRINSIC_VERSION } from './v4/Extrinsic.js'; -import { BIT_SIGNED, BIT_UNSIGNED, DEFAULT_VERSION, UNMASK_VERSION } from './constants.js'; +import { BIT_SIGNED, BIT_UNSIGNED, DEFAULT_V5_VERSION, DEFAULT_VERSION, EXTRINSIC_VERSION as LATEST_EXTRINSIC_VERSION, UNMASK_VERSION } from './constants.js'; interface CreateOptions { version?: number; @@ -36,16 +36,13 @@ const VERSIONS = [ 'ExtrinsicV4' ]; -const DEFAULT_V5_VERSION = 'signed'; - const V5_VERSIONS = { bare: 'ExtrinsicV4', + // Not supported yet general: 'GeneralExtrinsicV5', signed: 'ExtrinsicV4' }; -type SubVersionV5 = 'signed' | 'bare' | 'general'; - export { LATEST_EXTRINSIC_VERSION }; /** @internal */ diff --git a/packages/types/src/extrinsic/ExtrinsicPayload.ts b/packages/types/src/extrinsic/ExtrinsicPayload.ts index c2463127b3f0..068921fcea24 100644 --- a/packages/types/src/extrinsic/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/ExtrinsicPayload.ts @@ -9,14 +9,16 @@ import type { ExtrinsicPayloadV4 } from '../interfaces/extrinsics/index.js'; import type { Hash, MultiLocation } from '../interfaces/types.js'; import type { ExtrinsicPayloadValue, ICompact, IKeyringPair, INumber, IOption } from '../types/index.js'; import type { GenericExtrinsicEra } from './ExtrinsicEra.js'; +import type { SubVersionV5 } from './types.js'; import { AbstractBase } from '@polkadot/types-codec'; import { hexToU8a, isHex, u8aToHex } from '@polkadot/util'; -import { DEFAULT_VERSION } from './constants.js'; +import { DEFAULT_V5_VERSION, DEFAULT_VERSION } from './constants.js'; interface ExtrinsicPayloadOptions { version?: number; + subVersionV5?: SubVersionV5; } // all our known types that can be returned @@ -27,16 +29,24 @@ const VERSIONS = [ 'ExtrinsicPayloadUnknown', 'ExtrinsicPayloadUnknown', 'ExtrinsicPayloadUnknown', - 'ExtrinsicPayloadV4', 'ExtrinsicPayloadV4' ]; +const V5_VERSIONS = { + bare: 'ExtrinsicPayloadV4', + // Not supported yet + general: 'GeneralExtrinsicPayloadV5', + signed: 'ExtrinsicPayloadV4' +}; + /** @internal */ -function decodeExtrinsicPayload (registry: Registry, value?: GenericExtrinsicPayload | ExtrinsicPayloadValue | Uint8Array | string, version: number = DEFAULT_VERSION): ExtrinsicPayloadVx { +function decodeExtrinsicPayload (registry: Registry, value?: GenericExtrinsicPayload | ExtrinsicPayloadValue | Uint8Array | string, version: number = DEFAULT_VERSION, subVersionV5: SubVersionV5 = DEFAULT_V5_VERSION): ExtrinsicPayloadVx { if (value instanceof GenericExtrinsicPayload) { return value.unwrap(); } + const extVersion = version === 5 ? V5_VERSIONS[subVersionV5] : VERSIONS[version] || VERSIONS[0]; + /** * HACK: In order to change the assetId from `number | object` to HexString (While maintaining the true type ie Option), * to allow for easier generalization of the SignerPayloadJSON interface the below check is necessary. The ExtrinsicPayloadV4 class does not like @@ -52,10 +62,10 @@ function decodeExtrinsicPayload (registry: Registry, value?: GenericExtrinsicPay assetId: registry.createType('TAssetConversion', hexToU8a((value as ExtrinsicPayloadValue).assetId)).toJSON() }; - return registry.createTypeUnsafe(VERSIONS[version] || VERSIONS[0], [adjustedPayload, { version }]); + return registry.createTypeUnsafe(extVersion, [adjustedPayload, { version }]); } - return registry.createTypeUnsafe(VERSIONS[version] || VERSIONS[0], [value, { version }]); + return registry.createTypeUnsafe(extVersion, [value, { version }]); } /** @@ -65,8 +75,8 @@ function decodeExtrinsicPayload (registry: Registry, value?: GenericExtrinsicPay * on the contents included */ export class GenericExtrinsicPayload extends AbstractBase { - constructor (registry: Registry, value?: Partial | Uint8Array | string, { version }: ExtrinsicPayloadOptions = {}) { - super(registry, decodeExtrinsicPayload(registry, value as ExtrinsicPayloadValue, version)); + constructor (registry: Registry, value?: Partial | Uint8Array | string, { subVersionV5, version }: ExtrinsicPayloadOptions = {}) { + super(registry, decodeExtrinsicPayload(registry, value as ExtrinsicPayloadValue, version, subVersionV5)); } /** diff --git a/packages/types/src/extrinsic/constants.ts b/packages/types/src/extrinsic/constants.ts index bf827ea09795..6b8ed021493f 100644 --- a/packages/types/src/extrinsic/constants.ts +++ b/packages/types/src/extrinsic/constants.ts @@ -12,3 +12,8 @@ export const DEFAULT_VERSION = 4; export const IMMORTAL_ERA = new Uint8Array([0]); export const UNMASK_VERSION = 0b01111111; + +export const DEFAULT_V5_VERSION = 'signed'; + +// Latest extrinsic version is v5, which has backwards compatibility for v4 signed extrinsics +export const EXTRINSIC_VERSION = 4; diff --git a/packages/types/src/extrinsic/types.ts b/packages/types/src/extrinsic/types.ts index c0a04bee5794..ebb1560e53e5 100644 --- a/packages/types/src/extrinsic/types.ts +++ b/packages/types/src/extrinsic/types.ts @@ -21,3 +21,5 @@ export interface ExtrinsicExtraValue { nonce?: AnyNumber; tip?: AnyNumber; } + +export type SubVersionV5 = 'signed' | 'bare' | 'general'; diff --git a/packages/types/src/extrinsic/v4/Extrinsic.ts b/packages/types/src/extrinsic/v4/Extrinsic.ts index 6067609f306f..c60befd59515 100644 --- a/packages/types/src/extrinsic/v4/Extrinsic.ts +++ b/packages/types/src/extrinsic/v4/Extrinsic.ts @@ -10,7 +10,7 @@ import type { ExtrinsicOptions } from '../types.js'; import { Struct } from '@polkadot/types-codec'; import { isU8a } from '@polkadot/util'; -export const EXTRINSIC_VERSION = 4; +import { EXTRINSIC_VERSION } from '../constants.js'; export interface ExtrinsicValueV4 { method?: Call; From 52ad2279fe1ae7bff1c73bad143b695c0c8f3215 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Fri, 6 Sep 2024 14:10:52 -0400 Subject: [PATCH 03/71] Fix latest version to 5 --- packages/types/src/extrinsic/Extrinsic.ts | 2 +- packages/types/src/extrinsic/constants.ts | 2 +- packages/types/src/extrinsic/v4/Extrinsic.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index b805e59827f9..bac1d43bbbba 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -15,7 +15,7 @@ import type { ExtrinsicValueV4 } from './v4/Extrinsic.js'; import { AbstractBase } from '@polkadot/types-codec'; import { compactAddLength, compactFromU8a, compactToU8a, isHex, isU8a, objectProperty, objectSpread, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; -import { BIT_SIGNED, BIT_UNSIGNED, DEFAULT_V5_VERSION, DEFAULT_VERSION, EXTRINSIC_VERSION as LATEST_EXTRINSIC_VERSION, UNMASK_VERSION } from './constants.js'; +import { BIT_SIGNED, BIT_UNSIGNED, DEFAULT_V5_VERSION, DEFAULT_VERSION, LATEST_EXTRINSIC_VERSION, UNMASK_VERSION } from './constants.js'; interface CreateOptions { version?: number; diff --git a/packages/types/src/extrinsic/constants.ts b/packages/types/src/extrinsic/constants.ts index 6b8ed021493f..0a70170852d4 100644 --- a/packages/types/src/extrinsic/constants.ts +++ b/packages/types/src/extrinsic/constants.ts @@ -16,4 +16,4 @@ export const UNMASK_VERSION = 0b01111111; export const DEFAULT_V5_VERSION = 'signed'; // Latest extrinsic version is v5, which has backwards compatibility for v4 signed extrinsics -export const EXTRINSIC_VERSION = 4; +export const LATEST_EXTRINSIC_VERSION = 5; diff --git a/packages/types/src/extrinsic/v4/Extrinsic.ts b/packages/types/src/extrinsic/v4/Extrinsic.ts index c60befd59515..368f3c86bd88 100644 --- a/packages/types/src/extrinsic/v4/Extrinsic.ts +++ b/packages/types/src/extrinsic/v4/Extrinsic.ts @@ -10,7 +10,7 @@ import type { ExtrinsicOptions } from '../types.js'; import { Struct } from '@polkadot/types-codec'; import { isU8a } from '@polkadot/util'; -import { EXTRINSIC_VERSION } from '../constants.js'; +const EXTRINSIC_VERSION = 4; export interface ExtrinsicValueV4 { method?: Call; From 55b04995b10fc52f8e16ee7c1a1feef6567bf7ec Mon Sep 17 00:00:00 2001 From: tarikgul Date: Tue, 10 Sep 2024 13:27:40 -0400 Subject: [PATCH 04/71] Add v5 dir --- packages/types/src/extrinsic/v5/Extrinsic.ts | 26 ++++++++++++++ .../src/extrinsic/v5/ExtrinsicPayload.ts | 35 +++++++++++++++++++ .../src/extrinsic/v5/ExtrinsicSignature.ts | 13 +++++++ packages/types/src/extrinsic/v5/index.ts | 6 ++++ 4 files changed, 80 insertions(+) create mode 100644 packages/types/src/extrinsic/v5/Extrinsic.ts create mode 100644 packages/types/src/extrinsic/v5/ExtrinsicPayload.ts create mode 100644 packages/types/src/extrinsic/v5/ExtrinsicSignature.ts create mode 100644 packages/types/src/extrinsic/v5/index.ts diff --git a/packages/types/src/extrinsic/v5/Extrinsic.ts b/packages/types/src/extrinsic/v5/Extrinsic.ts new file mode 100644 index 000000000000..b2d7c9b37dcc --- /dev/null +++ b/packages/types/src/extrinsic/v5/Extrinsic.ts @@ -0,0 +1,26 @@ +// Copyright 2017-2024 @polkadot/types authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { ExtrinsicSignatureV4 } from '../../interfaces/extrinsics/index.js'; +import type { Call } from '../../interfaces/runtime/index.js'; +import type { Registry } from '../../types/index.js'; +import type { ExtrinsicOptions } from '../types.js'; + +import { GenericExtrinsicV4 } from '../v4/Extrinsic.js'; + +const EXTRINSIC_VERSION = 5; + +export interface ExtrinsicValueV4 { + method?: Call; + signature?: ExtrinsicSignatureV4; +} + +export class GenericExtrinsicV5 extends GenericExtrinsicV4 { + constructor (registry: Registry, value?: Uint8Array | ExtrinsicValueV4 | Call, { isSigned }: Partial = {}) { + super(registry, value, { isSigned }); + } + + public override get version () { + return EXTRINSIC_VERSION; + } +} diff --git a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts new file mode 100644 index 000000000000..05150263e375 --- /dev/null +++ b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts @@ -0,0 +1,35 @@ +// Copyright 2017-2024 @polkadot/types authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { SignOptions } from '@polkadot/keyring/types'; +import type { Registry } from '@polkadot/types-codec/types'; +import type { HexString } from '@polkadot/util/types'; +import type { ExtrinsicPayloadValue, IKeyringPair } from '../../types/index.js'; + +import { Enum } from '@polkadot/types-codec'; + +import { GenericExtrinsicPayloadV4 } from '../v4/ExtrinsicPayload.js'; + +export class GenericExtrinsicPayloadV5 extends GenericExtrinsicPayloadV4 { + #signOptions: SignOptions; + + constructor (registry: Registry, value?: ExtrinsicPayloadValue | Uint8Array | HexString) { + super(registry, 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 + // (which may be 64 or 65 bytes) + this.#signOptions = { + withType: registry.createTypeUnsafe('ExtrinsicSignature', []) instanceof Enum + }; + } + + public override sign (signerPair: IKeyringPair) { + // NOTE The `toU8a({ method: 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..V3, if we have a V6, carry this comment + return signerPair.sign(this.registry.hash(this.toU8a({ method: true })), this.#signOptions); + } +} diff --git a/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts b/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts new file mode 100644 index 000000000000..cab773bec381 --- /dev/null +++ b/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2024 @polkadot/types authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { Registry } from '../../types/index.js'; +import type { ExtrinsicSignatureOptions } from '../types.js'; + +import { GenericExtrinsicSignatureV4 } from '../v4/ExtrinsicSignature.js'; + +export class GenericExtrinsicSignatureV5 extends GenericExtrinsicSignatureV4 { + constructor (registry: Registry, value?: GenericExtrinsicSignatureV4 | Uint8Array, { isSigned }: ExtrinsicSignatureOptions = {}) { + super(registry, value, { isSigned }); + } +} diff --git a/packages/types/src/extrinsic/v5/index.ts b/packages/types/src/extrinsic/v5/index.ts new file mode 100644 index 000000000000..d99511d9a93c --- /dev/null +++ b/packages/types/src/extrinsic/v5/index.ts @@ -0,0 +1,6 @@ +// Copyright 2017-2024 @polkadot/types authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +export { GenericExtrinsicV5 } from './Extrinsic.js'; +export { GenericExtrinsicPayloadV5 } from './ExtrinsicPayload.js'; +export { GenericExtrinsicSignatureV5 } from './ExtrinsicSignature.js'; From 34e4a02643802462067771e462e3be5a8f9bcf51 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Thu, 19 Sep 2024 13:55:43 -0400 Subject: [PATCH 05/71] Add v5 types to interfaces --- packages/types-augment/src/registry/interfaces.ts | 5 ++++- packages/types/src/extrinsic/Extrinsic.ts | 5 +++-- packages/types/src/extrinsic/index.ts | 1 + .../types/src/interfaces/extrinsics/definitions.ts | 3 +++ packages/types/src/interfaces/extrinsics/types.ts | 11 ++++++++++- 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/types-augment/src/registry/interfaces.ts b/packages/types-augment/src/registry/interfaces.ts index 3a28ca1ca0b5..5b77684993c2 100644 --- a/packages/types-augment/src/registry/interfaces.ts +++ b/packages/types-augment/src/registry/interfaces.ts @@ -35,7 +35,7 @@ import type { ApprovalFlag, DefunctVoter, Renouncing, SetIndex, Vote, VoteIndex, import type { CreatedBlock, ImportedAux } from '@polkadot/types/interfaces/engine'; import type { BlockV0, BlockV1, BlockV2, EIP1559Transaction, EIP2930Transaction, EthAccessList, EthAccessListItem, EthAccount, EthAddress, EthBlock, EthBloom, EthCallRequest, EthFeeHistory, EthFilter, EthFilterAddress, EthFilterChanges, EthFilterTopic, EthFilterTopicEntry, EthFilterTopicInner, EthHeader, EthLog, EthReceipt, EthReceiptV0, EthReceiptV3, EthRichBlock, EthRichHeader, EthStorageProof, EthSubKind, EthSubParams, EthSubResult, EthSyncInfo, EthSyncStatus, EthTransaction, EthTransactionAction, EthTransactionCondition, EthTransactionRequest, EthTransactionSignature, EthTransactionStatus, EthWork, EthereumAccountId, EthereumAddress, EthereumLookupSource, EthereumSignature, LegacyTransaction, TransactionV0, TransactionV1, TransactionV2 } from '@polkadot/types/interfaces/eth'; import type { EvmAccount, EvmCallInfo, EvmCallInfoV2, EvmCreateInfo, EvmCreateInfoV2, EvmLog, EvmVicinity, EvmWeightInfo, ExitError, ExitFatal, ExitReason, ExitRevert, ExitSucceed } from '@polkadot/types/interfaces/evm'; -import type { AnySignature, EcdsaSignature, Ed25519Signature, Era, Extrinsic, ExtrinsicEra, ExtrinsicPayload, ExtrinsicPayloadUnknown, ExtrinsicPayloadV4, ExtrinsicSignature, ExtrinsicSignatureV4, ExtrinsicUnknown, ExtrinsicV4, ImmortalEra, MortalEra, MultiSignature, Signature, SignerPayload, Sr25519Signature } from '@polkadot/types/interfaces/extrinsics'; +import type { AnySignature, EcdsaSignature, Ed25519Signature, Era, Extrinsic, ExtrinsicEra, ExtrinsicPayload, ExtrinsicPayloadUnknown, ExtrinsicPayloadV4, ExtrinsicPayloadV5, ExtrinsicSignature, ExtrinsicSignatureV4, ExtrinsicSignatureV5, ExtrinsicUnknown, ExtrinsicV4, ExtrinsicV5, ImmortalEra, MortalEra, MultiSignature, Signature, SignerPayload, Sr25519Signature } from '@polkadot/types/interfaces/extrinsics'; import type { FungiblesAccessError } from '@polkadot/types/interfaces/fungibles'; import type { AssetOptions, Owner, PermissionLatest, PermissionVersions, PermissionsV1 } from '@polkadot/types/interfaces/genericAsset'; import type { GenesisBuildErr } from '@polkadot/types/interfaces/genesisBuilder'; @@ -504,12 +504,15 @@ declare module '@polkadot/types/types/registry' { ExtrinsicPayload: ExtrinsicPayload; ExtrinsicPayloadUnknown: ExtrinsicPayloadUnknown; ExtrinsicPayloadV4: ExtrinsicPayloadV4; + ExtrinsicPayloadV5: ExtrinsicPayloadV5; ExtrinsicSignature: ExtrinsicSignature; ExtrinsicSignatureV4: ExtrinsicSignatureV4; + ExtrinsicSignatureV5: ExtrinsicSignatureV5; ExtrinsicStatus: ExtrinsicStatus; ExtrinsicsWeight: ExtrinsicsWeight; ExtrinsicUnknown: ExtrinsicUnknown; ExtrinsicV4: ExtrinsicV4; + ExtrinsicV5: ExtrinsicV5; f32: f32; F32: F32; f64: f64; diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index bac1d43bbbba..f47d017c215a 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -33,7 +33,8 @@ const VERSIONS = [ 'ExtrinsicUnknown', 'ExtrinsicUnknown', 'ExtrinsicUnknown', - 'ExtrinsicV4' + 'ExtrinsicV4', + 'ExtrinsicV5' ]; const V5_VERSIONS = { @@ -269,7 +270,7 @@ export class GenericExtrinsic extends ExtrinsicBa static LATEST_EXTRINSIC_VERSION = LATEST_EXTRINSIC_VERSION; constructor (registry: Registry, value?: GenericExtrinsic | ExtrinsicValue | AnyU8a | Call, { subVersionV5, version }: CreateOptions = {}) { - super(registry, decodeExtrinsic(registry, value, version, subVersionV5)); + super(registry, decodeExtrinsic(registry, value, registry.metadata.extrinsic.version?.toNumber() || version, subVersionV5)); } /** diff --git a/packages/types/src/extrinsic/index.ts b/packages/types/src/extrinsic/index.ts index 66813951d39b..14689bd86ef6 100644 --- a/packages/types/src/extrinsic/index.ts +++ b/packages/types/src/extrinsic/index.ts @@ -11,3 +11,4 @@ export { GenericSignerPayload } from './SignerPayload.js'; // all starred export * from './v4/index.js'; +export * from './v5/index.js'; diff --git a/packages/types/src/interfaces/extrinsics/definitions.ts b/packages/types/src/interfaces/extrinsics/definitions.ts index dd3a381018a7..a0c78d17492f 100644 --- a/packages/types/src/interfaces/extrinsics/definitions.ts +++ b/packages/types/src/interfaces/extrinsics/definitions.ts @@ -18,6 +18,9 @@ export default { ExtrinsicSignatureV4: 'GenericExtrinsicSignatureV4', ExtrinsicUnknown: 'GenericExtrinsicUnknown', ExtrinsicPayloadUnknown: 'GenericExtrinsicPayloadUnknown', + ExtrinsicV5: 'GenericExtrinsicV5', + ExtrinsicPayloadV5: 'GenericExtrinsicPayloadV5', + ExtrinsicSignatureV5: 'GenericExtrinsicSignatureV5', // eras Era: 'ExtrinsicEra', diff --git a/packages/types/src/interfaces/extrinsics/types.ts b/packages/types/src/interfaces/extrinsics/types.ts index 016c520b2ee0..056443f08841 100644 --- a/packages/types/src/interfaces/extrinsics/types.ts +++ b/packages/types/src/interfaces/extrinsics/types.ts @@ -1,7 +1,7 @@ // Auto-generated via `yarn polkadot-types-from-defs`, do not edit /* eslint-disable */ -import type { GenericExtrinsic, GenericExtrinsicEra, GenericExtrinsicPayload, GenericExtrinsicPayloadUnknown, GenericExtrinsicPayloadV4, GenericExtrinsicSignatureV4, GenericExtrinsicUnknown, GenericExtrinsicV4, GenericImmortalEra, GenericMortalEra, GenericSignerPayload } from '@polkadot/types'; +import type { GenericExtrinsic, GenericExtrinsicEra, GenericExtrinsicPayload, GenericExtrinsicPayloadUnknown, GenericExtrinsicPayloadV4, GenericExtrinsicPayloadV5, GenericExtrinsicSignatureV4, GenericExtrinsicSignatureV5, GenericExtrinsicUnknown, GenericExtrinsicV4, GenericExtrinsicV5, GenericImmortalEra, GenericMortalEra, GenericSignerPayload } from '@polkadot/types'; import type { Enum, U8aFixed } from '@polkadot/types-codec'; import type { H512 } from '@polkadot/types/interfaces/runtime'; @@ -32,18 +32,27 @@ export interface ExtrinsicPayloadUnknown extends GenericExtrinsicPayloadUnknown /** @name ExtrinsicPayloadV4 */ export interface ExtrinsicPayloadV4 extends GenericExtrinsicPayloadV4 {} +/** @name ExtrinsicPayloadV5 */ +export interface ExtrinsicPayloadV5 extends GenericExtrinsicPayloadV5 {} + /** @name ExtrinsicSignature */ export interface ExtrinsicSignature extends MultiSignature {} /** @name ExtrinsicSignatureV4 */ export interface ExtrinsicSignatureV4 extends GenericExtrinsicSignatureV4 {} +/** @name ExtrinsicSignatureV5 */ +export interface ExtrinsicSignatureV5 extends GenericExtrinsicSignatureV5 {} + /** @name ExtrinsicUnknown */ export interface ExtrinsicUnknown extends GenericExtrinsicUnknown {} /** @name ExtrinsicV4 */ export interface ExtrinsicV4 extends GenericExtrinsicV4 {} +/** @name ExtrinsicV5 */ +export interface ExtrinsicV5 extends GenericExtrinsicV5 {} + /** @name ImmortalEra */ export interface ImmortalEra extends GenericImmortalEra {} From 2e28c3fe15c1b7243065ed1e2d6d06ce42c7a8bf Mon Sep 17 00:00:00 2001 From: tarikgul Date: Thu, 19 Sep 2024 14:04:35 -0400 Subject: [PATCH 06/71] Cleanup paths --- packages/types/src/extrinsic/Extrinsic.ts | 4 ++-- packages/types/src/extrinsic/ExtrinsicPayload.ts | 7 ++++--- packages/types/src/extrinsic/v4/Extrinsic.ts | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index f47d017c215a..7271bb9edce3 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -38,10 +38,10 @@ const VERSIONS = [ ]; const V5_VERSIONS = { - bare: 'ExtrinsicV4', + bare: 'ExtrinsicV5', // Not supported yet general: 'GeneralExtrinsicV5', - signed: 'ExtrinsicV4' + signed: 'ExtrinsicV5' }; export { LATEST_EXTRINSIC_VERSION }; diff --git a/packages/types/src/extrinsic/ExtrinsicPayload.ts b/packages/types/src/extrinsic/ExtrinsicPayload.ts index 068921fcea24..c74f80873fd8 100644 --- a/packages/types/src/extrinsic/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/ExtrinsicPayload.ts @@ -29,14 +29,15 @@ const VERSIONS = [ 'ExtrinsicPayloadUnknown', 'ExtrinsicPayloadUnknown', 'ExtrinsicPayloadUnknown', - 'ExtrinsicPayloadV4' + 'ExtrinsicPayloadV4', + 'ExtrinsicPayloadV5' ]; const V5_VERSIONS = { - bare: 'ExtrinsicPayloadV4', + bare: 'ExtrinsicPayloadV5', // Not supported yet general: 'GeneralExtrinsicPayloadV5', - signed: 'ExtrinsicPayloadV4' + signed: 'ExtrinsicPayloadV5' }; /** @internal */ diff --git a/packages/types/src/extrinsic/v4/Extrinsic.ts b/packages/types/src/extrinsic/v4/Extrinsic.ts index 368f3c86bd88..6067609f306f 100644 --- a/packages/types/src/extrinsic/v4/Extrinsic.ts +++ b/packages/types/src/extrinsic/v4/Extrinsic.ts @@ -10,7 +10,7 @@ import type { ExtrinsicOptions } from '../types.js'; import { Struct } from '@polkadot/types-codec'; import { isU8a } from '@polkadot/util'; -const EXTRINSIC_VERSION = 4; +export const EXTRINSIC_VERSION = 4; export interface ExtrinsicValueV4 { method?: Call; From 17993bf2e54caf3d7d6269c05a943831099990c2 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Thu, 19 Sep 2024 16:16:45 -0400 Subject: [PATCH 07/71] Update ExtrinsicSignature V5 --- .../src/extrinsic/v4/ExtrinsicSignature.ts | 2 +- packages/types/src/extrinsic/v5/Extrinsic.ts | 2 +- .../src/extrinsic/v5/ExtrinsicSignature.ts | 59 ++++++++++++++++++- 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/packages/types/src/extrinsic/v4/ExtrinsicSignature.ts b/packages/types/src/extrinsic/v4/ExtrinsicSignature.ts index 8ffe00a19010..4d7bbb4cc349 100644 --- a/packages/types/src/extrinsic/v4/ExtrinsicSignature.ts +++ b/packages/types/src/extrinsic/v4/ExtrinsicSignature.ts @@ -17,7 +17,7 @@ import { GenericExtrinsicPayloadV4 } from './ExtrinsicPayload.js'; // Ensure we have enough data for all types of signatures const FAKE_SIGNATURE = new Uint8Array(256).fill(1); -function toAddress (registry: Registry, address: Address | Uint8Array | string): Address { +export function toAddress (registry: Registry, address: Address | Uint8Array | string): Address { return registry.createTypeUnsafe('Address', [isU8a(address) ? u8aToHex(address) : address]); } diff --git a/packages/types/src/extrinsic/v5/Extrinsic.ts b/packages/types/src/extrinsic/v5/Extrinsic.ts index b2d7c9b37dcc..dc92e4862d8a 100644 --- a/packages/types/src/extrinsic/v5/Extrinsic.ts +++ b/packages/types/src/extrinsic/v5/Extrinsic.ts @@ -8,7 +8,7 @@ import type { ExtrinsicOptions } from '../types.js'; import { GenericExtrinsicV4 } from '../v4/Extrinsic.js'; -const EXTRINSIC_VERSION = 5; +export const EXTRINSIC_VERSION = 5; export interface ExtrinsicValueV4 { method?: Call; diff --git a/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts b/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts index cab773bec381..67ce7b096937 100644 --- a/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts +++ b/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts @@ -1,13 +1,68 @@ // Copyright 2017-2024 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { Registry } from '../../types/index.js'; +import type { Address, Call, ExtrinsicSignature } from '@polkadot/types/interfaces'; +import type { HexString } from '@polkadot/util/types'; +import type { ExtrinsicPayloadValue, IExtrinsicSignature, Registry, SignatureOptions } from '../../types/index.js'; import type { ExtrinsicSignatureOptions } from '../types.js'; -import { GenericExtrinsicSignatureV4 } from '../v4/ExtrinsicSignature.js'; +import { isUndefined, objectSpread } from '@polkadot/util'; + +import { IMMORTAL_ERA } from '../constants.js'; +import { GenericExtrinsicSignatureV4, toAddress } from '../v4/ExtrinsicSignature.js'; +import { GenericExtrinsicPayloadV5 } from '../v5/ExtrinsicPayload.js'; export class GenericExtrinsicSignatureV5 extends GenericExtrinsicSignatureV4 { + #signKeys: string[]; + constructor (registry: Registry, value?: GenericExtrinsicSignatureV4 | Uint8Array, { isSigned }: ExtrinsicSignatureOptions = {}) { + const signTypes = registry.getSignedExtensionTypes(); + super(registry, value, { isSigned }); + + this.#signKeys = Object.keys(signTypes); + } + + protected override _injectSignature (signer: Address, signature: ExtrinsicSignature, payload: GenericExtrinsicPayloadV5): IExtrinsicSignature { + // use the fields exposed to guide the getters + for (let i = 0, count = this.#signKeys.length; i < count; i++) { + const k = this.#signKeys[i]; + const v = payload.get(k); + + if (!isUndefined(v)) { + this.set(k, v); + } + } + + // additional fields (exposed in struct itself) + this.set('signer', signer); + this.set('signature', signature); + + return this; + } + + /** + * @description Adds a raw signature + */ + public override addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | HexString, payload: ExtrinsicPayloadValue | Uint8Array | HexString): IExtrinsicSignature { + return this._injectSignature( + toAddress(this.registry, signer), + this.registry.createTypeUnsafe('ExtrinsicSignature', [signature]), + new GenericExtrinsicPayloadV5(this.registry, payload) + ); + } + + /** + * @description Creates a payload from the supplied options + */ + public override createPayload (method: Call, options: SignatureOptions): GenericExtrinsicPayloadV5 { + const { era, runtimeVersion: { specVersion, transactionVersion } } = options; + + return new GenericExtrinsicPayloadV5(this.registry, objectSpread({}, options, { + era: era || IMMORTAL_ERA, + method: method.toHex(), + specVersion, + transactionVersion + })); } } From 91696470217008f01c8abe47b6f3dbb97637971e Mon Sep 17 00:00:00 2001 From: tarikgul Date: Thu, 19 Sep 2024 16:37:24 -0400 Subject: [PATCH 08/71] Cleanup v5 folders --- .../src/extrinsic/v4/ExtrinsicSignature.ts | 2 +- packages/types/src/extrinsic/v5/Extrinsic.ts | 102 +++++++++- .../src/extrinsic/v5/ExtrinsicSignature.ts | 185 ++++++++++++++++-- 3 files changed, 266 insertions(+), 23 deletions(-) diff --git a/packages/types/src/extrinsic/v4/ExtrinsicSignature.ts b/packages/types/src/extrinsic/v4/ExtrinsicSignature.ts index 4d7bbb4cc349..8ffe00a19010 100644 --- a/packages/types/src/extrinsic/v4/ExtrinsicSignature.ts +++ b/packages/types/src/extrinsic/v4/ExtrinsicSignature.ts @@ -17,7 +17,7 @@ import { GenericExtrinsicPayloadV4 } from './ExtrinsicPayload.js'; // Ensure we have enough data for all types of signatures const FAKE_SIGNATURE = new Uint8Array(256).fill(1); -export function toAddress (registry: Registry, address: Address | Uint8Array | string): Address { +function toAddress (registry: Registry, address: Address | Uint8Array | string): Address { return registry.createTypeUnsafe('Address', [isU8a(address) ? u8aToHex(address) : address]); } diff --git a/packages/types/src/extrinsic/v5/Extrinsic.ts b/packages/types/src/extrinsic/v5/Extrinsic.ts index dc92e4862d8a..526d139f5bc0 100644 --- a/packages/types/src/extrinsic/v5/Extrinsic.ts +++ b/packages/types/src/extrinsic/v5/Extrinsic.ts @@ -1,26 +1,108 @@ // Copyright 2017-2024 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { ExtrinsicSignatureV4 } from '../../interfaces/extrinsics/index.js'; -import type { Call } from '../../interfaces/runtime/index.js'; -import type { Registry } from '../../types/index.js'; +import type { HexString } from '@polkadot/util/types'; +import type { ExtrinsicSignatureV5 } from '../../interfaces/extrinsics/index.js'; +import type { Address, Call } from '../../interfaces/runtime/index.js'; +import type { ExtrinsicPayloadValue, IExtrinsicImpl, IKeyringPair, Registry, SignatureOptions } from '../../types/index.js'; import type { ExtrinsicOptions } from '../types.js'; -import { GenericExtrinsicV4 } from '../v4/Extrinsic.js'; +import { Struct } from '@polkadot/types-codec'; +import { isU8a } from '@polkadot/util'; export const EXTRINSIC_VERSION = 5; -export interface ExtrinsicValueV4 { +export interface ExtrinsicValueV5 { method?: Call; - signature?: ExtrinsicSignatureV4; + signature?: ExtrinsicSignatureV5; } -export class GenericExtrinsicV5 extends GenericExtrinsicV4 { - constructor (registry: Registry, value?: Uint8Array | ExtrinsicValueV4 | Call, { isSigned }: Partial = {}) { - super(registry, value, { isSigned }); +/** + * @name GenericExtrinsicV4 + * @description + * The third generation of compact extrinsics + */ +export class GenericExtrinsicV5 extends Struct implements IExtrinsicImpl { + constructor (registry: Registry, value?: Uint8Array | ExtrinsicValueV5 | Call, { isSigned }: Partial = {}) { + super(registry, { + signature: 'ExtrinsicSignatureV5', + // eslint-disable-next-line sort-keys + method: 'Call' + }, GenericExtrinsicV5.decodeExtrinsic(registry, value, isSigned)); } - public override get version () { + /** @internal */ + public static decodeExtrinsic (registry: Registry, value?: Call | Uint8Array | ExtrinsicValueV5, isSigned = false): ExtrinsicValueV5 { + if (value instanceof GenericExtrinsicV5) { + return value; + } else if (value instanceof registry.createClassUnsafe('Call')) { + return { method: value }; + } else if (isU8a(value)) { + // here we decode manually since we need to pull through the version information + const signature = registry.createTypeUnsafe('ExtrinsicSignatureV5', [value, { isSigned }]); + const method = registry.createTypeUnsafe('Call', [value.subarray(signature.encodedLength)]); + + return { + method, + signature + }; + } + + return value || {}; + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + public override get encodedLength (): number { + return this.toU8a().length; + } + + /** + * @description The [[Call]] this extrinsic wraps + */ + public get method (): Call { + return this.getT('method'); + } + + /** + * @description The [[ExtrinsicSignatureV4]] + */ + public get signature (): ExtrinsicSignatureV5 { + return this.getT('signature'); + } + + /** + * @description The version for the signature + */ + public get version (): number { return EXTRINSIC_VERSION; } + + /** + * @description Add an [[ExtrinsicSignatureV4]] to the extrinsic (already generated) + */ + public addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | HexString, payload: ExtrinsicPayloadValue | Uint8Array | HexString): GenericExtrinsicV5 { + this.signature.addSignature(signer, signature, payload); + + return this; + } + + /** + * @description Sign the extrinsic with a specific keypair + */ + public sign (account: IKeyringPair, options: SignatureOptions): GenericExtrinsicV5 { + this.signature.sign(this.method, account, options); + + return this; + } + + /** + * @describe Adds a fake signature to the extrinsic + */ + public signFake (signer: Address | Uint8Array | string, options: SignatureOptions): GenericExtrinsicV5 { + this.signature.signFake(this.method, signer, options); + + return this; + } } diff --git a/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts b/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts index 67ce7b096937..55cdef901891 100644 --- a/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts +++ b/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts @@ -1,29 +1,146 @@ // Copyright 2017-2024 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { Address, Call, ExtrinsicSignature } from '@polkadot/types/interfaces'; +import type { MultiLocation } from '@polkadot/types/interfaces'; import type { HexString } from '@polkadot/util/types'; -import type { ExtrinsicPayloadValue, IExtrinsicSignature, Registry, SignatureOptions } from '../../types/index.js'; +import type { EcdsaSignature, Ed25519Signature, ExtrinsicEra, ExtrinsicSignature, Sr25519Signature } from '../../interfaces/extrinsics/index.js'; +import type { Address, Call, Hash } from '../../interfaces/runtime/index.js'; +import type { ExtrinsicPayloadValue, ICompact, IExtrinsicSignature, IKeyringPair, INumber, IOption, Registry, SignatureOptions } from '../../types/index.js'; import type { ExtrinsicSignatureOptions } from '../types.js'; -import { isUndefined, objectSpread } from '@polkadot/util'; +import { Struct } from '@polkadot/types-codec'; +import { isU8a, isUndefined, objectProperties, objectSpread, stringify, u8aToHex } from '@polkadot/util'; -import { IMMORTAL_ERA } from '../constants.js'; -import { GenericExtrinsicSignatureV4, toAddress } from '../v4/ExtrinsicSignature.js'; -import { GenericExtrinsicPayloadV5 } from '../v5/ExtrinsicPayload.js'; +import { EMPTY_U8A, IMMORTAL_ERA } from '../constants.js'; +import { GenericExtrinsicPayloadV5 } from './ExtrinsicPayload.js'; -export class GenericExtrinsicSignatureV5 extends GenericExtrinsicSignatureV4 { +// Ensure we have enough data for all types of signatures +const FAKE_SIGNATURE = new Uint8Array(256).fill(1); + +function toAddress (registry: Registry, address: Address | Uint8Array | string): Address { + return registry.createTypeUnsafe('Address', [isU8a(address) ? u8aToHex(address) : address]); +} + +/** + * @name GenericExtrinsicSignatureV5 + * @description + * A container for the [[Signature]] associated with a specific [[Extrinsic]] + */ +export class GenericExtrinsicSignatureV5 extends Struct implements IExtrinsicSignature { #signKeys: string[]; - constructor (registry: Registry, value?: GenericExtrinsicSignatureV4 | Uint8Array, { isSigned }: ExtrinsicSignatureOptions = {}) { + constructor (registry: Registry, value?: GenericExtrinsicSignatureV5 | Uint8Array, { isSigned }: ExtrinsicSignatureOptions = {}) { const signTypes = registry.getSignedExtensionTypes(); - super(registry, value, { isSigned }); + super( + registry, + objectSpread( + // eslint-disable-next-line sort-keys + { signer: 'Address', signature: 'ExtrinsicSignature' }, + signTypes + ), + GenericExtrinsicSignatureV5.decodeExtrinsicSignature(value, isSigned) + ); this.#signKeys = Object.keys(signTypes); + + objectProperties(this, this.#signKeys, (k) => this.get(k)); + } + + /** @internal */ + public static decodeExtrinsicSignature (value?: GenericExtrinsicSignatureV5 | Uint8Array, isSigned = false): GenericExtrinsicSignatureV5 | Uint8Array { + if (!value) { + return EMPTY_U8A; + } else if (value instanceof GenericExtrinsicSignatureV5) { + return value; + } + + return isSigned + ? value + : EMPTY_U8A; + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + public override 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.getT('era'); + } + + /** + * @description The [[Index]] for the signature + */ + public get nonce (): ICompact { + return this.getT('nonce'); + } + + /** + * @description The actual [[EcdsaSignature]], [[Ed25519Signature]] or [[Sr25519Signature]] + */ + public get signature (): EcdsaSignature | Ed25519Signature | 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; } - protected override _injectSignature (signer: Address, signature: ExtrinsicSignature, payload: GenericExtrinsicPayloadV5): IExtrinsicSignature { + /** + * @description The raw [[ExtrinsicSignature]] + */ + public get multiSignature (): ExtrinsicSignature { + return this.getT('signature'); + } + + /** + * @description The [[Address]] that signed + */ + public get signer (): Address { + return this.getT('signer'); + } + + /** + * @description The [[Balance]] tip + */ + public get tip (): ICompact { + return this.getT('tip'); + } + + /** + * @description The [[u32]] or [[MultiLocation]] assetId + */ + public get assetId (): IOption { + return this.getT('assetId'); + } + + /** + * @description the [[u32]] mode + */ + public get mode (): INumber { + return this.getT('mode'); + } + + /** + * @description The [[Hash]] for the metadata + */ + public get metadataHash (): IOption { + return this.getT('metadataHash'); + } + + protected _injectSignature (signer: Address, signature: ExtrinsicSignature, payload: GenericExtrinsicPayloadV5): IExtrinsicSignature { // use the fields exposed to guide the getters for (let i = 0, count = this.#signKeys.length; i < count; i++) { const k = this.#signKeys[i]; @@ -44,7 +161,7 @@ export class GenericExtrinsicSignatureV5 extends GenericExtrinsicSignatureV4 { /** * @description Adds a raw signature */ - public override addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | HexString, payload: ExtrinsicPayloadValue | Uint8Array | HexString): IExtrinsicSignature { + public addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | HexString, payload: ExtrinsicPayloadValue | Uint8Array | HexString): IExtrinsicSignature { return this._injectSignature( toAddress(this.registry, signer), this.registry.createTypeUnsafe('ExtrinsicSignature', [signature]), @@ -55,7 +172,7 @@ export class GenericExtrinsicSignatureV5 extends GenericExtrinsicSignatureV4 { /** * @description Creates a payload from the supplied options */ - public override createPayload (method: Call, options: SignatureOptions): GenericExtrinsicPayloadV5 { + public createPayload (method: Call, options: SignatureOptions): GenericExtrinsicPayloadV5 { const { era, runtimeVersion: { specVersion, transactionVersion } } = options; return new GenericExtrinsicPayloadV5(this.registry, objectSpread({}, options, { @@ -65,4 +182,48 @@ export class GenericExtrinsicSignatureV5 extends GenericExtrinsicSignatureV4 { transactionVersion })); } + + /** + * @description Generate a payload and applies the signature from a keypair + */ + public sign (method: Call, account: IKeyringPair, options: SignatureOptions): IExtrinsicSignature { + if (!account?.addressRaw) { + throw new Error(`Expected a valid keypair for signing, found ${stringify(account)}`); + } + + const payload = this.createPayload(method, options); + + return this._injectSignature( + toAddress(this.registry, account.addressRaw), + this.registry.createTypeUnsafe('ExtrinsicSignature', [payload.sign(account)]), + payload + ); + } + + /** + * @description Generate a payload and applies a fake signature + */ + public signFake (method: Call, address: Address | Uint8Array | string, options: SignatureOptions): IExtrinsicSignature { + if (!address) { + throw new Error(`Expected a valid address for signing, found ${stringify(address)}`); + } + + const payload = this.createPayload(method, options); + + return this._injectSignature( + toAddress(this.registry, address), + this.registry.createTypeUnsafe('ExtrinsicSignature', [FAKE_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 override toU8a (isBare?: boolean): Uint8Array { + return this.isSigned + ? super.toU8a(isBare) + : EMPTY_U8A; + } } From 159d01c1d97c7c8ed45ff10e17b868677d1a6a8e Mon Sep 17 00:00:00 2001 From: tarikgul Date: Thu, 19 Sep 2024 16:52:02 -0400 Subject: [PATCH 09/71] nits --- packages/types/src/extrinsic/v5/Extrinsic.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/types/src/extrinsic/v5/Extrinsic.ts b/packages/types/src/extrinsic/v5/Extrinsic.ts index 526d139f5bc0..148346669aec 100644 --- a/packages/types/src/extrinsic/v5/Extrinsic.ts +++ b/packages/types/src/extrinsic/v5/Extrinsic.ts @@ -18,7 +18,7 @@ export interface ExtrinsicValueV5 { } /** - * @name GenericExtrinsicV4 + * @name GenericExtrinsicV5 * @description * The third generation of compact extrinsics */ @@ -66,7 +66,7 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicImpl { } /** - * @description The [[ExtrinsicSignatureV4]] + * @description The [[ExtrinsicSignatureV5]] */ public get signature (): ExtrinsicSignatureV5 { return this.getT('signature'); @@ -80,7 +80,7 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicImpl { } /** - * @description Add an [[ExtrinsicSignatureV4]] to the extrinsic (already generated) + * @description Add an [[ExtrinsicSignatureV5]] to the extrinsic (already generated) */ public addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | HexString, payload: ExtrinsicPayloadValue | Uint8Array | HexString): GenericExtrinsicV5 { this.signature.addSignature(signer, signature, payload); From 38768e3d811e67398b51be609b1a16f044511a90 Mon Sep 17 00:00:00 2001 From: bee344 Date: Thu, 19 Sep 2024 19:04:17 -0300 Subject: [PATCH 10/71] signed works --- packages/types/src/extrinsic/Extrinsic.ts | 10 +- packages/types/src/extrinsic/v5/Extrinsic.ts | 2 +- .../src/extrinsic/v5/ExtrinsicPayload.ts | 113 ++++++++++++++++-- 3 files changed, 110 insertions(+), 15 deletions(-) diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 7271bb9edce3..5f8579c11f2a 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -3,19 +3,19 @@ import type { AnyJson, AnyTuple, AnyU8a, ArgsDef, IMethod, Inspect, IOption } from '@polkadot/types-codec/types'; import type { HexString } from '@polkadot/util/types'; -import type { EcdsaSignature, Ed25519Signature, ExtrinsicUnknown, ExtrinsicV4, Sr25519Signature } from '../interfaces/extrinsics/index.js'; +import type { EcdsaSignature, Ed25519Signature, ExtrinsicUnknown, ExtrinsicV5, Sr25519Signature } from '../interfaces/extrinsics/index.js'; import type { FunctionMetadataLatest } from '../interfaces/metadata/index.js'; import type { Address, Call, CodecHash, Hash } from '../interfaces/runtime/index.js'; import type { MultiLocation } from '../interfaces/types.js'; import type { CallBase, ExtrinsicPayloadValue, ICompact, IExtrinsic, IKeyringPair, INumber, Registry, SignatureOptions } from '../types/index.js'; import type { GenericExtrinsicEra } from './ExtrinsicEra.js'; import type { SubVersionV5 } from './types.js'; -import type { ExtrinsicValueV4 } from './v4/Extrinsic.js'; import { AbstractBase } from '@polkadot/types-codec'; import { compactAddLength, compactFromU8a, compactToU8a, isHex, isU8a, objectProperty, objectSpread, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; import { BIT_SIGNED, BIT_UNSIGNED, DEFAULT_V5_VERSION, DEFAULT_VERSION, LATEST_EXTRINSIC_VERSION, UNMASK_VERSION } from './constants.js'; +import type { ExtrinsicValueV5 } from './v5/Extrinsic.js'; interface CreateOptions { version?: number; @@ -25,8 +25,8 @@ interface CreateOptions { // NOTE The following 2 types, as well as the VERSION structure and the latest export // is to be changed with the addition of a new extrinsic version -type ExtrinsicVx = ExtrinsicV4; -type ExtrinsicValue = ExtrinsicValueV4; +type ExtrinsicVx = ExtrinsicV5; +type ExtrinsicValue = ExtrinsicValueV5; const VERSIONS = [ 'ExtrinsicUnknown', // v0 is unknown @@ -90,7 +90,7 @@ function decodeU8a (registry: Registry, value: Uint8Array, version: number, subV } abstract class ExtrinsicBase extends AbstractBase { - constructor (registry: Registry, value: ExtrinsicV4 | ExtrinsicUnknown, initialU8aLength?: number) { + constructor (registry: Registry, value: ExtrinsicV5 | ExtrinsicUnknown, initialU8aLength?: number) { super(registry, value, initialU8aLength); const signKeys = Object.keys(registry.getSignedExtensionTypes()); diff --git a/packages/types/src/extrinsic/v5/Extrinsic.ts b/packages/types/src/extrinsic/v5/Extrinsic.ts index 148346669aec..ec1fe65284f6 100644 --- a/packages/types/src/extrinsic/v5/Extrinsic.ts +++ b/packages/types/src/extrinsic/v5/Extrinsic.ts @@ -10,7 +10,7 @@ import type { ExtrinsicOptions } from '../types.js'; import { Struct } from '@polkadot/types-codec'; import { isU8a } from '@polkadot/util'; -export const EXTRINSIC_VERSION = 5; +export const EXTRINSIC_VERSION = 4; export interface ExtrinsicValueV5 { method?: Call; diff --git a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts index 05150263e375..641b40b20068 100644 --- a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts @@ -2,19 +2,34 @@ // SPDX-License-Identifier: Apache-2.0 import type { SignOptions } from '@polkadot/keyring/types'; -import type { Registry } from '@polkadot/types-codec/types'; +import type { Hash, MultiLocation } from '@polkadot/types/interfaces'; +import type { Bytes } from '@polkadot/types-codec'; +import type { Inspect, Registry } from '@polkadot/types-codec/types'; import type { HexString } from '@polkadot/util/types'; -import type { ExtrinsicPayloadValue, IKeyringPair } from '../../types/index.js'; +import type { BlockHash } from '../../interfaces/chain/index.js'; +import type { ExtrinsicEra } from '../../interfaces/extrinsics/index.js'; +import type { ExtrinsicPayloadValue, ICompact, IKeyringPair, INumber, IOption } from '../../types/index.js'; -import { Enum } from '@polkadot/types-codec'; +import { Enum, Struct } from '@polkadot/types-codec'; +import { objectSpread } from '@polkadot/util'; -import { GenericExtrinsicPayloadV4 } from '../v4/ExtrinsicPayload.js'; +import { sign } from '../util.js'; -export class GenericExtrinsicPayloadV5 extends GenericExtrinsicPayloadV4 { +/** + * @name GenericExtrinsicPayloadV5 + * @description + * A signing payload for an [[Extrinsic]]. For the final encoding, it is + * variable length based on the contents included + */ +export class GenericExtrinsicPayloadV5 extends Struct { #signOptions: SignOptions; constructor (registry: Registry, value?: ExtrinsicPayloadValue | Uint8Array | HexString) { - super(registry, value); + super(registry, objectSpread( + { 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 @@ -24,12 +39,92 @@ export class GenericExtrinsicPayloadV5 extends GenericExtrinsicPayloadV4 { }; } - public override sign (signerPair: IKeyringPair) { + /** + * @description Returns a breakdown of the hex encoding for this Codec + */ + public override inspect (): Inspect { + return super.inspect({ method: true }); + } + + /** + * @description The block [[BlockHash]] the signature applies to (mortal/immortal) + */ + public get blockHash (): BlockHash { + return this.getT('blockHash'); + } + + /** + * @description The [[ExtrinsicEra]] + */ + public get era (): ExtrinsicEra { + return this.getT('era'); + } + + /** + * @description The genesis [[BlockHash]] the signature applies to (mortal/immortal) + */ + public get genesisHash (): BlockHash { + return this.getT('genesisHash'); + } + + /** + * @description The [[Bytes]] contained in the payload + */ + public get method (): Bytes { + return this.getT('method'); + } + + /** + * @description The [[Index]] + */ + public get nonce (): ICompact { + return this.getT('nonce'); + } + + /** + * @description The specVersion for this signature + */ + public get specVersion (): INumber { + return this.getT('specVersion'); + } + + /** + * @description The tip [[Balance]] + */ + public get tip (): ICompact { + return this.getT('tip'); + } + + /** + * @description The transactionVersion for this signature + */ + public get transactionVersion (): INumber { + return this.getT('transactionVersion'); + } + + /** + * @description The (optional) asset id for this signature for chains that support transaction fees in assets + */ + public get assetId (): IOption { + return this.getT('assetId'); + } + + /** + * @description The (optional) asset id for this signature for chains that support transaction fees in assets + */ + public get metadataHash (): IOption { + return this.getT('metadataHash'); + } + + /** + * @description Sign the payload with the keypair + */ + public sign (signerPair: IKeyringPair): Uint8Array { // NOTE The `toU8a({ method: 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..V3, if we have a V6, carry this comment - return signerPair.sign(this.registry.hash(this.toU8a({ method: true })), this.#signOptions); + // The same applies to V1..V3, if we have a V5, carry this comment + return sign(this.registry, signerPair, this.toU8a({ method: true }), this.#signOptions); } } From c7a86bcf272b09c25d61108e695a04c4d6f108cc Mon Sep 17 00:00:00 2001 From: bee344 Date: Thu, 19 Sep 2024 20:18:25 -0300 Subject: [PATCH 11/71] general works? --- packages/types/src/extrinsic/Extrinsic.ts | 2 +- .../types/src/extrinsic/ExtrinsicPayload.ts | 6 ++--- packages/types/src/extrinsic/util.ts | 18 +++++++++++-- packages/types/src/extrinsic/v5/Extrinsic.ts | 26 ++++++++++++------- .../src/extrinsic/v5/ExtrinsicPayload.ts | 14 +++++++--- packages/types/src/types/extrinsic.ts | 11 ++++++++ packages/types/src/types/interfaces.ts | 4 +-- 7 files changed, 59 insertions(+), 22 deletions(-) diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 5f8579c11f2a..bdf5c79a96ba 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -40,7 +40,7 @@ const VERSIONS = [ const V5_VERSIONS = { bare: 'ExtrinsicV5', // Not supported yet - general: 'GeneralExtrinsicV5', + general: 'ExtrinsicV5', signed: 'ExtrinsicV5' }; diff --git a/packages/types/src/extrinsic/ExtrinsicPayload.ts b/packages/types/src/extrinsic/ExtrinsicPayload.ts index c74f80873fd8..58b1aeb2d94d 100644 --- a/packages/types/src/extrinsic/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/ExtrinsicPayload.ts @@ -5,7 +5,7 @@ import type { Bytes } from '@polkadot/types-codec'; import type { AnyJson, BareOpts, Registry } from '@polkadot/types-codec/types'; import type { HexString } from '@polkadot/util/types'; import type { BlockHash } from '../interfaces/chain/index.js'; -import type { ExtrinsicPayloadV4 } from '../interfaces/extrinsics/index.js'; +import type { ExtrinsicPayloadV4, ExtrinsicPayloadV5 } from '../interfaces/extrinsics/index.js'; import type { Hash, MultiLocation } from '../interfaces/types.js'; import type { ExtrinsicPayloadValue, ICompact, IKeyringPair, INumber, IOption } from '../types/index.js'; import type { GenericExtrinsicEra } from './ExtrinsicEra.js'; @@ -22,7 +22,7 @@ interface ExtrinsicPayloadOptions { } // all our known types that can be returned -type ExtrinsicPayloadVx = ExtrinsicPayloadV4; +type ExtrinsicPayloadVx = ExtrinsicPayloadV5; const VERSIONS = [ 'ExtrinsicPayloadUnknown', // v0 is unknown @@ -36,7 +36,7 @@ const VERSIONS = [ const V5_VERSIONS = { bare: 'ExtrinsicPayloadV5', // Not supported yet - general: 'GeneralExtrinsicPayloadV5', + general: 'ExtrinsicPayloadV5', signed: 'ExtrinsicPayloadV5' }; diff --git a/packages/types/src/extrinsic/util.ts b/packages/types/src/extrinsic/util.ts index 84a5361b19be..842b1a47cca4 100644 --- a/packages/types/src/extrinsic/util.ts +++ b/packages/types/src/extrinsic/util.ts @@ -1,9 +1,9 @@ // Copyright 2017-2024 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { SignOptions } from '@polkadot/keyring/types'; import type { Registry } from '@polkadot/types-codec/types'; import type { IKeyringPair } from '../types/index.js'; +import type { SignOptions, SignV5Options } from '@polkadot/keyring/types'; // a helper function for both types of payloads, Raw and metadata-known export function sign (registry: Registry, signerPair: IKeyringPair, u8a: Uint8Array, options?: SignOptions): Uint8Array { @@ -11,5 +11,19 @@ export function sign (registry: Registry, signerPair: IKeyringPair, u8a: Uint8Ar ? registry.hash(u8a) : u8a; - return signerPair.sign(encoded, options); + return signerPair.sign(encoded, options); + } + +// a helper function for both types of payloads, Raw and metadata-known +export function signV5(registry: Registry, signerPair: IKeyringPair, u8a: Uint8Array, options?: SignV5Options): Uint8Array { + // if (options?.subVersionV5 === 'general') { + // return signerPair.sign(registry.hash(u8a), options); + // } else { + const encoded = u8a.length > 256 + ? registry.hash(u8a) + : u8a; + + return signerPair.sign(encoded, options); + // } + } diff --git a/packages/types/src/extrinsic/v5/Extrinsic.ts b/packages/types/src/extrinsic/v5/Extrinsic.ts index ec1fe65284f6..58a147750172 100644 --- a/packages/types/src/extrinsic/v5/Extrinsic.ts +++ b/packages/types/src/extrinsic/v5/Extrinsic.ts @@ -4,13 +4,11 @@ import type { HexString } from '@polkadot/util/types'; import type { ExtrinsicSignatureV5 } from '../../interfaces/extrinsics/index.js'; import type { Address, Call } from '../../interfaces/runtime/index.js'; -import type { ExtrinsicPayloadValue, IExtrinsicImpl, IKeyringPair, Registry, SignatureOptions } from '../../types/index.js'; +import type { ExtrinsicPayloadValue, IExtrinsicV5Impl, IKeyringPair, Registry, SignatureV5Options } from '../../types/index.js'; import type { ExtrinsicOptions } from '../types.js'; import { Struct } from '@polkadot/types-codec'; -import { isU8a } from '@polkadot/util'; - -export const EXTRINSIC_VERSION = 4; +import { isU8a, objectSpread } from '@polkadot/util'; export interface ExtrinsicValueV5 { method?: Call; @@ -22,7 +20,7 @@ export interface ExtrinsicValueV5 { * @description * The third generation of compact extrinsics */ -export class GenericExtrinsicV5 extends Struct implements IExtrinsicImpl { +export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { constructor (registry: Registry, value?: Uint8Array | ExtrinsicValueV5 | Call, { isSigned }: Partial = {}) { super(registry, { signature: 'ExtrinsicSignatureV5', @@ -76,7 +74,11 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicImpl { * @description The version for the signature */ public get version (): number { - return EXTRINSIC_VERSION; + return this.getT('version'); + } + + public get subVersionV5 (): 'signed' | 'bare' | 'general' { + return this.getT('subVersionV5') } /** @@ -91,8 +93,10 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicImpl { /** * @description Sign the extrinsic with a specific keypair */ - public sign (account: IKeyringPair, options: SignatureOptions): GenericExtrinsicV5 { - this.signature.sign(this.method, account, options); + public sign (account: IKeyringPair, options: SignatureV5Options): GenericExtrinsicV5 { + const subVersionV5 = this.subVersionV5; + const newOpts = objectSpread({}, { ...options, subVersionV5}) as SignatureV5Options; + this.signature.sign(this.method, account, newOpts); return this; } @@ -100,8 +104,10 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicImpl { /** * @describe Adds a fake signature to the extrinsic */ - public signFake (signer: Address | Uint8Array | string, options: SignatureOptions): GenericExtrinsicV5 { - this.signature.signFake(this.method, signer, options); + public signFake (signer: Address | Uint8Array | string, options: SignatureV5Options): GenericExtrinsicV5 { + const subVersionV5 = this.subVersionV5; + const newOpts = objectSpread({}, { ...options, subVersionV5}) as SignatureV5Options; + this.signature.signFake(this.method, signer, newOpts); return this; } diff --git a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts index 641b40b20068..15230ccf2f95 100644 --- a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts @@ -1,7 +1,7 @@ // Copyright 2017-2024 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { SignOptions } from '@polkadot/keyring/types'; +import type { SignOptions, SignV5Options } from '@polkadot/keyring/types'; import type { Hash, MultiLocation } from '@polkadot/types/interfaces'; import type { Bytes } from '@polkadot/types-codec'; import type { Inspect, Registry } from '@polkadot/types-codec/types'; @@ -13,7 +13,7 @@ import type { ExtrinsicPayloadValue, ICompact, IKeyringPair, INumber, IOption } import { Enum, Struct } from '@polkadot/types-codec'; import { objectSpread } from '@polkadot/util'; -import { sign } from '../util.js'; +import { signV5 } from '../util.js'; /** * @name GenericExtrinsicPayloadV5 @@ -22,7 +22,7 @@ import { sign } from '../util.js'; * variable length based on the contents included */ export class GenericExtrinsicPayloadV5 extends Struct { - #signOptions: SignOptions; + #signOptions: SignV5Options; constructor (registry: Registry, value?: ExtrinsicPayloadValue | Uint8Array | HexString) { super(registry, objectSpread( @@ -116,6 +116,10 @@ export class GenericExtrinsicPayloadV5 extends Struct { return this.getT('metadataHash'); } + public get subVersionV5 (): 'signed' | 'bare' | 'general' { + return this.getT('subVersionV5'); + } + /** * @description Sign the payload with the keypair */ @@ -125,6 +129,8 @@ export class GenericExtrinsicPayloadV5 extends Struct { // 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 this comment - return sign(this.registry, signerPair, this.toU8a({ method: true }), this.#signOptions); + const subVersionV5 = this.subVersionV5; + const newOpts = objectSpread({} as SignV5Options, {...this.#signOptions, subVersionV5}) + return signV5(this.registry, signerPair, this.toU8a({ method: true }), newOpts); } } diff --git a/packages/types/src/types/extrinsic.ts b/packages/types/src/types/extrinsic.ts index 6a0fe33cdd46..8fbe9a9a535e 100644 --- a/packages/types/src/types/extrinsic.ts +++ b/packages/types/src/types/extrinsic.ts @@ -202,6 +202,10 @@ export interface SignatureOptions { withSignedTransaction?: boolean; } +export interface SignatureV5Options extends SignatureOptions { + subVersionV5?: 'signed' | 'bare' | 'general'; +} + interface ExtrinsicSignatureBase { readonly isSigned: boolean; readonly era: IExtrinsicEra; @@ -247,6 +251,13 @@ export interface IExtrinsicImpl extends IExtrinsicSignable { readonly version: number; } +export interface IExtrinsicV5Impl extends IExtrinsicSignable { + readonly method: Call; + readonly signature: IExtrinsicSignature; + readonly version: number; + readonly subVersionV5: 'signed' | 'bare' | 'general' +} + export interface IExtrinsic extends IExtrinsicSignable>, ExtrinsicSignatureBase, IMethod { readonly length: number; readonly method: IMethod; diff --git a/packages/types/src/types/interfaces.ts b/packages/types/src/types/interfaces.ts index 6b7564c37e11..05c7ff5a4453 100644 --- a/packages/types/src/types/interfaces.ts +++ b/packages/types/src/types/interfaces.ts @@ -1,7 +1,7 @@ // Copyright 2017-2024 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { SignOptions } from '@polkadot/keyring/types'; +import type { SignV5Options } from '@polkadot/keyring/types'; import type { AnyTuple, Codec, IMethod as IMethodBase, INumber, IText } from '@polkadot/types-codec/types'; import type { FunctionMetadataLatest, StorageEntryMetadataLatest } from '../interfaces/metadata/index.js'; import type { Registry } from './registry.js'; @@ -17,7 +17,7 @@ export interface IKeyringPair { readonly addressRaw: Uint8Array; readonly publicKey: Uint8Array; - sign: (data: Uint8Array, options?: SignOptions) => Uint8Array; + sign: (data: Uint8Array, options?: SignV5Options) => Uint8Array; } export interface IRuntimeVersionBase { From 437b8858bad2abb2376cced9b74b7d9211c5105f Mon Sep 17 00:00:00 2001 From: tarikgul Date: Fri, 20 Sep 2024 09:14:28 -0400 Subject: [PATCH 12/71] Clean linter, and imports --- packages/types/src/extrinsic/Extrinsic.ts | 2 +- .../types/src/extrinsic/ExtrinsicPayload.ts | 2 +- packages/types/src/extrinsic/types.ts | 5 +++++ packages/types/src/extrinsic/util.ts | 18 +++++++++--------- packages/types/src/extrinsic/v5/Extrinsic.ts | 8 +++++--- .../types/src/extrinsic/v5/ExtrinsicPayload.ts | 5 +++-- packages/types/src/types/extrinsic.ts | 2 +- packages/types/src/types/interfaces.ts | 2 +- 8 files changed, 26 insertions(+), 18 deletions(-) diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index bdf5c79a96ba..33dbc1d73e98 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -10,12 +10,12 @@ import type { MultiLocation } from '../interfaces/types.js'; import type { CallBase, ExtrinsicPayloadValue, ICompact, IExtrinsic, IKeyringPair, INumber, Registry, SignatureOptions } from '../types/index.js'; import type { GenericExtrinsicEra } from './ExtrinsicEra.js'; import type { SubVersionV5 } from './types.js'; +import type { ExtrinsicValueV5 } from './v5/Extrinsic.js'; import { AbstractBase } from '@polkadot/types-codec'; import { compactAddLength, compactFromU8a, compactToU8a, isHex, isU8a, objectProperty, objectSpread, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; import { BIT_SIGNED, BIT_UNSIGNED, DEFAULT_V5_VERSION, DEFAULT_VERSION, LATEST_EXTRINSIC_VERSION, UNMASK_VERSION } from './constants.js'; -import type { ExtrinsicValueV5 } from './v5/Extrinsic.js'; interface CreateOptions { version?: number; diff --git a/packages/types/src/extrinsic/ExtrinsicPayload.ts b/packages/types/src/extrinsic/ExtrinsicPayload.ts index 58b1aeb2d94d..532c6823a7a0 100644 --- a/packages/types/src/extrinsic/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/ExtrinsicPayload.ts @@ -5,7 +5,7 @@ import type { Bytes } from '@polkadot/types-codec'; import type { AnyJson, BareOpts, Registry } from '@polkadot/types-codec/types'; import type { HexString } from '@polkadot/util/types'; import type { BlockHash } from '../interfaces/chain/index.js'; -import type { ExtrinsicPayloadV4, ExtrinsicPayloadV5 } from '../interfaces/extrinsics/index.js'; +import type { ExtrinsicPayloadV5 } from '../interfaces/extrinsics/index.js'; import type { Hash, MultiLocation } from '../interfaces/types.js'; import type { ExtrinsicPayloadValue, ICompact, IKeyringPair, INumber, IOption } from '../types/index.js'; import type { GenericExtrinsicEra } from './ExtrinsicEra.js'; diff --git a/packages/types/src/extrinsic/types.ts b/packages/types/src/extrinsic/types.ts index ebb1560e53e5..eed43b19732d 100644 --- a/packages/types/src/extrinsic/types.ts +++ b/packages/types/src/extrinsic/types.ts @@ -1,6 +1,7 @@ // Copyright 2017-2024 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { SignOptions } from '@polkadot/keyring/types'; import type { AnyNumber } from '@polkadot/types-codec/types'; export interface ExtrinsicOptions { @@ -23,3 +24,7 @@ export interface ExtrinsicExtraValue { } export type SubVersionV5 = 'signed' | 'bare' | 'general'; + +export interface SignV5Options extends SignOptions { + subVersionV5?: SubVersionV5; +} diff --git a/packages/types/src/extrinsic/util.ts b/packages/types/src/extrinsic/util.ts index 842b1a47cca4..f3d4dc2b573a 100644 --- a/packages/types/src/extrinsic/util.ts +++ b/packages/types/src/extrinsic/util.ts @@ -1,9 +1,10 @@ // Copyright 2017-2024 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { SignOptions } from '@polkadot/keyring/types'; import type { Registry } from '@polkadot/types-codec/types'; import type { IKeyringPair } from '../types/index.js'; -import type { SignOptions, SignV5Options } from '@polkadot/keyring/types'; +import type { SignV5Options } from './types.js'; // a helper function for both types of payloads, Raw and metadata-known export function sign (registry: Registry, signerPair: IKeyringPair, u8a: Uint8Array, options?: SignOptions): Uint8Array { @@ -11,19 +12,18 @@ export function sign (registry: Registry, signerPair: IKeyringPair, u8a: Uint8Ar ? registry.hash(u8a) : u8a; - return signerPair.sign(encoded, options); - } + return signerPair.sign(encoded, options); +} // a helper function for both types of payloads, Raw and metadata-known -export function signV5(registry: Registry, signerPair: IKeyringPair, u8a: Uint8Array, options?: SignV5Options): Uint8Array { +export function signV5 (registry: Registry, signerPair: IKeyringPair, u8a: Uint8Array, options?: SignV5Options): Uint8Array { // if (options?.subVersionV5 === 'general') { // return signerPair.sign(registry.hash(u8a), options); // } else { - const encoded = u8a.length > 256 - ? registry.hash(u8a) - : u8a; + const encoded = u8a.length > 256 + ? registry.hash(u8a) + : u8a; - return signerPair.sign(encoded, options); + return signerPair.sign(encoded, options); // } - } diff --git a/packages/types/src/extrinsic/v5/Extrinsic.ts b/packages/types/src/extrinsic/v5/Extrinsic.ts index 58a147750172..0ccfcd3eb623 100644 --- a/packages/types/src/extrinsic/v5/Extrinsic.ts +++ b/packages/types/src/extrinsic/v5/Extrinsic.ts @@ -78,7 +78,7 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { } public get subVersionV5 (): 'signed' | 'bare' | 'general' { - return this.getT('subVersionV5') + return this.getT('subVersionV5'); } /** @@ -95,7 +95,8 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { */ public sign (account: IKeyringPair, options: SignatureV5Options): GenericExtrinsicV5 { const subVersionV5 = this.subVersionV5; - const newOpts = objectSpread({}, { ...options, subVersionV5}) as SignatureV5Options; + const newOpts = objectSpread({}, { ...options, subVersionV5 }); + this.signature.sign(this.method, account, newOpts); return this; @@ -106,7 +107,8 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { */ public signFake (signer: Address | Uint8Array | string, options: SignatureV5Options): GenericExtrinsicV5 { const subVersionV5 = this.subVersionV5; - const newOpts = objectSpread({}, { ...options, subVersionV5}) as SignatureV5Options; + const newOpts = objectSpread({}, { ...options, subVersionV5 }); + this.signature.signFake(this.method, signer, newOpts); return this; diff --git a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts index 15230ccf2f95..159184de6cbe 100644 --- a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts @@ -1,7 +1,6 @@ // Copyright 2017-2024 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { SignOptions, SignV5Options } from '@polkadot/keyring/types'; import type { Hash, MultiLocation } from '@polkadot/types/interfaces'; import type { Bytes } from '@polkadot/types-codec'; import type { Inspect, Registry } from '@polkadot/types-codec/types'; @@ -9,6 +8,7 @@ import type { HexString } from '@polkadot/util/types'; import type { BlockHash } from '../../interfaces/chain/index.js'; import type { ExtrinsicEra } from '../../interfaces/extrinsics/index.js'; import type { ExtrinsicPayloadValue, ICompact, IKeyringPair, INumber, IOption } from '../../types/index.js'; +import type { SignV5Options } from '../types.js'; import { Enum, Struct } from '@polkadot/types-codec'; import { objectSpread } from '@polkadot/util'; @@ -130,7 +130,8 @@ export class GenericExtrinsicPayloadV5 extends Struct { // the extra information, only the pure data (and is not decoded) ... // The same applies to V1..V3, if we have a V5, carry this comment const subVersionV5 = this.subVersionV5; - const newOpts = objectSpread({} as SignV5Options, {...this.#signOptions, subVersionV5}) + const newOpts = objectSpread({} as SignV5Options, { ...this.#signOptions, subVersionV5 }); + return signV5(this.registry, signerPair, this.toU8a({ method: true }), newOpts); } } diff --git a/packages/types/src/types/extrinsic.ts b/packages/types/src/types/extrinsic.ts index 8fbe9a9a535e..7368df735058 100644 --- a/packages/types/src/types/extrinsic.ts +++ b/packages/types/src/types/extrinsic.ts @@ -203,7 +203,7 @@ export interface SignatureOptions { } export interface SignatureV5Options extends SignatureOptions { - subVersionV5?: 'signed' | 'bare' | 'general'; + subVersionV5?: 'signed' | 'bare' | 'general'; } interface ExtrinsicSignatureBase { diff --git a/packages/types/src/types/interfaces.ts b/packages/types/src/types/interfaces.ts index 05c7ff5a4453..670f6ced7119 100644 --- a/packages/types/src/types/interfaces.ts +++ b/packages/types/src/types/interfaces.ts @@ -1,8 +1,8 @@ // Copyright 2017-2024 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { SignV5Options } from '@polkadot/keyring/types'; import type { AnyTuple, Codec, IMethod as IMethodBase, INumber, IText } from '@polkadot/types-codec/types'; +import type { SignV5Options } from '../extrinsic/types.js'; import type { FunctionMetadataLatest, StorageEntryMetadataLatest } from '../interfaces/metadata/index.js'; import type { Registry } from './registry.js'; From 378949cb970dc368bbcf0d34cd1beb947f16d9b4 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Fri, 20 Sep 2024 09:20:56 -0400 Subject: [PATCH 13/71] fix typing --- packages/types/src/extrinsic/v5/Extrinsic.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/types/src/extrinsic/v5/Extrinsic.ts b/packages/types/src/extrinsic/v5/Extrinsic.ts index 0ccfcd3eb623..152f8b22bcac 100644 --- a/packages/types/src/extrinsic/v5/Extrinsic.ts +++ b/packages/types/src/extrinsic/v5/Extrinsic.ts @@ -95,7 +95,7 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { */ public sign (account: IKeyringPair, options: SignatureV5Options): GenericExtrinsicV5 { const subVersionV5 = this.subVersionV5; - const newOpts = objectSpread({}, { ...options, subVersionV5 }); + const newOpts: SignatureV5Options = objectSpread({}, { ...options, subVersionV5 }); this.signature.sign(this.method, account, newOpts); @@ -107,7 +107,7 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { */ public signFake (signer: Address | Uint8Array | string, options: SignatureV5Options): GenericExtrinsicV5 { const subVersionV5 = this.subVersionV5; - const newOpts = objectSpread({}, { ...options, subVersionV5 }); + const newOpts: SignatureV5Options = objectSpread({}, { ...options, subVersionV5 }); this.signature.signFake(this.method, signer, newOpts); From 2c3e2854f63c63ca5f270fc9a85d81ef1ec1b64c Mon Sep 17 00:00:00 2001 From: tarikgul Date: Fri, 20 Sep 2024 10:28:44 -0400 Subject: [PATCH 14/71] Change sign in v5 to hash all payloads --- packages/types/src/extrinsic/util.ts | 14 -------------- .../types/src/extrinsic/v5/ExtrinsicPayload.ts | 9 ++------- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/packages/types/src/extrinsic/util.ts b/packages/types/src/extrinsic/util.ts index f3d4dc2b573a..84a5361b19be 100644 --- a/packages/types/src/extrinsic/util.ts +++ b/packages/types/src/extrinsic/util.ts @@ -4,7 +4,6 @@ import type { SignOptions } from '@polkadot/keyring/types'; import type { Registry } from '@polkadot/types-codec/types'; import type { IKeyringPair } from '../types/index.js'; -import type { SignV5Options } from './types.js'; // a helper function for both types of payloads, Raw and metadata-known export function sign (registry: Registry, signerPair: IKeyringPair, u8a: Uint8Array, options?: SignOptions): Uint8Array { @@ -14,16 +13,3 @@ export function sign (registry: Registry, signerPair: IKeyringPair, u8a: Uint8Ar return signerPair.sign(encoded, options); } - -// a helper function for both types of payloads, Raw and metadata-known -export function signV5 (registry: Registry, signerPair: IKeyringPair, u8a: Uint8Array, options?: SignV5Options): Uint8Array { - // if (options?.subVersionV5 === 'general') { - // return signerPair.sign(registry.hash(u8a), options); - // } else { - const encoded = u8a.length > 256 - ? registry.hash(u8a) - : u8a; - - return signerPair.sign(encoded, options); - // } -} diff --git a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts index 159184de6cbe..72532166ac7f 100644 --- a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts @@ -13,8 +13,6 @@ import type { SignV5Options } from '../types.js'; import { Enum, Struct } from '@polkadot/types-codec'; import { objectSpread } from '@polkadot/util'; -import { signV5 } from '../util.js'; - /** * @name GenericExtrinsicPayloadV5 * @description @@ -128,10 +126,7 @@ export class GenericExtrinsicPayloadV5 extends Struct { // 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..V3, if we have a V5, carry this comment - const subVersionV5 = this.subVersionV5; - const newOpts = objectSpread({} as SignV5Options, { ...this.#signOptions, subVersionV5 }); - - return signV5(this.registry, signerPair, this.toU8a({ method: true }), newOpts); + // The same applies to V1..V3, if we have a V6, carry this comment + return signerPair.sign(this.registry.hash(this.toU8a({ method: true })), this.#signOptions); } } From 72de8df5d0d99147347b6aa80148367e1c26e82c Mon Sep 17 00:00:00 2001 From: tarikgul Date: Fri, 20 Sep 2024 10:32:58 -0400 Subject: [PATCH 15/71] remove SignV5Options --- packages/types/src/extrinsic/types.ts | 5 ----- packages/types/src/extrinsic/v5/ExtrinsicPayload.ts | 4 ++-- packages/types/src/types/interfaces.ts | 4 ++-- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/types/src/extrinsic/types.ts b/packages/types/src/extrinsic/types.ts index eed43b19732d..ebb1560e53e5 100644 --- a/packages/types/src/extrinsic/types.ts +++ b/packages/types/src/extrinsic/types.ts @@ -1,7 +1,6 @@ // Copyright 2017-2024 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { SignOptions } from '@polkadot/keyring/types'; import type { AnyNumber } from '@polkadot/types-codec/types'; export interface ExtrinsicOptions { @@ -24,7 +23,3 @@ export interface ExtrinsicExtraValue { } export type SubVersionV5 = 'signed' | 'bare' | 'general'; - -export interface SignV5Options extends SignOptions { - subVersionV5?: SubVersionV5; -} diff --git a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts index 72532166ac7f..beda1f1db3e8 100644 --- a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts @@ -1,6 +1,7 @@ // Copyright 2017-2024 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { SignOptions } from '@polkadot/keyring/types'; import type { Hash, MultiLocation } from '@polkadot/types/interfaces'; import type { Bytes } from '@polkadot/types-codec'; import type { Inspect, Registry } from '@polkadot/types-codec/types'; @@ -8,7 +9,6 @@ import type { HexString } from '@polkadot/util/types'; import type { BlockHash } from '../../interfaces/chain/index.js'; import type { ExtrinsicEra } from '../../interfaces/extrinsics/index.js'; import type { ExtrinsicPayloadValue, ICompact, IKeyringPair, INumber, IOption } from '../../types/index.js'; -import type { SignV5Options } from '../types.js'; import { Enum, Struct } from '@polkadot/types-codec'; import { objectSpread } from '@polkadot/util'; @@ -20,7 +20,7 @@ import { objectSpread } from '@polkadot/util'; * variable length based on the contents included */ export class GenericExtrinsicPayloadV5 extends Struct { - #signOptions: SignV5Options; + #signOptions: SignOptions; constructor (registry: Registry, value?: ExtrinsicPayloadValue | Uint8Array | HexString) { super(registry, objectSpread( diff --git a/packages/types/src/types/interfaces.ts b/packages/types/src/types/interfaces.ts index 670f6ced7119..6b7564c37e11 100644 --- a/packages/types/src/types/interfaces.ts +++ b/packages/types/src/types/interfaces.ts @@ -1,8 +1,8 @@ // Copyright 2017-2024 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { SignOptions } from '@polkadot/keyring/types'; import type { AnyTuple, Codec, IMethod as IMethodBase, INumber, IText } from '@polkadot/types-codec/types'; -import type { SignV5Options } from '../extrinsic/types.js'; import type { FunctionMetadataLatest, StorageEntryMetadataLatest } from '../interfaces/metadata/index.js'; import type { Registry } from './registry.js'; @@ -17,7 +17,7 @@ export interface IKeyringPair { readonly addressRaw: Uint8Array; readonly publicKey: Uint8Array; - sign: (data: Uint8Array, options?: SignV5Options) => Uint8Array; + sign: (data: Uint8Array, options?: SignOptions) => Uint8Array; } export interface IRuntimeVersionBase { From 13b40c53b6e15136000095638eceff73b261f61f Mon Sep 17 00:00:00 2001 From: tarikgul Date: Fri, 20 Sep 2024 11:19:13 -0400 Subject: [PATCH 16/71] Remove SignatureV5Options --- packages/types/src/extrinsic/v5/Extrinsic.ts | 22 +++++-------------- .../src/extrinsic/v5/ExtrinsicPayload.ts | 4 ---- packages/types/src/types/extrinsic.ts | 5 ----- 3 files changed, 6 insertions(+), 25 deletions(-) diff --git a/packages/types/src/extrinsic/v5/Extrinsic.ts b/packages/types/src/extrinsic/v5/Extrinsic.ts index 152f8b22bcac..70f61565f237 100644 --- a/packages/types/src/extrinsic/v5/Extrinsic.ts +++ b/packages/types/src/extrinsic/v5/Extrinsic.ts @@ -4,11 +4,11 @@ import type { HexString } from '@polkadot/util/types'; import type { ExtrinsicSignatureV5 } from '../../interfaces/extrinsics/index.js'; import type { Address, Call } from '../../interfaces/runtime/index.js'; -import type { ExtrinsicPayloadValue, IExtrinsicV5Impl, IKeyringPair, Registry, SignatureV5Options } from '../../types/index.js'; +import type { ExtrinsicPayloadValue, IExtrinsicV5Impl, IKeyringPair, Registry, SignatureOptions } from '../../types/index.js'; import type { ExtrinsicOptions } from '../types.js'; import { Struct } from '@polkadot/types-codec'; -import { isU8a, objectSpread } from '@polkadot/util'; +import { isU8a } from '@polkadot/util'; export interface ExtrinsicValueV5 { method?: Call; @@ -77,10 +77,6 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { return this.getT('version'); } - public get subVersionV5 (): 'signed' | 'bare' | 'general' { - return this.getT('subVersionV5'); - } - /** * @description Add an [[ExtrinsicSignatureV5]] to the extrinsic (already generated) */ @@ -93,11 +89,8 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { /** * @description Sign the extrinsic with a specific keypair */ - public sign (account: IKeyringPair, options: SignatureV5Options): GenericExtrinsicV5 { - const subVersionV5 = this.subVersionV5; - const newOpts: SignatureV5Options = objectSpread({}, { ...options, subVersionV5 }); - - this.signature.sign(this.method, account, newOpts); + public sign (account: IKeyringPair, options: SignatureOptions): GenericExtrinsicV5 { + this.signature.sign(this.method, account, options); return this; } @@ -105,11 +98,8 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { /** * @describe Adds a fake signature to the extrinsic */ - public signFake (signer: Address | Uint8Array | string, options: SignatureV5Options): GenericExtrinsicV5 { - const subVersionV5 = this.subVersionV5; - const newOpts: SignatureV5Options = objectSpread({}, { ...options, subVersionV5 }); - - this.signature.signFake(this.method, signer, newOpts); + public signFake (signer: Address | Uint8Array | string, options: SignatureOptions): GenericExtrinsicV5 { + this.signature.signFake(this.method, signer, options); return this; } diff --git a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts index beda1f1db3e8..9cd66e724613 100644 --- a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts @@ -114,10 +114,6 @@ export class GenericExtrinsicPayloadV5 extends Struct { return this.getT('metadataHash'); } - public get subVersionV5 (): 'signed' | 'bare' | 'general' { - return this.getT('subVersionV5'); - } - /** * @description Sign the payload with the keypair */ diff --git a/packages/types/src/types/extrinsic.ts b/packages/types/src/types/extrinsic.ts index 7368df735058..ed0546aef045 100644 --- a/packages/types/src/types/extrinsic.ts +++ b/packages/types/src/types/extrinsic.ts @@ -202,10 +202,6 @@ export interface SignatureOptions { withSignedTransaction?: boolean; } -export interface SignatureV5Options extends SignatureOptions { - subVersionV5?: 'signed' | 'bare' | 'general'; -} - interface ExtrinsicSignatureBase { readonly isSigned: boolean; readonly era: IExtrinsicEra; @@ -255,7 +251,6 @@ export interface IExtrinsicV5Impl extends IExtrinsicSignable { readonly method: Call; readonly signature: IExtrinsicSignature; readonly version: number; - readonly subVersionV5: 'signed' | 'bare' | 'general' } export interface IExtrinsic extends IExtrinsicSignable>, ExtrinsicSignatureBase, IMethod { From d92fa6e8c8232e3c912f595bd6cde99771233034 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Fri, 20 Sep 2024 11:46:35 -0400 Subject: [PATCH 17/71] Change versioning --- packages/types/src/extrinsic/v5/Extrinsic.ts | 4 +- .../src/extrinsic/v5/ExtrinsicPayload.ts | 100 ++---------------- 2 files changed, 11 insertions(+), 93 deletions(-) diff --git a/packages/types/src/extrinsic/v5/Extrinsic.ts b/packages/types/src/extrinsic/v5/Extrinsic.ts index 70f61565f237..955403604a24 100644 --- a/packages/types/src/extrinsic/v5/Extrinsic.ts +++ b/packages/types/src/extrinsic/v5/Extrinsic.ts @@ -10,6 +10,8 @@ import type { ExtrinsicOptions } from '../types.js'; import { Struct } from '@polkadot/types-codec'; import { isU8a } from '@polkadot/util'; +export const EXTRINSIC_VERSION = 5; + export interface ExtrinsicValueV5 { method?: Call; signature?: ExtrinsicSignatureV5; @@ -74,7 +76,7 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { * @description The version for the signature */ public get version (): number { - return this.getT('version'); + return EXTRINSIC_VERSION; } /** diff --git a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts index 9cd66e724613..e01c13d94a0b 100644 --- a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts @@ -2,16 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 import type { SignOptions } from '@polkadot/keyring/types'; -import type { Hash, MultiLocation } from '@polkadot/types/interfaces'; -import type { Bytes } from '@polkadot/types-codec'; -import type { Inspect, Registry } from '@polkadot/types-codec/types'; +import type { Registry } from '@polkadot/types-codec/types'; import type { HexString } from '@polkadot/util/types'; -import type { BlockHash } from '../../interfaces/chain/index.js'; -import type { ExtrinsicEra } from '../../interfaces/extrinsics/index.js'; -import type { ExtrinsicPayloadValue, ICompact, IKeyringPair, INumber, IOption } from '../../types/index.js'; +import type { ExtrinsicPayloadValue, IKeyringPair } from '../../types/index.js'; -import { Enum, Struct } from '@polkadot/types-codec'; -import { objectSpread } from '@polkadot/util'; +import { Enum } from '@polkadot/types-codec'; + +import { GenericExtrinsicPayloadV4 } from '../v4/ExtrinsicPayload.js'; /** * @name GenericExtrinsicPayloadV5 @@ -19,15 +16,11 @@ import { objectSpread } from '@polkadot/util'; * A signing payload for an [[Extrinsic]]. For the final encoding, it is * variable length based on the contents included */ -export class GenericExtrinsicPayloadV5 extends Struct { +export class GenericExtrinsicPayloadV5 extends GenericExtrinsicPayloadV4 { #signOptions: SignOptions; constructor (registry: Registry, value?: ExtrinsicPayloadValue | Uint8Array | HexString) { - super(registry, objectSpread( - { method: 'Bytes' }, - registry.getSignedExtensionTypes(), - registry.getSignedExtensionExtra() - ), value); + super(registry, 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 @@ -37,87 +30,10 @@ export class GenericExtrinsicPayloadV5 extends Struct { }; } - /** - * @description Returns a breakdown of the hex encoding for this Codec - */ - public override inspect (): Inspect { - return super.inspect({ method: true }); - } - - /** - * @description The block [[BlockHash]] the signature applies to (mortal/immortal) - */ - public get blockHash (): BlockHash { - return this.getT('blockHash'); - } - - /** - * @description The [[ExtrinsicEra]] - */ - public get era (): ExtrinsicEra { - return this.getT('era'); - } - - /** - * @description The genesis [[BlockHash]] the signature applies to (mortal/immortal) - */ - public get genesisHash (): BlockHash { - return this.getT('genesisHash'); - } - - /** - * @description The [[Bytes]] contained in the payload - */ - public get method (): Bytes { - return this.getT('method'); - } - - /** - * @description The [[Index]] - */ - public get nonce (): ICompact { - return this.getT('nonce'); - } - - /** - * @description The specVersion for this signature - */ - public get specVersion (): INumber { - return this.getT('specVersion'); - } - - /** - * @description The tip [[Balance]] - */ - public get tip (): ICompact { - return this.getT('tip'); - } - - /** - * @description The transactionVersion for this signature - */ - public get transactionVersion (): INumber { - return this.getT('transactionVersion'); - } - - /** - * @description The (optional) asset id for this signature for chains that support transaction fees in assets - */ - public get assetId (): IOption { - return this.getT('assetId'); - } - - /** - * @description The (optional) asset id for this signature for chains that support transaction fees in assets - */ - public get metadataHash (): IOption { - return this.getT('metadataHash'); - } - /** * @description Sign the payload with the keypair */ - public sign (signerPair: IKeyringPair): Uint8Array { + public override sign (signerPair: IKeyringPair): Uint8Array { // NOTE The `toU8a({ method: 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 From b4a2f0fa0493f4f706e288b71ea40298cdf1b8ec Mon Sep 17 00:00:00 2001 From: bee344 Date: Fri, 20 Sep 2024 15:56:15 -0300 Subject: [PATCH 18/71] added bitmasks --- packages/types/src/extrinsic/Extrinsic.ts | 44 ++++++++++++------- .../types/src/extrinsic/ExtrinsicPayload.ts | 16 +++---- packages/types/src/extrinsic/constants.ts | 14 +++++- packages/types/src/extrinsic/types.ts | 2 +- packages/types/src/extrinsic/util.ts | 7 +++ packages/types/src/extrinsic/v5/Extrinsic.ts | 6 ++- .../src/extrinsic/v5/ExtrinsicPayload.ts | 3 +- 7 files changed, 64 insertions(+), 28 deletions(-) diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 33dbc1d73e98..5d73c0417484 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -9,17 +9,17 @@ import type { Address, Call, CodecHash, Hash } from '../interfaces/runtime/index import type { MultiLocation } from '../interfaces/types.js'; import type { CallBase, ExtrinsicPayloadValue, ICompact, IExtrinsic, IKeyringPair, INumber, Registry, SignatureOptions } from '../types/index.js'; import type { GenericExtrinsicEra } from './ExtrinsicEra.js'; -import type { SubVersionV5 } from './types.js'; +import type { Preamble } from './types.js'; import type { ExtrinsicValueV5 } from './v5/Extrinsic.js'; import { AbstractBase } from '@polkadot/types-codec'; import { compactAddLength, compactFromU8a, compactToU8a, isHex, isU8a, objectProperty, objectSpread, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; -import { BIT_SIGNED, BIT_UNSIGNED, DEFAULT_V5_VERSION, DEFAULT_VERSION, LATEST_EXTRINSIC_VERSION, UNMASK_VERSION } from './constants.js'; +import { BARE_EXTRINSIC, BIT_SIGNED, BIT_UNSIGNED, DEFAULT_PREAMBLE, DEFAULT_VERSION, GENERAL_EXTRINSIC, LATEST_EXTRINSIC_VERSION, SIGNED_EXTRINSIC, UNMASK_VERSION } from './constants.js'; interface CreateOptions { version?: number; - subVersionV5?: SubVersionV5; + preamble?: Preamble; } // NOTE The following 2 types, as well as the VERSION structure and the latest export @@ -37,23 +37,29 @@ const VERSIONS = [ 'ExtrinsicV5' ]; -const V5_VERSIONS = { +const PREAMBLE = { bare: 'ExtrinsicV5', // Not supported yet general: 'ExtrinsicV5', signed: 'ExtrinsicV5' }; +const PreambleMask = { + bare: BARE_EXTRINSIC, + general: GENERAL_EXTRINSIC, + signed: SIGNED_EXTRINSIC +} + export { LATEST_EXTRINSIC_VERSION }; /** @internal */ -function newFromValue (registry: Registry, value: any, version: number, subVersionV5: SubVersionV5): ExtrinsicVx | ExtrinsicUnknown { +function newFromValue (registry: Registry, value: any, version: number, preamble: Preamble): ExtrinsicVx | ExtrinsicUnknown { if (value instanceof GenericExtrinsic) { return value.unwrap(); } const isSigned = (version & BIT_SIGNED) === BIT_SIGNED; - const type = (version & UNMASK_VERSION) === 5 ? V5_VERSIONS[subVersionV5] : VERSIONS[version & UNMASK_VERSION] || VERSIONS[0]; + const type = (version & UNMASK_VERSION) === 5 ? PREAMBLE[preamble] : VERSIONS[version & UNMASK_VERSION] || VERSIONS[0]; // we cast here since the VERSION definition is incredibly broad - we don't have a // slice for "only add extrinsic types", and more string definitions become unwieldy @@ -61,20 +67,20 @@ function newFromValue (registry: Registry, value: any, version: number, subVersi } /** @internal */ -function decodeExtrinsic (registry: Registry, value?: GenericExtrinsic | ExtrinsicValue | AnyU8a | Call, version: number = DEFAULT_VERSION, subVersionV5: SubVersionV5 = DEFAULT_V5_VERSION): ExtrinsicVx | ExtrinsicUnknown { +function decodeExtrinsic (registry: Registry, value?: GenericExtrinsic | ExtrinsicValue | AnyU8a | Call, version: number = DEFAULT_VERSION, preamble: Preamble = DEFAULT_PREAMBLE): ExtrinsicVx | ExtrinsicUnknown { if (isU8a(value) || Array.isArray(value) || isHex(value)) { - return decodeU8a(registry, u8aToU8a(value), version, subVersionV5); + return decodeU8a(registry, u8aToU8a(value), version, preamble); } else if (value instanceof registry.createClassUnsafe('Call')) { - return newFromValue(registry, { method: value }, version, subVersionV5); + return newFromValue(registry, { method: value }, version, preamble); } - return newFromValue(registry, value, version, subVersionV5); + return newFromValue(registry, value, version, preamble); } /** @internal */ -function decodeU8a (registry: Registry, value: Uint8Array, version: number, subVersionV5: SubVersionV5): ExtrinsicVx | ExtrinsicUnknown { +function decodeU8a (registry: Registry, value: Uint8Array, version: number, preamble: Preamble): ExtrinsicVx | ExtrinsicUnknown { if (!value.length) { - return newFromValue(registry, new Uint8Array(), version, subVersionV5); + return newFromValue(registry, new Uint8Array(), version, preamble); } const [offset, length] = compactFromU8a(value); @@ -86,7 +92,7 @@ function decodeU8a (registry: Registry, value: Uint8Array, version: number, subV const data = value.subarray(offset, total); - return newFromValue(registry, data.subarray(1), data[0], subVersionV5); + return newFromValue(registry, data.subarray(1), data[0], preamble); } abstract class ExtrinsicBase extends AbstractBase { @@ -237,7 +243,13 @@ abstract class ExtrinsicBase extends AbstractBase extends ExtrinsicBa static LATEST_EXTRINSIC_VERSION = LATEST_EXTRINSIC_VERSION; - constructor (registry: Registry, value?: GenericExtrinsic | ExtrinsicValue | AnyU8a | Call, { subVersionV5, version }: CreateOptions = {}) { - super(registry, decodeExtrinsic(registry, value, registry.metadata.extrinsic.version?.toNumber() || version, subVersionV5)); + constructor (registry: Registry, value?: GenericExtrinsic | ExtrinsicValue | AnyU8a | Call, { preamble, version }: CreateOptions = {}) { + super(registry, decodeExtrinsic(registry, value, registry.metadata.extrinsic.version?.toNumber() || version, preamble)); } /** diff --git a/packages/types/src/extrinsic/ExtrinsicPayload.ts b/packages/types/src/extrinsic/ExtrinsicPayload.ts index 532c6823a7a0..d6acce7b7959 100644 --- a/packages/types/src/extrinsic/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/ExtrinsicPayload.ts @@ -9,16 +9,16 @@ import type { ExtrinsicPayloadV5 } from '../interfaces/extrinsics/index.js'; import type { Hash, MultiLocation } from '../interfaces/types.js'; import type { ExtrinsicPayloadValue, ICompact, IKeyringPair, INumber, IOption } from '../types/index.js'; import type { GenericExtrinsicEra } from './ExtrinsicEra.js'; -import type { SubVersionV5 } from './types.js'; +import type { Preamble } from './types.js'; import { AbstractBase } from '@polkadot/types-codec'; import { hexToU8a, isHex, u8aToHex } from '@polkadot/util'; -import { DEFAULT_V5_VERSION, DEFAULT_VERSION } from './constants.js'; +import { DEFAULT_PREAMBLE, DEFAULT_VERSION } from './constants.js'; interface ExtrinsicPayloadOptions { version?: number; - subVersionV5?: SubVersionV5; + pramble?: Preamble; } // all our known types that can be returned @@ -33,7 +33,7 @@ const VERSIONS = [ 'ExtrinsicPayloadV5' ]; -const V5_VERSIONS = { +const PREAMBLES = { bare: 'ExtrinsicPayloadV5', // Not supported yet general: 'ExtrinsicPayloadV5', @@ -41,12 +41,12 @@ const V5_VERSIONS = { }; /** @internal */ -function decodeExtrinsicPayload (registry: Registry, value?: GenericExtrinsicPayload | ExtrinsicPayloadValue | Uint8Array | string, version: number = DEFAULT_VERSION, subVersionV5: SubVersionV5 = DEFAULT_V5_VERSION): ExtrinsicPayloadVx { +function decodeExtrinsicPayload (registry: Registry, value?: GenericExtrinsicPayload | ExtrinsicPayloadValue | Uint8Array | string, version: number = DEFAULT_VERSION, preamble: Preamble = DEFAULT_PREAMBLE): ExtrinsicPayloadVx { if (value instanceof GenericExtrinsicPayload) { return value.unwrap(); } - const extVersion = version === 5 ? V5_VERSIONS[subVersionV5] : VERSIONS[version] || VERSIONS[0]; + const extVersion = version === 5 ? PREAMBLES[preamble] : VERSIONS[version] || VERSIONS[0]; /** * HACK: In order to change the assetId from `number | object` to HexString (While maintaining the true type ie Option), @@ -76,8 +76,8 @@ function decodeExtrinsicPayload (registry: Registry, value?: GenericExtrinsicPay * on the contents included */ export class GenericExtrinsicPayload extends AbstractBase { - constructor (registry: Registry, value?: Partial | Uint8Array | string, { subVersionV5, version }: ExtrinsicPayloadOptions = {}) { - super(registry, decodeExtrinsicPayload(registry, value as ExtrinsicPayloadValue, version, subVersionV5)); + constructor (registry: Registry, value?: Partial | Uint8Array | string, { pramble, version }: ExtrinsicPayloadOptions = {}) { + super(registry, decodeExtrinsicPayload(registry, value as ExtrinsicPayloadValue, version, pramble)); } /** diff --git a/packages/types/src/extrinsic/constants.ts b/packages/types/src/extrinsic/constants.ts index 0a70170852d4..b745718593ec 100644 --- a/packages/types/src/extrinsic/constants.ts +++ b/packages/types/src/extrinsic/constants.ts @@ -13,7 +13,19 @@ export const IMMORTAL_ERA = new Uint8Array([0]); export const UNMASK_VERSION = 0b01111111; -export const DEFAULT_V5_VERSION = 'signed'; +export const DEFAULT_PREAMBLE = 'signed'; // Latest extrinsic version is v5, which has backwards compatibility for v4 signed extrinsics export const LATEST_EXTRINSIC_VERSION = 5; + +export const VERSION_MASK = 0b00111111; + +export const TYPE_MASK = 0b11000000; + +export const BARE_EXTRINSIC = 0b00000000; + +export const SIGNED_EXTRINSIC = 0b10000000; + +export const GENERAL_EXTRINSIC = 0b01000000; + +export const LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION = 4; \ No newline at end of file diff --git a/packages/types/src/extrinsic/types.ts b/packages/types/src/extrinsic/types.ts index ebb1560e53e5..c468dc8582d2 100644 --- a/packages/types/src/extrinsic/types.ts +++ b/packages/types/src/extrinsic/types.ts @@ -22,4 +22,4 @@ export interface ExtrinsicExtraValue { tip?: AnyNumber; } -export type SubVersionV5 = 'signed' | 'bare' | 'general'; +export type Preamble = 'signed' | 'bare' | 'general'; diff --git a/packages/types/src/extrinsic/util.ts b/packages/types/src/extrinsic/util.ts index 84a5361b19be..044a94cade78 100644 --- a/packages/types/src/extrinsic/util.ts +++ b/packages/types/src/extrinsic/util.ts @@ -13,3 +13,10 @@ export function sign (registry: Registry, signerPair: IKeyringPair, u8a: Uint8Ar return signerPair.sign(encoded, options); } + +// a helper function for both types of payloads, Raw and metadata-known +export function signV5 (registry: Registry, signerPair: IKeyringPair, u8a: Uint8Array, options?: SignOptions): Uint8Array { + const encoded = registry.hash(u8a) + + return signerPair.sign(encoded, options); +} diff --git a/packages/types/src/extrinsic/v5/Extrinsic.ts b/packages/types/src/extrinsic/v5/Extrinsic.ts index 955403604a24..a1fd4f2ca9ce 100644 --- a/packages/types/src/extrinsic/v5/Extrinsic.ts +++ b/packages/types/src/extrinsic/v5/Extrinsic.ts @@ -5,7 +5,7 @@ import type { HexString } from '@polkadot/util/types'; import type { ExtrinsicSignatureV5 } from '../../interfaces/extrinsics/index.js'; import type { Address, Call } from '../../interfaces/runtime/index.js'; import type { ExtrinsicPayloadValue, IExtrinsicV5Impl, IKeyringPair, Registry, SignatureOptions } from '../../types/index.js'; -import type { ExtrinsicOptions } from '../types.js'; +import type { ExtrinsicOptions, Preamble } from '../types.js'; import { Struct } from '@polkadot/types-codec'; import { isU8a } from '@polkadot/util'; @@ -79,6 +79,10 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { return EXTRINSIC_VERSION; } + public get preamble (): Preamble { + return this.getT('preamble'); + } + /** * @description Add an [[ExtrinsicSignatureV5]] to the extrinsic (already generated) */ diff --git a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts index e01c13d94a0b..453421c88c13 100644 --- a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts @@ -9,6 +9,7 @@ import type { ExtrinsicPayloadValue, IKeyringPair } from '../../types/index.js'; import { Enum } from '@polkadot/types-codec'; import { GenericExtrinsicPayloadV4 } from '../v4/ExtrinsicPayload.js'; +import { signV5 } from '../util.js'; /** * @name GenericExtrinsicPayloadV5 @@ -39,6 +40,6 @@ export class GenericExtrinsicPayloadV5 extends GenericExtrinsicPayloadV4 { // 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 V6, carry this comment - return signerPair.sign(this.registry.hash(this.toU8a({ method: true })), this.#signOptions); + return signV5(this.registry, signerPair, this.toU8a({ method: true }), this.#signOptions); } } From 6a9a4324ee38d3ced37173a7b5104cdeba19a5ee Mon Sep 17 00:00:00 2001 From: tarikgul Date: Fri, 20 Sep 2024 17:34:21 -0400 Subject: [PATCH 19/71] cleanup --- packages/types/src/extrinsic/Extrinsic.ts | 22 ++++++++++--------- .../types/src/extrinsic/ExtrinsicPayload.ts | 4 ++-- packages/types/src/extrinsic/constants.ts | 2 +- packages/types/src/extrinsic/util.ts | 2 +- .../src/extrinsic/v5/ExtrinsicPayload.ts | 2 +- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 5d73c0417484..9e886694cff3 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -15,7 +15,7 @@ import type { ExtrinsicValueV5 } from './v5/Extrinsic.js'; import { AbstractBase } from '@polkadot/types-codec'; import { compactAddLength, compactFromU8a, compactToU8a, isHex, isU8a, objectProperty, objectSpread, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; -import { BARE_EXTRINSIC, BIT_SIGNED, BIT_UNSIGNED, DEFAULT_PREAMBLE, DEFAULT_VERSION, GENERAL_EXTRINSIC, LATEST_EXTRINSIC_VERSION, SIGNED_EXTRINSIC, UNMASK_VERSION } from './constants.js'; +import { BARE_EXTRINSIC, BIT_SIGNED, BIT_UNSIGNED, DEFAULT_PREAMBLE, GENERAL_EXTRINSIC, LATEST_EXTRINSIC_VERSION, LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION, SIGNED_EXTRINSIC, UNMASK_VERSION } from './constants.js'; interface CreateOptions { version?: number; @@ -48,7 +48,7 @@ const PreambleMask = { bare: BARE_EXTRINSIC, general: GENERAL_EXTRINSIC, signed: SIGNED_EXTRINSIC -} +}; export { LATEST_EXTRINSIC_VERSION }; @@ -67,7 +67,7 @@ function newFromValue (registry: Registry, value: any, version: number, preamble } /** @internal */ -function decodeExtrinsic (registry: Registry, value?: GenericExtrinsic | ExtrinsicValue | AnyU8a | Call, version: number = DEFAULT_VERSION, preamble: Preamble = DEFAULT_PREAMBLE): ExtrinsicVx | ExtrinsicUnknown { +function decodeExtrinsic (registry: Registry, value?: GenericExtrinsic | ExtrinsicValue | AnyU8a | Call, version: number = LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION, preamble: Preamble = DEFAULT_PREAMBLE): ExtrinsicVx | ExtrinsicUnknown { if (isU8a(value) || Array.isArray(value) || isHex(value)) { return decodeU8a(registry, u8aToU8a(value), version, preamble); } else if (value instanceof registry.createClassUnsafe('Call')) { @@ -96,7 +96,9 @@ function decodeU8a (registry: Registry, value: Uint8Array, version: number, prea } abstract class ExtrinsicBase extends AbstractBase { - constructor (registry: Registry, value: ExtrinsicV5 | ExtrinsicUnknown, initialU8aLength?: number) { + readonly #preamble: Preamble; + + constructor (registry: Registry, value: ExtrinsicV5 | ExtrinsicUnknown, initialU8aLength?: number, preamble?: Preamble) { super(registry, value, initialU8aLength); const signKeys = Object.keys(registry.getSignedExtensionTypes()); @@ -107,6 +109,8 @@ abstract class ExtrinsicBase extends AbstractBase extends AbstractBase extends ExtrinsicBa static LATEST_EXTRINSIC_VERSION = LATEST_EXTRINSIC_VERSION; constructor (registry: Registry, value?: GenericExtrinsic | ExtrinsicValue | AnyU8a | Call, { preamble, version }: CreateOptions = {}) { - super(registry, decodeExtrinsic(registry, value, registry.metadata.extrinsic.version?.toNumber() || version, preamble)); + super(registry, decodeExtrinsic(registry, value, version || registry.metadata.extrinsic.version?.toNumber(), preamble), undefined, preamble); } /** diff --git a/packages/types/src/extrinsic/ExtrinsicPayload.ts b/packages/types/src/extrinsic/ExtrinsicPayload.ts index d6acce7b7959..50539c97734b 100644 --- a/packages/types/src/extrinsic/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/ExtrinsicPayload.ts @@ -14,7 +14,7 @@ import type { Preamble } from './types.js'; import { AbstractBase } from '@polkadot/types-codec'; import { hexToU8a, isHex, u8aToHex } from '@polkadot/util'; -import { DEFAULT_PREAMBLE, DEFAULT_VERSION } from './constants.js'; +import { DEFAULT_PREAMBLE } from './constants.js'; interface ExtrinsicPayloadOptions { version?: number; @@ -41,7 +41,7 @@ const PREAMBLES = { }; /** @internal */ -function decodeExtrinsicPayload (registry: Registry, value?: GenericExtrinsicPayload | ExtrinsicPayloadValue | Uint8Array | string, version: number = DEFAULT_VERSION, preamble: Preamble = DEFAULT_PREAMBLE): ExtrinsicPayloadVx { +function decodeExtrinsicPayload (registry: Registry, value?: GenericExtrinsicPayload | ExtrinsicPayloadValue | Uint8Array | string, version = 5, preamble: Preamble = DEFAULT_PREAMBLE): ExtrinsicPayloadVx { if (value instanceof GenericExtrinsicPayload) { return value.unwrap(); } diff --git a/packages/types/src/extrinsic/constants.ts b/packages/types/src/extrinsic/constants.ts index b745718593ec..aff05aef2d3d 100644 --- a/packages/types/src/extrinsic/constants.ts +++ b/packages/types/src/extrinsic/constants.ts @@ -28,4 +28,4 @@ export const SIGNED_EXTRINSIC = 0b10000000; export const GENERAL_EXTRINSIC = 0b01000000; -export const LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION = 4; \ No newline at end of file +export const LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION = 4; diff --git a/packages/types/src/extrinsic/util.ts b/packages/types/src/extrinsic/util.ts index 044a94cade78..3a1fbd323f18 100644 --- a/packages/types/src/extrinsic/util.ts +++ b/packages/types/src/extrinsic/util.ts @@ -16,7 +16,7 @@ export function sign (registry: Registry, signerPair: IKeyringPair, u8a: Uint8Ar // a helper function for both types of payloads, Raw and metadata-known export function signV5 (registry: Registry, signerPair: IKeyringPair, u8a: Uint8Array, options?: SignOptions): Uint8Array { - const encoded = registry.hash(u8a) + const encoded = registry.hash(u8a); return signerPair.sign(encoded, options); } diff --git a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts index 453421c88c13..cf3781cf8a04 100644 --- a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts @@ -8,8 +8,8 @@ import type { ExtrinsicPayloadValue, IKeyringPair } from '../../types/index.js'; import { Enum } from '@polkadot/types-codec'; -import { GenericExtrinsicPayloadV4 } from '../v4/ExtrinsicPayload.js'; import { signV5 } from '../util.js'; +import { GenericExtrinsicPayloadV4 } from '../v4/ExtrinsicPayload.js'; /** * @name GenericExtrinsicPayloadV5 From e1baccf1aad9e55474423b98319d1e076c4bd108 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Fri, 20 Sep 2024 18:53:17 -0400 Subject: [PATCH 20/71] change version to constant --- packages/types/src/extrinsic/ExtrinsicPayload.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/types/src/extrinsic/ExtrinsicPayload.ts b/packages/types/src/extrinsic/ExtrinsicPayload.ts index 50539c97734b..4b90787f0e5f 100644 --- a/packages/types/src/extrinsic/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/ExtrinsicPayload.ts @@ -14,7 +14,7 @@ import type { Preamble } from './types.js'; import { AbstractBase } from '@polkadot/types-codec'; import { hexToU8a, isHex, u8aToHex } from '@polkadot/util'; -import { DEFAULT_PREAMBLE } from './constants.js'; +import { DEFAULT_PREAMBLE, LATEST_EXTRINSIC_VERSION } from './constants.js'; interface ExtrinsicPayloadOptions { version?: number; @@ -41,7 +41,7 @@ const PREAMBLES = { }; /** @internal */ -function decodeExtrinsicPayload (registry: Registry, value?: GenericExtrinsicPayload | ExtrinsicPayloadValue | Uint8Array | string, version = 5, preamble: Preamble = DEFAULT_PREAMBLE): ExtrinsicPayloadVx { +function decodeExtrinsicPayload (registry: Registry, value?: GenericExtrinsicPayload | ExtrinsicPayloadValue | Uint8Array | string, version = LATEST_EXTRINSIC_VERSION, preamble: Preamble = DEFAULT_PREAMBLE): ExtrinsicPayloadVx { if (value instanceof GenericExtrinsicPayload) { return value.unwrap(); } From fd2a35c339481319f633c07cba222a547eddb3ee Mon Sep 17 00:00:00 2001 From: tarikgul Date: Fri, 20 Sep 2024 18:53:27 -0400 Subject: [PATCH 21/71] Add tests --- .../types/src/extrinsic/v5/Extrinsic.spec.ts | 86 ++++++++++ .../src/extrinsic/v5/ExtrinsicPayload.spec.ts | 39 +++++ .../extrinsic/v5/ExtrinsicSignature.spec.ts | 160 ++++++++++++++++++ 3 files changed, 285 insertions(+) create mode 100644 packages/types/src/extrinsic/v5/Extrinsic.spec.ts create mode 100644 packages/types/src/extrinsic/v5/ExtrinsicPayload.spec.ts create mode 100644 packages/types/src/extrinsic/v5/ExtrinsicSignature.spec.ts diff --git a/packages/types/src/extrinsic/v5/Extrinsic.spec.ts b/packages/types/src/extrinsic/v5/Extrinsic.spec.ts new file mode 100644 index 000000000000..d27e91cf0f70 --- /dev/null +++ b/packages/types/src/extrinsic/v5/Extrinsic.spec.ts @@ -0,0 +1,86 @@ +// Copyright 2017-2024 @polkadot/types authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +/// + +import { createTestPairs } from '@polkadot/keyring/testingPairs'; +import rpcMetadata from '@polkadot/types-support/metadata/static-substrate'; +import { BN } from '@polkadot/util'; + +import { TypeRegistry } from '../../create/index.js'; +import { decorateExtrinsics, Metadata } from '../../metadata/index.js'; +import { GenericExtrinsicV5 as Extrinsic } from './index.js'; + +const registry = new TypeRegistry(); +const metadata = new Metadata(registry, rpcMetadata); +const keyring = createTestPairs({ type: 'ed25519' }, false); + +registry.setMetadata(metadata); + +const tx = decorateExtrinsics(registry, metadata.asLatest, metadata.version); + +describe('ExtrinsicV4', (): void => { + it('constructs a sane Uint8Array (default)', (): void => { + const xt = new Extrinsic(registry); + + // expect(`${xt.method.section}${xt.method.method}`).toEqual('system.fillBlock'); + expect(`${xt.method.section}.${xt.method.method}`).toEqual('system.remark'); + + expect(xt.toU8a()).toEqual(new Uint8Array([ + 0, 0, // index + // 0, 0, 0, 0 // fillBlock, Perbill + 0 // remark, Vec + ])); + }); + + it('creates a unsigned extrinsic', (): void => { + expect( + new Extrinsic( + registry, + tx['balances']['transferAllowDeath'](keyring.bob.publicKey, 6969n) + ).toHex() + ).toEqual( + '0x' + + '0600' + // balance.transferAllowDeath + '00' + + 'd7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9' + + 'e56c' + ); + }); + + it('creates a signed extrinsic', (): void => { + expect( + new Extrinsic( + registry, + tx['balances']['transferAllowDeath'](keyring.bob.publicKey, 6969n) + ).sign(keyring.alice, { + blockHash: '0xec7afaf1cca720ce88c1d1b689d81f0583cc15a97d621cf046dd9abf605ef22f', + genesisHash: '0xdcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025b', + mode: 0, + nonce: 1, + runtimeVersion: { + apis: [], + authoringVersion: new BN(123), + implName: 'test', + implVersion: new BN(123), + specName: 'test', + specVersion: new BN(123), + transactionVersion: new BN(123) + }, + tip: 2 + }).toHex() + ).toEqual( + '0x' + + '00' + + 'd172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f' + + '00' + // ed25519 + '84181ebef350cc212e70e042b6ebcd33ca955bf9497711a64aa7c64e2b8c2732' + + 'ab837715364eab7be5cc76f74eefa36d3ba9ee698264ed28a286c8360fc9aa0c' + + '0004080000' + // era. nonce, tip, mode + '0600' + + '00' + + 'd7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9' + + 'e56c' + ); + }); +}); diff --git a/packages/types/src/extrinsic/v5/ExtrinsicPayload.spec.ts b/packages/types/src/extrinsic/v5/ExtrinsicPayload.spec.ts new file mode 100644 index 000000000000..e2854568f244 --- /dev/null +++ b/packages/types/src/extrinsic/v5/ExtrinsicPayload.spec.ts @@ -0,0 +1,39 @@ +// Copyright 2017-2024 @polkadot/types authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +/// + +import rpcMetadata from '@polkadot/types-support/metadata/static-substrate'; + +import { TypeRegistry } from '../../create/index.js'; +import { decorateExtrinsics, Metadata } from '../../metadata/index.js'; +import { GenericExtrinsicPayloadV5 as ExtrinsicPayload } from './index.js'; + +const registry = new TypeRegistry(); +const metadata = new Metadata(registry, rpcMetadata); + +registry.setMetadata(metadata); + +const tx = decorateExtrinsics(registry, metadata.asLatest, metadata.version); + +describe('ExtrinsicPayload', (): void => { + it('has a sane inspect', (): void => { + // we don't expect this to fail, however it is actually a good + // reference for the ordering in base Substrate + expect(new ExtrinsicPayload(registry, { method: tx['timestamp']['set'](0).toHex() } as never).inspect()).toEqual({ + inner: [ + { name: 'method', outer: [new Uint8Array([3, 0, 0])] }, + { inner: undefined, name: 'era', outer: [new Uint8Array([0]), new Uint8Array([0])] }, + { name: 'nonce', outer: [new Uint8Array([0])] }, + { name: 'tip', outer: [new Uint8Array([0])] }, + { name: 'assetId', outer: [new Uint8Array([0])] }, + { name: 'mode', outer: [new Uint8Array([0])] }, + { name: 'specVersion', outer: [new Uint8Array([0, 0, 0, 0])] }, + { name: 'transactionVersion', outer: [new Uint8Array([0, 0, 0, 0])] }, + { name: 'genesisHash', outer: [new Uint8Array(32)] }, + { name: 'blockHash', outer: [new Uint8Array(32)] }, + { name: 'metadataHash', outer: [new Uint8Array([0])] } + ] + }); + }); +}); diff --git a/packages/types/src/extrinsic/v5/ExtrinsicSignature.spec.ts b/packages/types/src/extrinsic/v5/ExtrinsicSignature.spec.ts new file mode 100644 index 000000000000..71c96c4dd544 --- /dev/null +++ b/packages/types/src/extrinsic/v5/ExtrinsicSignature.spec.ts @@ -0,0 +1,160 @@ +// Copyright 2017-2024 @polkadot/types authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +/// + +import { createTestPairs } from '@polkadot/keyring/testingPairs'; +import metadataStatic from '@polkadot/types-support/metadata/static-substrate'; +import { BN_ZERO } from '@polkadot/util'; + +import { TypeRegistry } from '../../create/index.js'; +import { Metadata } from '../../metadata/index.js'; +import { GenericExtrinsicSignatureV5 as ExtrinsicSignature } from './index.js'; + +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(); + + const u8a = new Uint8Array([ + // signer as an AccountIndex + 0x01, 0x08, // 4 << 2 + // 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(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).signFake( + registry.createType('Call'), + pairs.alice.publicKey, + signOptions + ).toHex() + ).toEqual( + '0x' + + '00' + // MultiAddress + 'd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d' + + '01' + + '0101010101010101010101010101010101010101010101010101010101010101' + + '0101010101010101010101010101010101010101010101010101010101010101' + + '00a5010000' + + '00' // Mode + ); + }); + + it('fake signs default (AccountId address)', (): void => { + const registry = new TypeRegistry(); + const metadata = new Metadata(registry, metadataStatic); + + registry.setMetadata(metadata); + registry.register({ + Address: 'AccountId', + ExtrinsicSignature: 'AnySignature' + }); + + expect( + new ExtrinsicSignature(registry).signFake( + registry.createType('Call'), + pairs.alice.address, + signOptions + ).toHex() + ).toEqual( + '0x' + + // Address = AccountId, no prefix + 'd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d' + + // This is a prefix-less signature, anySignture as opposed to Multi above + '0101010101010101010101010101010101010101010101010101010101010101' + + '0101010101010101010101010101010101010101010101010101010101010101' + + '00a5010000' + + '00' // mode + ); + }); + + it('fake signs with non-enum signature', (): void => { + const registry = new TypeRegistry(); + const metadata = new Metadata(registry, metadataStatic); + + registry.setMetadata(metadata); + registry.register({ + Address: 'AccountId', + ExtrinsicSignature: '[u8;65]' + }); + + expect( + new ExtrinsicSignature(registry).signFake( + registry.createType('Call'), + pairs.alice.address, + signOptions + ).toHex() + ).toEqual( + '0x' + + // Address = AccountId, no prefix + 'd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d' + + // 65 bytes here + '01' + + '0101010101010101010101010101010101010101010101010101010101010101' + + '0101010101010101010101010101010101010101010101010101010101010101' + + '00a5010000' + + '00' // mode + ); + }); + + it('injects a signature', (): void => { + const registry = new TypeRegistry(); + const metadata = new Metadata(registry, metadataStatic); + + registry.setMetadata(metadata); + + expect( + new ExtrinsicSignature(registry).addSignature( + pairs.alice.publicKey, + new Uint8Array(65).fill(1), + new Uint8Array(0) + ).toHex() + ).toEqual( + '0x' + + '00' + // MultiAddress + 'd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d' + + '01' + + '0101010101010101010101010101010101010101010101010101010101010101' + + '0101010101010101010101010101010101010101010101010101010101010101' + + '00000000' + + '00' // mode + ); + }); +}); From 9a371948ef1bfb0d70dd8321b7d2ce983ab46237 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Fri, 20 Sep 2024 23:48:40 -0400 Subject: [PATCH 22/71] Decouple payload v5 from v4 --- .../src/extrinsic/v5/ExtrinsicPayload.ts | 99 +++++++++++++++++-- 1 file changed, 92 insertions(+), 7 deletions(-) diff --git a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts index cf3781cf8a04..3cc32fc50b5a 100644 --- a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts @@ -2,14 +2,18 @@ // SPDX-License-Identifier: Apache-2.0 import type { SignOptions } from '@polkadot/keyring/types'; -import type { Registry } from '@polkadot/types-codec/types'; +import type { Hash, MultiLocation } from '@polkadot/types/interfaces'; +import type { Bytes } from '@polkadot/types-codec'; +import type { Inspect, Registry } from '@polkadot/types-codec/types'; import type { HexString } from '@polkadot/util/types'; -import type { ExtrinsicPayloadValue, IKeyringPair } from '../../types/index.js'; +import type { BlockHash } from '../../interfaces/chain/index.js'; +import type { ExtrinsicEra } from '../../interfaces/extrinsics/index.js'; +import type { ExtrinsicPayloadValue, ICompact, IKeyringPair, INumber, IOption } from '../../types/index.js'; -import { Enum } from '@polkadot/types-codec'; +import { Enum, Struct } from '@polkadot/types-codec'; +import { objectSpread } from '@polkadot/util'; import { signV5 } from '../util.js'; -import { GenericExtrinsicPayloadV4 } from '../v4/ExtrinsicPayload.js'; /** * @name GenericExtrinsicPayloadV5 @@ -17,11 +21,15 @@ import { GenericExtrinsicPayloadV4 } from '../v4/ExtrinsicPayload.js'; * A signing payload for an [[Extrinsic]]. For the final encoding, it is * variable length based on the contents included */ -export class GenericExtrinsicPayloadV5 extends GenericExtrinsicPayloadV4 { +export class GenericExtrinsicPayloadV5 extends Struct { #signOptions: SignOptions; constructor (registry: Registry, value?: ExtrinsicPayloadValue | Uint8Array | HexString) { - super(registry, value); + super(registry, objectSpread( + { 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 @@ -31,10 +39,87 @@ export class GenericExtrinsicPayloadV5 extends GenericExtrinsicPayloadV4 { }; } + /** + * @description Returns a breakdown of the hex encoding for this Codec + */ + public override inspect (): Inspect { + return super.inspect({ method: true }); + } + + /** + * @description The block [[BlockHash]] the signature applies to (mortal/immortal) + */ + public get blockHash (): BlockHash { + return this.getT('blockHash'); + } + + /** + * @description The [[ExtrinsicEra]] + */ + public get era (): ExtrinsicEra { + return this.getT('era'); + } + + /** + * @description The genesis [[BlockHash]] the signature applies to (mortal/immortal) + */ + public get genesisHash (): BlockHash { + return this.getT('genesisHash'); + } + + /** + * @description The [[Bytes]] contained in the payload + */ + public get method (): Bytes { + return this.getT('method'); + } + + /** + * @description The [[Index]] + */ + public get nonce (): ICompact { + return this.getT('nonce'); + } + + /** + * @description The specVersion for this signature + */ + public get specVersion (): INumber { + return this.getT('specVersion'); + } + + /** + * @description The tip [[Balance]] + */ + public get tip (): ICompact { + return this.getT('tip'); + } + + /** + * @description The transactionVersion for this signature + */ + public get transactionVersion (): INumber { + return this.getT('transactionVersion'); + } + + /** + * @description The (optional) asset id for this signature for chains that support transaction fees in assets + */ + public get assetId (): IOption { + return this.getT('assetId'); + } + + /** + * @description The (optional) asset id for this signature for chains that support transaction fees in assets + */ + public get metadataHash (): IOption { + return this.getT('metadataHash'); + } + /** * @description Sign the payload with the keypair */ - public override sign (signerPair: IKeyringPair): Uint8Array { + public sign (signerPair: IKeyringPair): Uint8Array { // NOTE The `toU8a({ method: 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 From d77053bb186bb7c5319a1c3fc8d98c53403b0d6d Mon Sep 17 00:00:00 2001 From: tarikgul Date: Fri, 20 Sep 2024 23:49:25 -0400 Subject: [PATCH 23/71] Add helper fn to get signed extension version from the registry --- packages/types-codec/src/types/registry.ts | 1 + packages/types/src/create/registry.ts | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/packages/types-codec/src/types/registry.ts b/packages/types-codec/src/types/registry.ts index 5f7aecbe698c..43e27731b59f 100644 --- a/packages/types-codec/src/types/registry.ts +++ b/packages/types-codec/src/types/registry.ts @@ -71,6 +71,7 @@ export interface Registry { getClassName (clazz: CodecClass): string | undefined; getOrThrow (name: K, msg?: string): CodecClass; getOrUnknown (name: K): CodecClass; + getSignedExtensionVersion (): Record; getSignedExtensionExtra (): Record; getSignedExtensionTypes (): Record; diff --git a/packages/types/src/create/registry.ts b/packages/types/src/create/registry.ts index 2ae82f0f9abd..f6faac890733 100644 --- a/packages/types/src/create/registry.ts +++ b/packages/types/src/create/registry.ts @@ -441,6 +441,11 @@ export class TypeRegistry implements Registry { return this.get(name, true) as unknown as CodecClass; } + public getSignedExtensionVersion (): Record { + // Default for now + return { version: 'u8' }; + } + public getSignedExtensionExtra (): Record { return expandExtensionTypes(this.#signedExtensions, 'payload', this.#userExtensions); } From f277d825ba918b877b6b785302e33cf30819cb45 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Sun, 22 Sep 2024 20:39:11 -0400 Subject: [PATCH 24/71] Add version to encoding --- packages/types-codec/src/types/registry.ts | 2 +- packages/types/src/create/registry.ts | 6 +++--- .../types/src/extrinsic/v5/ExtrinsicSignature.ts | 16 ++++++++++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/types-codec/src/types/registry.ts b/packages/types-codec/src/types/registry.ts index 43e27731b59f..21478bb009d6 100644 --- a/packages/types-codec/src/types/registry.ts +++ b/packages/types-codec/src/types/registry.ts @@ -71,7 +71,7 @@ export interface Registry { getClassName (clazz: CodecClass): string | undefined; getOrThrow (name: K, msg?: string): CodecClass; getOrUnknown (name: K): CodecClass; - getSignedExtensionVersion (): Record; + getSignedExtensionVersion (): number; getSignedExtensionExtra (): Record; getSignedExtensionTypes (): Record; diff --git a/packages/types/src/create/registry.ts b/packages/types/src/create/registry.ts index f6faac890733..1b0106d7a9af 100644 --- a/packages/types/src/create/registry.ts +++ b/packages/types/src/create/registry.ts @@ -441,9 +441,9 @@ export class TypeRegistry implements Registry { return this.get(name, true) as unknown as CodecClass; } - public getSignedExtensionVersion (): Record { - // Default for now - return { version: 'u8' }; + // Only used in extrinsic version 5 + public getSignedExtensionVersion (): number { + return 0; } public getSignedExtensionExtra (): Record { diff --git a/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts b/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts index 55cdef901891..1cef1bb1e437 100644 --- a/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts +++ b/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts @@ -28,20 +28,23 @@ function toAddress (registry: Registry, address: Address | Uint8Array | string): */ export class GenericExtrinsicSignatureV5 extends Struct implements IExtrinsicSignature { #signKeys: string[]; + #signedExtensionVersion: number; constructor (registry: Registry, value?: GenericExtrinsicSignatureV5 | Uint8Array, { isSigned }: ExtrinsicSignatureOptions = {}) { const signTypes = registry.getSignedExtensionTypes(); + const signedVersion = registry.getSignedExtensionVersion(); super( registry, objectSpread( // eslint-disable-next-line sort-keys - { signer: 'Address', signature: 'ExtrinsicSignature' }, + { signer: 'Address', signature: 'ExtrinsicSignature', signedExtensionVersion: 'u8' }, signTypes ), GenericExtrinsicSignatureV5.decodeExtrinsicSignature(value, isSigned) ); + this.#signedExtensionVersion = signedVersion; this.#signKeys = Object.keys(signTypes); objectProperties(this, this.#signKeys, (k) => this.get(k)); @@ -140,13 +143,22 @@ export class GenericExtrinsicSignatureV5 extends Struct implements IExtrinsicSig return this.getT('metadataHash'); } + /** + * @description The [[u8]] for the TransactionExtension version + */ + public get signedExtensionVersion (): INumber { + return this.getT('signedExtensionVersion'); + } + protected _injectSignature (signer: Address, signature: ExtrinsicSignature, payload: GenericExtrinsicPayloadV5): IExtrinsicSignature { // use the fields exposed to guide the getters for (let i = 0, count = this.#signKeys.length; i < count; i++) { const k = this.#signKeys[i]; const v = payload.get(k); - if (!isUndefined(v)) { + if (k === 'signedExtensionVersion') { + this.set(k, this.registry.createType('u8', this.#signedExtensionVersion)); + } else if (!isUndefined(v)) { this.set(k, v); } } From d7fb11ce889a944669261fd0b76b329a0992f441 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Sun, 22 Sep 2024 21:05:51 -0400 Subject: [PATCH 25/71] Cleanup tests --- packages/types/src/extrinsic/v5/Extrinsic.spec.ts | 1 + packages/types/src/extrinsic/v5/ExtrinsicSignature.spec.ts | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/packages/types/src/extrinsic/v5/Extrinsic.spec.ts b/packages/types/src/extrinsic/v5/Extrinsic.spec.ts index d27e91cf0f70..fe65d33aced1 100644 --- a/packages/types/src/extrinsic/v5/Extrinsic.spec.ts +++ b/packages/types/src/extrinsic/v5/Extrinsic.spec.ts @@ -76,6 +76,7 @@ describe('ExtrinsicV4', (): void => { '00' + // ed25519 '84181ebef350cc212e70e042b6ebcd33ca955bf9497711a64aa7c64e2b8c2732' + 'ab837715364eab7be5cc76f74eefa36d3ba9ee698264ed28a286c8360fc9aa0c' + + '00' + // TransactionExtension version '0004080000' + // era. nonce, tip, mode '0600' + '00' + diff --git a/packages/types/src/extrinsic/v5/ExtrinsicSignature.spec.ts b/packages/types/src/extrinsic/v5/ExtrinsicSignature.spec.ts index 71c96c4dd544..7fd7d58f9d70 100644 --- a/packages/types/src/extrinsic/v5/ExtrinsicSignature.spec.ts +++ b/packages/types/src/extrinsic/v5/ExtrinsicSignature.spec.ts @@ -42,6 +42,7 @@ describe('ExtrinsicSignatureV4', (): void => { 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, // TransactionExtension version // extra stuff 0x00, // immortal, 0x04, // nonce, compact @@ -72,6 +73,7 @@ describe('ExtrinsicSignatureV4', (): void => { '01' + '0101010101010101010101010101010101010101010101010101010101010101' + '0101010101010101010101010101010101010101010101010101010101010101' + + '00' + // TransactionExtension version '00a5010000' + '00' // Mode ); @@ -100,6 +102,7 @@ describe('ExtrinsicSignatureV4', (): void => { // This is a prefix-less signature, anySignture as opposed to Multi above '0101010101010101010101010101010101010101010101010101010101010101' + '0101010101010101010101010101010101010101010101010101010101010101' + + '00' + // TransactionExtension version '00a5010000' + '00' // mode ); @@ -129,6 +132,7 @@ describe('ExtrinsicSignatureV4', (): void => { '01' + '0101010101010101010101010101010101010101010101010101010101010101' + '0101010101010101010101010101010101010101010101010101010101010101' + + '00' + // TransactionExtension version '00a5010000' + '00' // mode ); @@ -153,6 +157,7 @@ describe('ExtrinsicSignatureV4', (): void => { '01' + '0101010101010101010101010101010101010101010101010101010101010101' + '0101010101010101010101010101010101010101010101010101010101010101' + + '00' + // TransactionExtension version '00000000' + '00' // mode ); From ae43b6eec193f324efd985b4a6d2f64869da1c2e Mon Sep 17 00:00:00 2001 From: bee344 Date: Mon, 23 Sep 2024 09:23:53 -0300 Subject: [PATCH 26/71] renamed getSignedExtensionVersion to getTransactionExtensionVersion --- packages/types-codec/src/types/registry.ts | 2 +- packages/types/src/create/registry.ts | 2 +- packages/types/src/extrinsic/v5/ExtrinsicSignature.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/types-codec/src/types/registry.ts b/packages/types-codec/src/types/registry.ts index 21478bb009d6..373fa84e5d19 100644 --- a/packages/types-codec/src/types/registry.ts +++ b/packages/types-codec/src/types/registry.ts @@ -71,7 +71,7 @@ export interface Registry { getClassName (clazz: CodecClass): string | undefined; getOrThrow (name: K, msg?: string): CodecClass; getOrUnknown (name: K): CodecClass; - getSignedExtensionVersion (): number; + getTransactionExtensionVersion (): number; getSignedExtensionExtra (): Record; getSignedExtensionTypes (): Record; diff --git a/packages/types/src/create/registry.ts b/packages/types/src/create/registry.ts index 1b0106d7a9af..7059025faf4d 100644 --- a/packages/types/src/create/registry.ts +++ b/packages/types/src/create/registry.ts @@ -442,7 +442,7 @@ export class TypeRegistry implements Registry { } // Only used in extrinsic version 5 - public getSignedExtensionVersion (): number { + public getTransactionExtensionVersion (): number { return 0; } diff --git a/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts b/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts index 1cef1bb1e437..e97702fa3856 100644 --- a/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts +++ b/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts @@ -32,7 +32,7 @@ export class GenericExtrinsicSignatureV5 extends Struct implements IExtrinsicSig constructor (registry: Registry, value?: GenericExtrinsicSignatureV5 | Uint8Array, { isSigned }: ExtrinsicSignatureOptions = {}) { const signTypes = registry.getSignedExtensionTypes(); - const signedVersion = registry.getSignedExtensionVersion(); + const signedVersion = registry.getTransactionExtensionVersion(); super( registry, From 721054dd2f9cf0edb78676f70206858052e7242f Mon Sep 17 00:00:00 2001 From: bee344 Date: Mon, 23 Sep 2024 09:32:41 -0300 Subject: [PATCH 27/71] removed this.type for v5 --- packages/types/src/extrinsic/Extrinsic.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 9e886694cff3..06c05cbe8609 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -250,7 +250,7 @@ abstract class ExtrinsicBase extends AbstractBase Date: Mon, 23 Sep 2024 09:37:03 -0300 Subject: [PATCH 28/71] undo this.type removal --- packages/types/src/extrinsic/Extrinsic.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 06c05cbe8609..9e886694cff3 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -250,7 +250,7 @@ abstract class ExtrinsicBase extends AbstractBase Date: Mon, 23 Sep 2024 09:44:46 -0300 Subject: [PATCH 29/71] replaced signedExtensionVersion by transactionExtensionVersion for v5 in ExtrinsicSignature --- .../types/src/extrinsic/v5/ExtrinsicSignature.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts b/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts index e97702fa3856..7ab070ff1876 100644 --- a/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts +++ b/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts @@ -28,7 +28,7 @@ function toAddress (registry: Registry, address: Address | Uint8Array | string): */ export class GenericExtrinsicSignatureV5 extends Struct implements IExtrinsicSignature { #signKeys: string[]; - #signedExtensionVersion: number; + #transactionExtensionVersion: number; constructor (registry: Registry, value?: GenericExtrinsicSignatureV5 | Uint8Array, { isSigned }: ExtrinsicSignatureOptions = {}) { const signTypes = registry.getSignedExtensionTypes(); @@ -38,13 +38,13 @@ export class GenericExtrinsicSignatureV5 extends Struct implements IExtrinsicSig registry, objectSpread( // eslint-disable-next-line sort-keys - { signer: 'Address', signature: 'ExtrinsicSignature', signedExtensionVersion: 'u8' }, + { signer: 'Address', signature: 'ExtrinsicSignature', transactionExtensionVersion: 'u8' }, signTypes ), GenericExtrinsicSignatureV5.decodeExtrinsicSignature(value, isSigned) ); - this.#signedExtensionVersion = signedVersion; + this.#transactionExtensionVersion = signedVersion; this.#signKeys = Object.keys(signTypes); objectProperties(this, this.#signKeys, (k) => this.get(k)); @@ -146,8 +146,8 @@ export class GenericExtrinsicSignatureV5 extends Struct implements IExtrinsicSig /** * @description The [[u8]] for the TransactionExtension version */ - public get signedExtensionVersion (): INumber { - return this.getT('signedExtensionVersion'); + public get transactionExtensionVersion (): INumber { + return this.getT('transactionExtensionVersion'); } protected _injectSignature (signer: Address, signature: ExtrinsicSignature, payload: GenericExtrinsicPayloadV5): IExtrinsicSignature { @@ -156,8 +156,8 @@ export class GenericExtrinsicSignatureV5 extends Struct implements IExtrinsicSig const k = this.#signKeys[i]; const v = payload.get(k); - if (k === 'signedExtensionVersion') { - this.set(k, this.registry.createType('u8', this.#signedExtensionVersion)); + if (k === 'transactionExtensionVersion') { + this.set(k, this.registry.createType('u8', this.#transactionExtensionVersion)); } else if (!isUndefined(v)) { this.set(k, v); } From 0b1c350fcc97e6f724ed84f66742be472c5daa71 Mon Sep 17 00:00:00 2001 From: bee344 Date: Tue, 24 Sep 2024 11:09:56 -0300 Subject: [PATCH 30/71] unfinished Preamble Class --- .../types-augment/src/registry/interfaces.ts | 7 ++- packages/types/src/extrinsic/Extrinsic.ts | 35 ++++++----- .../types/src/extrinsic/ExtrinsicPayload.ts | 10 +-- packages/types/src/extrinsic/constants.ts | 2 +- packages/types/src/extrinsic/types.ts | 4 +- packages/types/src/extrinsic/v5/Extrinsic.ts | 54 ++++++++++------ .../src/extrinsic/v5/ExtrinsicSignature.ts | 2 +- packages/types/src/extrinsic/v5/Preamble.ts | 63 +++++++++++++++++++ .../src/interfaces/extrinsics/definitions.ts | 17 ++++- .../types/src/interfaces/extrinsics/types.ts | 33 +++++++++- packages/types/src/types/extrinsic.ts | 4 +- 11 files changed, 182 insertions(+), 49 deletions(-) create mode 100644 packages/types/src/extrinsic/v5/Preamble.ts diff --git a/packages/types-augment/src/registry/interfaces.ts b/packages/types-augment/src/registry/interfaces.ts index 5b77684993c2..91f4c086d5d0 100644 --- a/packages/types-augment/src/registry/interfaces.ts +++ b/packages/types-augment/src/registry/interfaces.ts @@ -35,7 +35,7 @@ import type { ApprovalFlag, DefunctVoter, Renouncing, SetIndex, Vote, VoteIndex, import type { CreatedBlock, ImportedAux } from '@polkadot/types/interfaces/engine'; import type { BlockV0, BlockV1, BlockV2, EIP1559Transaction, EIP2930Transaction, EthAccessList, EthAccessListItem, EthAccount, EthAddress, EthBlock, EthBloom, EthCallRequest, EthFeeHistory, EthFilter, EthFilterAddress, EthFilterChanges, EthFilterTopic, EthFilterTopicEntry, EthFilterTopicInner, EthHeader, EthLog, EthReceipt, EthReceiptV0, EthReceiptV3, EthRichBlock, EthRichHeader, EthStorageProof, EthSubKind, EthSubParams, EthSubResult, EthSyncInfo, EthSyncStatus, EthTransaction, EthTransactionAction, EthTransactionCondition, EthTransactionRequest, EthTransactionSignature, EthTransactionStatus, EthWork, EthereumAccountId, EthereumAddress, EthereumLookupSource, EthereumSignature, LegacyTransaction, TransactionV0, TransactionV1, TransactionV2 } from '@polkadot/types/interfaces/eth'; import type { EvmAccount, EvmCallInfo, EvmCallInfoV2, EvmCreateInfo, EvmCreateInfoV2, EvmLog, EvmVicinity, EvmWeightInfo, ExitError, ExitFatal, ExitReason, ExitRevert, ExitSucceed } from '@polkadot/types/interfaces/evm'; -import type { AnySignature, EcdsaSignature, Ed25519Signature, Era, Extrinsic, ExtrinsicEra, ExtrinsicPayload, ExtrinsicPayloadUnknown, ExtrinsicPayloadV4, ExtrinsicPayloadV5, ExtrinsicSignature, ExtrinsicSignatureV4, ExtrinsicSignatureV5, ExtrinsicUnknown, ExtrinsicV4, ExtrinsicV5, ImmortalEra, MortalEra, MultiSignature, Signature, SignerPayload, Sr25519Signature } from '@polkadot/types/interfaces/extrinsics'; +import type { AnySignature, EcdsaSignature, Ed25519Signature, Era, Extension, ExtensionVersion, Extrinsic, ExtrinsicEra, ExtrinsicPayload, ExtrinsicPayloadUnknown, ExtrinsicPayloadV4, ExtrinsicPayloadV5, ExtrinsicSignature, ExtrinsicSignatureV4, ExtrinsicSignatureV5, ExtrinsicUnknown, ExtrinsicV4, ExtrinsicV5, ExtrinsicVersion, ImmortalEra, MortalEra, MultiSignature, Preamble, Signature, SignerPayload, Sr25519Signature, TransactionExtension } from '@polkadot/types/interfaces/extrinsics'; import type { FungiblesAccessError } from '@polkadot/types/interfaces/fungibles'; import type { AssetOptions, Owner, PermissionLatest, PermissionVersions, PermissionsV1 } from '@polkadot/types/interfaces/genericAsset'; import type { GenesisBuildErr } from '@polkadot/types/interfaces/genesisBuilder'; @@ -491,6 +491,8 @@ declare module '@polkadot/types/types/registry' { ExplicitDisputeStatement: ExplicitDisputeStatement; Exposure: Exposure; ExtendedBalance: ExtendedBalance; + Extension: Extension; + ExtensionVersion: ExtensionVersion; Extrinsic: Extrinsic; ExtrinsicEra: ExtrinsicEra; ExtrinsicInclusionMode: ExtrinsicInclusionMode; @@ -513,6 +515,7 @@ declare module '@polkadot/types/types/registry' { ExtrinsicUnknown: ExtrinsicUnknown; ExtrinsicV4: ExtrinsicV4; ExtrinsicV5: ExtrinsicV5; + ExtrinsicVersion: ExtrinsicVersion; f32: f32; F32: F32; f64: f64; @@ -890,6 +893,7 @@ declare module '@polkadot/types/types/registry' { PortableType: PortableType; PortableTypeV14: PortableTypeV14; PostDispatchInfo: PostDispatchInfo; + Preamble: Preamble; Precommits: Precommits; PrefabWasmModule: PrefabWasmModule; PrefixedStorageKey: PrefixedStorageKey; @@ -1178,6 +1182,7 @@ declare module '@polkadot/types/types/registry' { TraceBlockResponse: TraceBlockResponse; TraceError: TraceError; TransactionalError: TransactionalError; + TransactionExtension: TransactionExtension; TransactionInfo: TransactionInfo; TransactionLongevity: TransactionLongevity; TransactionPriority: TransactionPriority; diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 9e886694cff3..14ce5fa11ae5 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -9,17 +9,17 @@ import type { Address, Call, CodecHash, Hash } from '../interfaces/runtime/index import type { MultiLocation } from '../interfaces/types.js'; import type { CallBase, ExtrinsicPayloadValue, ICompact, IExtrinsic, IKeyringPair, INumber, Registry, SignatureOptions } from '../types/index.js'; import type { GenericExtrinsicEra } from './ExtrinsicEra.js'; -import type { Preamble } from './types.js'; import type { ExtrinsicValueV5 } from './v5/Extrinsic.js'; import { AbstractBase } from '@polkadot/types-codec'; import { compactAddLength, compactFromU8a, compactToU8a, isHex, isU8a, objectProperty, objectSpread, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; -import { BARE_EXTRINSIC, BIT_SIGNED, BIT_UNSIGNED, DEFAULT_PREAMBLE, GENERAL_EXTRINSIC, LATEST_EXTRINSIC_VERSION, LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION, SIGNED_EXTRINSIC, UNMASK_VERSION } from './constants.js'; +import { BARE_EXTRINSIC, BIT_SIGNED, BIT_UNSIGNED, DEFAULT_PREAMBLE_KIND, GENERAL_EXTRINSIC, LATEST_EXTRINSIC_VERSION, LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION, SIGNED_EXTRINSIC, UNMASK_VERSION } from './constants.js'; +import type { PreambleKind } from './types.js'; interface CreateOptions { version?: number; - preamble?: Preamble; + preambleKind?: PreambleKind; } // NOTE The following 2 types, as well as the VERSION structure and the latest export @@ -50,16 +50,17 @@ const PreambleMask = { signed: SIGNED_EXTRINSIC }; + export { LATEST_EXTRINSIC_VERSION }; /** @internal */ -function newFromValue (registry: Registry, value: any, version: number, preamble: Preamble): ExtrinsicVx | ExtrinsicUnknown { +function newFromValue (registry: Registry, value: any, version: number, preambleKind: PreambleKind): ExtrinsicVx | ExtrinsicUnknown { if (value instanceof GenericExtrinsic) { return value.unwrap(); } const isSigned = (version & BIT_SIGNED) === BIT_SIGNED; - const type = (version & UNMASK_VERSION) === 5 ? PREAMBLE[preamble] : VERSIONS[version & UNMASK_VERSION] || VERSIONS[0]; + const type = (version & UNMASK_VERSION) === 5 ? PREAMBLE[preambleKind] : VERSIONS[version & UNMASK_VERSION] || VERSIONS[0]; // we cast here since the VERSION definition is incredibly broad - we don't have a // slice for "only add extrinsic types", and more string definitions become unwieldy @@ -67,20 +68,20 @@ function newFromValue (registry: Registry, value: any, version: number, preamble } /** @internal */ -function decodeExtrinsic (registry: Registry, value?: GenericExtrinsic | ExtrinsicValue | AnyU8a | Call, version: number = LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION, preamble: Preamble = DEFAULT_PREAMBLE): ExtrinsicVx | ExtrinsicUnknown { +function decodeExtrinsic (registry: Registry, value?: GenericExtrinsic | ExtrinsicValue | AnyU8a | Call, version: number = LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION, preambleKind: PreambleKind = 'signed'): ExtrinsicVx | ExtrinsicUnknown { if (isU8a(value) || Array.isArray(value) || isHex(value)) { - return decodeU8a(registry, u8aToU8a(value), version, preamble); + return decodeU8a(registry, u8aToU8a(value), version, preambleKind); } else if (value instanceof registry.createClassUnsafe('Call')) { - return newFromValue(registry, { method: value }, version, preamble); + return newFromValue(registry, { method: value }, version, preambleKind); } - return newFromValue(registry, value, version, preamble); + return newFromValue(registry, value, version, preambleKind); } /** @internal */ -function decodeU8a (registry: Registry, value: Uint8Array, version: number, preamble: Preamble): ExtrinsicVx | ExtrinsicUnknown { +function decodeU8a (registry: Registry, value: Uint8Array, version: number, preambleKind: PreambleKind): ExtrinsicVx | ExtrinsicUnknown { if (!value.length) { - return newFromValue(registry, new Uint8Array(), version, preamble); + return newFromValue(registry, new Uint8Array(), version, preambleKind); } const [offset, length] = compactFromU8a(value); @@ -92,13 +93,13 @@ function decodeU8a (registry: Registry, value: Uint8Array, version: number, prea const data = value.subarray(offset, total); - return newFromValue(registry, data.subarray(1), data[0], preamble); + return newFromValue(registry, data.subarray(1), data[0], preambleKind); } abstract class ExtrinsicBase extends AbstractBase { - readonly #preamble: Preamble; + readonly #preamble: PreambleKind; - constructor (registry: Registry, value: ExtrinsicV5 | ExtrinsicUnknown, initialU8aLength?: number, preamble?: Preamble) { + constructor (registry: Registry, value: ExtrinsicV5 | ExtrinsicUnknown, initialU8aLength?: number, preambleKind?: PreambleKind) { super(registry, value, initialU8aLength); const signKeys = Object.keys(registry.getSignedExtensionTypes()); @@ -110,7 +111,7 @@ abstract class ExtrinsicBase extends AbstractBase extends ExtrinsicBa static LATEST_EXTRINSIC_VERSION = LATEST_EXTRINSIC_VERSION; - constructor (registry: Registry, value?: GenericExtrinsic | ExtrinsicValue | AnyU8a | Call, { preamble, version }: CreateOptions = {}) { - super(registry, decodeExtrinsic(registry, value, version || registry.metadata.extrinsic.version?.toNumber(), preamble), undefined, preamble); + constructor (registry: Registry, value?: GenericExtrinsic | ExtrinsicValue | AnyU8a | Call, { preambleKind, version }: CreateOptions = {}) { + super(registry, decodeExtrinsic(registry, value, version || registry.metadata.extrinsic.version?.toNumber(), preambleKind), undefined, preambleKind); } /** diff --git a/packages/types/src/extrinsic/ExtrinsicPayload.ts b/packages/types/src/extrinsic/ExtrinsicPayload.ts index 4b90787f0e5f..022c5d5a9d66 100644 --- a/packages/types/src/extrinsic/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/ExtrinsicPayload.ts @@ -9,16 +9,16 @@ import type { ExtrinsicPayloadV5 } from '../interfaces/extrinsics/index.js'; import type { Hash, MultiLocation } from '../interfaces/types.js'; import type { ExtrinsicPayloadValue, ICompact, IKeyringPair, INumber, IOption } from '../types/index.js'; import type { GenericExtrinsicEra } from './ExtrinsicEra.js'; -import type { Preamble } from './types.js'; import { AbstractBase } from '@polkadot/types-codec'; import { hexToU8a, isHex, u8aToHex } from '@polkadot/util'; -import { DEFAULT_PREAMBLE, LATEST_EXTRINSIC_VERSION } from './constants.js'; +import { DEFAULT_PREAMBLE_KIND, LATEST_EXTRINSIC_VERSION } from './constants.js'; +import type { PreambleKind } from './types.js'; interface ExtrinsicPayloadOptions { version?: number; - pramble?: Preamble; + pramble?: PreambleKind; } // all our known types that can be returned @@ -41,12 +41,12 @@ const PREAMBLES = { }; /** @internal */ -function decodeExtrinsicPayload (registry: Registry, value?: GenericExtrinsicPayload | ExtrinsicPayloadValue | Uint8Array | string, version = LATEST_EXTRINSIC_VERSION, preamble: Preamble = DEFAULT_PREAMBLE): ExtrinsicPayloadVx { +function decodeExtrinsicPayload (registry: Registry, value?: GenericExtrinsicPayload | ExtrinsicPayloadValue | Uint8Array | string, version = LATEST_EXTRINSIC_VERSION, preambleKind: PreambleKind = DEFAULT_PREAMBLE_KIND): ExtrinsicPayloadVx { if (value instanceof GenericExtrinsicPayload) { return value.unwrap(); } - const extVersion = version === 5 ? PREAMBLES[preamble] : VERSIONS[version] || VERSIONS[0]; + const extVersion = version === 5 ? PREAMBLES[preambleKind] : VERSIONS[version] || VERSIONS[0]; /** * HACK: In order to change the assetId from `number | object` to HexString (While maintaining the true type ie Option), diff --git a/packages/types/src/extrinsic/constants.ts b/packages/types/src/extrinsic/constants.ts index aff05aef2d3d..6528a1212a46 100644 --- a/packages/types/src/extrinsic/constants.ts +++ b/packages/types/src/extrinsic/constants.ts @@ -13,7 +13,7 @@ export const IMMORTAL_ERA = new Uint8Array([0]); export const UNMASK_VERSION = 0b01111111; -export const DEFAULT_PREAMBLE = 'signed'; +export const DEFAULT_PREAMBLE_KIND = 'signed'; // Latest extrinsic version is v5, which has backwards compatibility for v4 signed extrinsics export const LATEST_EXTRINSIC_VERSION = 5; diff --git a/packages/types/src/extrinsic/types.ts b/packages/types/src/extrinsic/types.ts index c468dc8582d2..de47cb688b1c 100644 --- a/packages/types/src/extrinsic/types.ts +++ b/packages/types/src/extrinsic/types.ts @@ -2,10 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 import type { AnyNumber } from '@polkadot/types-codec/types'; +import type { Preamble } from '../interfaces/types.js'; export interface ExtrinsicOptions { isSigned: boolean; version: number; + preambleKind: PreambleKind; } export interface ExtrinsicPayloadOptions { @@ -22,4 +24,4 @@ export interface ExtrinsicExtraValue { tip?: AnyNumber; } -export type Preamble = 'signed' | 'bare' | 'general'; +export type PreambleKind = 'bare' | 'general' | 'signed'; diff --git a/packages/types/src/extrinsic/v5/Extrinsic.ts b/packages/types/src/extrinsic/v5/Extrinsic.ts index a1fd4f2ca9ce..9429f6f4d0be 100644 --- a/packages/types/src/extrinsic/v5/Extrinsic.ts +++ b/packages/types/src/extrinsic/v5/Extrinsic.ts @@ -2,19 +2,20 @@ // SPDX-License-Identifier: Apache-2.0 import type { HexString } from '@polkadot/util/types'; -import type { ExtrinsicSignatureV5 } from '../../interfaces/extrinsics/index.js'; +import type { ExtrinsicSignatureV5, Preamble } from '../../interfaces/extrinsics/index.js'; import type { Address, Call } from '../../interfaces/runtime/index.js'; import type { ExtrinsicPayloadValue, IExtrinsicV5Impl, IKeyringPair, Registry, SignatureOptions } from '../../types/index.js'; -import type { ExtrinsicOptions, Preamble } from '../types.js'; +import type { ExtrinsicOptions, PreambleKind } from '../types.js'; import { Struct } from '@polkadot/types-codec'; import { isU8a } from '@polkadot/util'; +import { DEFAULT_PREAMBLE_KIND } from '../constants.js'; export const EXTRINSIC_VERSION = 5; export interface ExtrinsicValueV5 { method?: Call; - signature?: ExtrinsicSignatureV5; + preamble?: Preamble; } /** @@ -23,28 +24,45 @@ export interface ExtrinsicValueV5 { * The third generation of compact extrinsics */ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { - constructor (registry: Registry, value?: Uint8Array | ExtrinsicValueV5 | Call, { isSigned }: Partial = {}) { + constructor(registry: Registry, value?: Uint8Array | ExtrinsicValueV5 | Call, { preambleKind }: Partial = {}) { + super(registry, { - signature: 'ExtrinsicSignatureV5', + preamble: 'Preamble', // eslint-disable-next-line sort-keys method: 'Call' - }, GenericExtrinsicV5.decodeExtrinsic(registry, value, isSigned)); + }, GenericExtrinsicV5.decodeExtrinsic(registry, value, preambleKind)); } /** @internal */ - public static decodeExtrinsic (registry: Registry, value?: Call | Uint8Array | ExtrinsicValueV5, isSigned = false): ExtrinsicValueV5 { + public static decodeExtrinsic(registry: Registry, value?: Call | Uint8Array | ExtrinsicValueV5, preambleKind = DEFAULT_PREAMBLE_KIND): ExtrinsicValueV5 { if (value instanceof GenericExtrinsicV5) { return value; } else if (value instanceof registry.createClassUnsafe('Call')) { return { method: value }; } else if (isU8a(value)) { + let preamble: Preamble; // here we decode manually since we need to pull through the version information - const signature = registry.createTypeUnsafe('ExtrinsicSignatureV5', [value, { isSigned }]); - const method = registry.createTypeUnsafe('Call', [value.subarray(signature.encodedLength)]); + if (preambleKind === 'bare') { + preamble = registry.createTypeUnsafe('Preamble', [{ + type: 'Bare', + asBare: EXTRINSIC_VERSION + }]) + } else if (preambleKind === 'signed') { + preamble = registry.createTypeUnsafe('Preamble', [{ + type: 'Signed', + asSigned: value + }]) + } else { + preamble = registry.createTypeUnsafe('Preamble', [{ + type: 'General', + asSigned: value + }]) + } + const method = registry.createTypeUnsafe('Call', [value.subarray(preamble.encodedLength)]); return { method, - signature + preamble }; } @@ -54,39 +72,39 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { /** * @description The length of the value when encoded as a Uint8Array */ - public override get encodedLength (): number { + public override get encodedLength(): number { return this.toU8a().length; } /** * @description The [[Call]] this extrinsic wraps */ - public get method (): Call { + public get method(): Call { return this.getT('method'); } /** * @description The [[ExtrinsicSignatureV5]] */ - public get signature (): ExtrinsicSignatureV5 { + public get signature(): ExtrinsicSignatureV5 { return this.getT('signature'); } /** * @description The version for the signature */ - public get version (): number { + public get version(): number { return EXTRINSIC_VERSION; } - public get preamble (): Preamble { + public get preamble(): Preamble { return this.getT('preamble'); } /** * @description Add an [[ExtrinsicSignatureV5]] to the extrinsic (already generated) */ - public addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | HexString, payload: ExtrinsicPayloadValue | Uint8Array | HexString): GenericExtrinsicV5 { + public addSignature(signer: Address | Uint8Array | string, signature: Uint8Array | HexString, payload: ExtrinsicPayloadValue | Uint8Array | HexString): GenericExtrinsicV5 { this.signature.addSignature(signer, signature, payload); return this; @@ -95,7 +113,7 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { /** * @description Sign the extrinsic with a specific keypair */ - public sign (account: IKeyringPair, options: SignatureOptions): GenericExtrinsicV5 { + public sign(account: IKeyringPair, options: SignatureOptions): GenericExtrinsicV5 { this.signature.sign(this.method, account, options); return this; @@ -104,7 +122,7 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { /** * @describe Adds a fake signature to the extrinsic */ - public signFake (signer: Address | Uint8Array | string, options: SignatureOptions): GenericExtrinsicV5 { + public signFake(signer: Address | Uint8Array | string, options: SignatureOptions): GenericExtrinsicV5 { this.signature.signFake(this.method, signer, options); return this; diff --git a/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts b/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts index 7ab070ff1876..469d510a0caa 100644 --- a/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts +++ b/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts @@ -38,7 +38,7 @@ export class GenericExtrinsicSignatureV5 extends Struct implements IExtrinsicSig registry, objectSpread( // eslint-disable-next-line sort-keys - { signer: 'Address', signature: 'ExtrinsicSignature', transactionExtensionVersion: 'u8' }, + { extrinsicFormatVersion: 'ExtrinsicVersion', signer: 'Address', signature: 'ExtrinsicSignature', transactionExtensionVersion: 'ExtensionVersion' }, signTypes ), GenericExtrinsicSignatureV5.decodeExtrinsicSignature(value, isSigned) diff --git a/packages/types/src/extrinsic/v5/Preamble.ts b/packages/types/src/extrinsic/v5/Preamble.ts new file mode 100644 index 000000000000..eb13a5913678 --- /dev/null +++ b/packages/types/src/extrinsic/v5/Preamble.ts @@ -0,0 +1,63 @@ +// Copyright 2017-2024 @polkadot/types authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { ITuple, Registry } from "@polkadot/types-codec/types"; +import type { Extension, ExtensionVersion, ExtrinsicSignatureV4, ExtrinsicVersion, MultiAddress, Preamble } from "@polkadot/types/interfaces"; + +export class GenericPreamble implements Preamble { + #preambleType: 'Bare' | 'Signed' | 'General'; + #asBare?: ExtrinsicVersion; + #asGeneral?: ITuple<[ExtensionVersion, Extension]>; + #asSigned?: ITuple<[MultiAddress, ExtrinsicSignatureV4, ExtensionVersion, Extension, ExtrinsicVersion]>; + + constructor(registry: Registry, type: 'Bare' | 'Signed' | 'General', data: any) { + this.#preambleType = type; + + if (type === 'Bare') { + this.#asBare = registry.createTypeUnsafe('u8', data); + } else if (type === 'Signed') { + this.#asSigned = registry.createTypeUnsafe>('(MultiAddress, ExtrinsicSignatureV4, ExtensionVersion, Extension, ExtrinsicVersion)', data); + } else { + this.#asGeneral = registry.createTypeUnsafe>('(ExtensionVersion, Extension)', data) + } + } + + get isBare(): boolean { + return this.#preambleType === 'Bare'; + } + + get asBare(): ExtrinsicVersion { + if (!this.isBare) { + throw new Error("Not a Bare type"); + } + return this.#asBare!; + } + + get isSigned(): boolean { + return this.#preambleType === 'Signed'; + } + + get asSigned(): ITuple<[MultiAddress, ExtrinsicSignatureV4, ExtensionVersion, Extension, ExtrinsicVersion]> { + if (!this.isSigned) { + throw new Error("Not a Signed type"); + } + return this.#asSigned!; + } + + get isGeneral(): boolean { + return this.#preambleType === 'General'; + } + + get asGeneral(): ITuple<[ExtensionVersion, Extension]> { + if (!this.isGeneral) { + throw new Error("Not a General type"); + } + return this.#asGeneral!; + } + + get type(): 'Bare' | 'Signed' | 'General' { + return this.#preambleType; + } + + +} \ No newline at end of file diff --git a/packages/types/src/interfaces/extrinsics/definitions.ts b/packages/types/src/interfaces/extrinsics/definitions.ts index a0c78d17492f..a9c06d1c83f1 100644 --- a/packages/types/src/interfaces/extrinsics/definitions.ts +++ b/packages/types/src/interfaces/extrinsics/definitions.ts @@ -40,6 +40,21 @@ export default { SignerPayload: 'GenericSignerPayload', EcdsaSignature: '[u8; 65]', Ed25519Signature: 'H512', - Sr25519Signature: 'H512' + Sr25519Signature: 'H512', + ExtrinsicVersion: 'u8', + ExtensionVersion: 'u8', + TransactionExtension: { + identifier: 'Text', + type: 'SiLookupTypeId', + Implicit: 'SiLookupTypeId' + }, + Extension: 'TransactionExtension', + Preamble: { + _enum: { + Bare: '(ExtrinsicVersion)', + Signed: '(MultiAddress, GenericExtrinsicSignatureV4, ExtensionVersion, Extension, ExtrinsicVersion)', + General: '(ExtensionVersion, Extension)', + } + } } } as Definitions; diff --git a/packages/types/src/interfaces/extrinsics/types.ts b/packages/types/src/interfaces/extrinsics/types.ts index 056443f08841..6b435e2e8df1 100644 --- a/packages/types/src/interfaces/extrinsics/types.ts +++ b/packages/types/src/interfaces/extrinsics/types.ts @@ -2,8 +2,10 @@ /* eslint-disable */ import type { GenericExtrinsic, GenericExtrinsicEra, GenericExtrinsicPayload, GenericExtrinsicPayloadUnknown, GenericExtrinsicPayloadV4, GenericExtrinsicPayloadV5, GenericExtrinsicSignatureV4, GenericExtrinsicSignatureV5, GenericExtrinsicUnknown, GenericExtrinsicV4, GenericExtrinsicV5, GenericImmortalEra, GenericMortalEra, GenericSignerPayload } from '@polkadot/types'; -import type { Enum, U8aFixed } from '@polkadot/types-codec'; -import type { H512 } from '@polkadot/types/interfaces/runtime'; +import type { Enum, Struct, Text, U8aFixed, u8 } from '@polkadot/types-codec'; +import type { ITuple } from '@polkadot/types-codec/types'; +import type { H512, MultiAddress } from '@polkadot/types/interfaces/runtime'; +import type { SiLookupTypeId } from '@polkadot/types/interfaces/scaleInfo'; /** @name AnySignature */ export interface AnySignature extends H512 {} @@ -17,6 +19,12 @@ export interface Ed25519Signature extends H512 {} /** @name Era */ export interface Era extends ExtrinsicEra {} +/** @name Extension */ +export interface Extension extends TransactionExtension {} + +/** @name ExtensionVersion */ +export interface ExtensionVersion extends u8 {} + /** @name Extrinsic */ export interface Extrinsic extends GenericExtrinsic {} @@ -53,6 +61,9 @@ export interface ExtrinsicV4 extends GenericExtrinsicV4 {} /** @name ExtrinsicV5 */ export interface ExtrinsicV5 extends GenericExtrinsicV5 {} +/** @name ExtrinsicVersion */ +export interface ExtrinsicVersion extends u8 {} + /** @name ImmortalEra */ export interface ImmortalEra extends GenericImmortalEra {} @@ -70,6 +81,17 @@ export interface MultiSignature extends Enum { readonly type: 'Ed25519' | 'Sr25519' | 'Ecdsa'; } +/** @name Preamble */ +export interface Preamble extends Enum { + readonly isBare: boolean; + readonly asBare: ExtrinsicVersion; + readonly isSigned: boolean; + readonly asSigned: ITuple<[MultiAddress, GenericExtrinsicSignatureV4, ExtensionVersion, Extension, ExtrinsicVersion]>; + readonly isGeneral: boolean; + readonly asGeneral: ITuple<[ExtensionVersion, Extension]>; + readonly type: 'Bare' | 'Signed' | 'General'; +} + /** @name Signature */ export interface Signature extends H512 {} @@ -79,4 +101,11 @@ export interface SignerPayload extends GenericSignerPayload {} /** @name Sr25519Signature */ export interface Sr25519Signature extends H512 {} +/** @name TransactionExtension */ +export interface TransactionExtension extends Struct { + readonly identifier: Text; + readonly type: SiLookupTypeId; + readonly Implicit: SiLookupTypeId; +} + export type PHANTOM_EXTRINSICS = 'extrinsics'; diff --git a/packages/types/src/types/extrinsic.ts b/packages/types/src/types/extrinsic.ts index ed0546aef045..2845adaa92a5 100644 --- a/packages/types/src/types/extrinsic.ts +++ b/packages/types/src/types/extrinsic.ts @@ -4,7 +4,7 @@ import type { AnyJson, AnyNumber, AnyTuple, AnyU8a, Codec } from '@polkadot/types-codec/types'; import type { HexString } from '@polkadot/util/types'; import type { ExtrinsicStatus } from '../interfaces/author/index.js'; -import type { EcdsaSignature, Ed25519Signature, Sr25519Signature } from '../interfaces/extrinsics/index.js'; +import type { EcdsaSignature, Ed25519Signature, Preamble, Sr25519Signature } from '../interfaces/extrinsics/index.js'; import type { Address, Call, H256, Hash } from '../interfaces/runtime/index.js'; import type { DispatchError, DispatchInfo, EventRecord } from '../interfaces/system/index.js'; import type { ICompact, IKeyringPair, IMethod, INumber, IRuntimeVersionBase } from './interfaces.js'; @@ -249,7 +249,7 @@ export interface IExtrinsicImpl extends IExtrinsicSignable { export interface IExtrinsicV5Impl extends IExtrinsicSignable { readonly method: Call; - readonly signature: IExtrinsicSignature; + readonly preamble: Preamble; readonly version: number; } From 072f69a91c366204ba39ca8430507d31ea237a12 Mon Sep 17 00:00:00 2001 From: bee344 Date: Tue, 24 Sep 2024 14:30:56 -0300 Subject: [PATCH 31/71] Revert "unfinished Preamble Class" This reverts commit 0b1c350fcc97e6f724ed84f66742be472c5daa71. --- .../types-augment/src/registry/interfaces.ts | 7 +-- packages/types/src/extrinsic/Extrinsic.ts | 35 +++++------ .../types/src/extrinsic/ExtrinsicPayload.ts | 10 +-- packages/types/src/extrinsic/constants.ts | 2 +- packages/types/src/extrinsic/types.ts | 4 +- packages/types/src/extrinsic/v5/Extrinsic.ts | 54 ++++++---------- .../src/extrinsic/v5/ExtrinsicSignature.ts | 2 +- packages/types/src/extrinsic/v5/Preamble.ts | 63 ------------------- .../src/interfaces/extrinsics/definitions.ts | 17 +---- .../types/src/interfaces/extrinsics/types.ts | 33 +--------- packages/types/src/types/extrinsic.ts | 4 +- 11 files changed, 49 insertions(+), 182 deletions(-) delete mode 100644 packages/types/src/extrinsic/v5/Preamble.ts diff --git a/packages/types-augment/src/registry/interfaces.ts b/packages/types-augment/src/registry/interfaces.ts index 91f4c086d5d0..5b77684993c2 100644 --- a/packages/types-augment/src/registry/interfaces.ts +++ b/packages/types-augment/src/registry/interfaces.ts @@ -35,7 +35,7 @@ import type { ApprovalFlag, DefunctVoter, Renouncing, SetIndex, Vote, VoteIndex, import type { CreatedBlock, ImportedAux } from '@polkadot/types/interfaces/engine'; import type { BlockV0, BlockV1, BlockV2, EIP1559Transaction, EIP2930Transaction, EthAccessList, EthAccessListItem, EthAccount, EthAddress, EthBlock, EthBloom, EthCallRequest, EthFeeHistory, EthFilter, EthFilterAddress, EthFilterChanges, EthFilterTopic, EthFilterTopicEntry, EthFilterTopicInner, EthHeader, EthLog, EthReceipt, EthReceiptV0, EthReceiptV3, EthRichBlock, EthRichHeader, EthStorageProof, EthSubKind, EthSubParams, EthSubResult, EthSyncInfo, EthSyncStatus, EthTransaction, EthTransactionAction, EthTransactionCondition, EthTransactionRequest, EthTransactionSignature, EthTransactionStatus, EthWork, EthereumAccountId, EthereumAddress, EthereumLookupSource, EthereumSignature, LegacyTransaction, TransactionV0, TransactionV1, TransactionV2 } from '@polkadot/types/interfaces/eth'; import type { EvmAccount, EvmCallInfo, EvmCallInfoV2, EvmCreateInfo, EvmCreateInfoV2, EvmLog, EvmVicinity, EvmWeightInfo, ExitError, ExitFatal, ExitReason, ExitRevert, ExitSucceed } from '@polkadot/types/interfaces/evm'; -import type { AnySignature, EcdsaSignature, Ed25519Signature, Era, Extension, ExtensionVersion, Extrinsic, ExtrinsicEra, ExtrinsicPayload, ExtrinsicPayloadUnknown, ExtrinsicPayloadV4, ExtrinsicPayloadV5, ExtrinsicSignature, ExtrinsicSignatureV4, ExtrinsicSignatureV5, ExtrinsicUnknown, ExtrinsicV4, ExtrinsicV5, ExtrinsicVersion, ImmortalEra, MortalEra, MultiSignature, Preamble, Signature, SignerPayload, Sr25519Signature, TransactionExtension } from '@polkadot/types/interfaces/extrinsics'; +import type { AnySignature, EcdsaSignature, Ed25519Signature, Era, Extrinsic, ExtrinsicEra, ExtrinsicPayload, ExtrinsicPayloadUnknown, ExtrinsicPayloadV4, ExtrinsicPayloadV5, ExtrinsicSignature, ExtrinsicSignatureV4, ExtrinsicSignatureV5, ExtrinsicUnknown, ExtrinsicV4, ExtrinsicV5, ImmortalEra, MortalEra, MultiSignature, Signature, SignerPayload, Sr25519Signature } from '@polkadot/types/interfaces/extrinsics'; import type { FungiblesAccessError } from '@polkadot/types/interfaces/fungibles'; import type { AssetOptions, Owner, PermissionLatest, PermissionVersions, PermissionsV1 } from '@polkadot/types/interfaces/genericAsset'; import type { GenesisBuildErr } from '@polkadot/types/interfaces/genesisBuilder'; @@ -491,8 +491,6 @@ declare module '@polkadot/types/types/registry' { ExplicitDisputeStatement: ExplicitDisputeStatement; Exposure: Exposure; ExtendedBalance: ExtendedBalance; - Extension: Extension; - ExtensionVersion: ExtensionVersion; Extrinsic: Extrinsic; ExtrinsicEra: ExtrinsicEra; ExtrinsicInclusionMode: ExtrinsicInclusionMode; @@ -515,7 +513,6 @@ declare module '@polkadot/types/types/registry' { ExtrinsicUnknown: ExtrinsicUnknown; ExtrinsicV4: ExtrinsicV4; ExtrinsicV5: ExtrinsicV5; - ExtrinsicVersion: ExtrinsicVersion; f32: f32; F32: F32; f64: f64; @@ -893,7 +890,6 @@ declare module '@polkadot/types/types/registry' { PortableType: PortableType; PortableTypeV14: PortableTypeV14; PostDispatchInfo: PostDispatchInfo; - Preamble: Preamble; Precommits: Precommits; PrefabWasmModule: PrefabWasmModule; PrefixedStorageKey: PrefixedStorageKey; @@ -1182,7 +1178,6 @@ declare module '@polkadot/types/types/registry' { TraceBlockResponse: TraceBlockResponse; TraceError: TraceError; TransactionalError: TransactionalError; - TransactionExtension: TransactionExtension; TransactionInfo: TransactionInfo; TransactionLongevity: TransactionLongevity; TransactionPriority: TransactionPriority; diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 14ce5fa11ae5..9e886694cff3 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -9,17 +9,17 @@ import type { Address, Call, CodecHash, Hash } from '../interfaces/runtime/index import type { MultiLocation } from '../interfaces/types.js'; import type { CallBase, ExtrinsicPayloadValue, ICompact, IExtrinsic, IKeyringPair, INumber, Registry, SignatureOptions } from '../types/index.js'; import type { GenericExtrinsicEra } from './ExtrinsicEra.js'; +import type { Preamble } from './types.js'; import type { ExtrinsicValueV5 } from './v5/Extrinsic.js'; import { AbstractBase } from '@polkadot/types-codec'; import { compactAddLength, compactFromU8a, compactToU8a, isHex, isU8a, objectProperty, objectSpread, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; -import { BARE_EXTRINSIC, BIT_SIGNED, BIT_UNSIGNED, DEFAULT_PREAMBLE_KIND, GENERAL_EXTRINSIC, LATEST_EXTRINSIC_VERSION, LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION, SIGNED_EXTRINSIC, UNMASK_VERSION } from './constants.js'; -import type { PreambleKind } from './types.js'; +import { BARE_EXTRINSIC, BIT_SIGNED, BIT_UNSIGNED, DEFAULT_PREAMBLE, GENERAL_EXTRINSIC, LATEST_EXTRINSIC_VERSION, LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION, SIGNED_EXTRINSIC, UNMASK_VERSION } from './constants.js'; interface CreateOptions { version?: number; - preambleKind?: PreambleKind; + preamble?: Preamble; } // NOTE The following 2 types, as well as the VERSION structure and the latest export @@ -50,17 +50,16 @@ const PreambleMask = { signed: SIGNED_EXTRINSIC }; - export { LATEST_EXTRINSIC_VERSION }; /** @internal */ -function newFromValue (registry: Registry, value: any, version: number, preambleKind: PreambleKind): ExtrinsicVx | ExtrinsicUnknown { +function newFromValue (registry: Registry, value: any, version: number, preamble: Preamble): ExtrinsicVx | ExtrinsicUnknown { if (value instanceof GenericExtrinsic) { return value.unwrap(); } const isSigned = (version & BIT_SIGNED) === BIT_SIGNED; - const type = (version & UNMASK_VERSION) === 5 ? PREAMBLE[preambleKind] : VERSIONS[version & UNMASK_VERSION] || VERSIONS[0]; + const type = (version & UNMASK_VERSION) === 5 ? PREAMBLE[preamble] : VERSIONS[version & UNMASK_VERSION] || VERSIONS[0]; // we cast here since the VERSION definition is incredibly broad - we don't have a // slice for "only add extrinsic types", and more string definitions become unwieldy @@ -68,20 +67,20 @@ function newFromValue (registry: Registry, value: any, version: number, preamble } /** @internal */ -function decodeExtrinsic (registry: Registry, value?: GenericExtrinsic | ExtrinsicValue | AnyU8a | Call, version: number = LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION, preambleKind: PreambleKind = 'signed'): ExtrinsicVx | ExtrinsicUnknown { +function decodeExtrinsic (registry: Registry, value?: GenericExtrinsic | ExtrinsicValue | AnyU8a | Call, version: number = LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION, preamble: Preamble = DEFAULT_PREAMBLE): ExtrinsicVx | ExtrinsicUnknown { if (isU8a(value) || Array.isArray(value) || isHex(value)) { - return decodeU8a(registry, u8aToU8a(value), version, preambleKind); + return decodeU8a(registry, u8aToU8a(value), version, preamble); } else if (value instanceof registry.createClassUnsafe('Call')) { - return newFromValue(registry, { method: value }, version, preambleKind); + return newFromValue(registry, { method: value }, version, preamble); } - return newFromValue(registry, value, version, preambleKind); + return newFromValue(registry, value, version, preamble); } /** @internal */ -function decodeU8a (registry: Registry, value: Uint8Array, version: number, preambleKind: PreambleKind): ExtrinsicVx | ExtrinsicUnknown { +function decodeU8a (registry: Registry, value: Uint8Array, version: number, preamble: Preamble): ExtrinsicVx | ExtrinsicUnknown { if (!value.length) { - return newFromValue(registry, new Uint8Array(), version, preambleKind); + return newFromValue(registry, new Uint8Array(), version, preamble); } const [offset, length] = compactFromU8a(value); @@ -93,13 +92,13 @@ function decodeU8a (registry: Registry, value: Uint8Array, version: number, prea const data = value.subarray(offset, total); - return newFromValue(registry, data.subarray(1), data[0], preambleKind); + return newFromValue(registry, data.subarray(1), data[0], preamble); } abstract class ExtrinsicBase extends AbstractBase { - readonly #preamble: PreambleKind; + readonly #preamble: Preamble; - constructor (registry: Registry, value: ExtrinsicV5 | ExtrinsicUnknown, initialU8aLength?: number, preambleKind?: PreambleKind) { + constructor (registry: Registry, value: ExtrinsicV5 | ExtrinsicUnknown, initialU8aLength?: number, preamble?: Preamble) { super(registry, value, initialU8aLength); const signKeys = Object.keys(registry.getSignedExtensionTypes()); @@ -111,7 +110,7 @@ abstract class ExtrinsicBase extends AbstractBase extends ExtrinsicBa static LATEST_EXTRINSIC_VERSION = LATEST_EXTRINSIC_VERSION; - constructor (registry: Registry, value?: GenericExtrinsic | ExtrinsicValue | AnyU8a | Call, { preambleKind, version }: CreateOptions = {}) { - super(registry, decodeExtrinsic(registry, value, version || registry.metadata.extrinsic.version?.toNumber(), preambleKind), undefined, preambleKind); + constructor (registry: Registry, value?: GenericExtrinsic | ExtrinsicValue | AnyU8a | Call, { preamble, version }: CreateOptions = {}) { + super(registry, decodeExtrinsic(registry, value, version || registry.metadata.extrinsic.version?.toNumber(), preamble), undefined, preamble); } /** diff --git a/packages/types/src/extrinsic/ExtrinsicPayload.ts b/packages/types/src/extrinsic/ExtrinsicPayload.ts index 022c5d5a9d66..4b90787f0e5f 100644 --- a/packages/types/src/extrinsic/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/ExtrinsicPayload.ts @@ -9,16 +9,16 @@ import type { ExtrinsicPayloadV5 } from '../interfaces/extrinsics/index.js'; import type { Hash, MultiLocation } from '../interfaces/types.js'; import type { ExtrinsicPayloadValue, ICompact, IKeyringPair, INumber, IOption } from '../types/index.js'; import type { GenericExtrinsicEra } from './ExtrinsicEra.js'; +import type { Preamble } from './types.js'; import { AbstractBase } from '@polkadot/types-codec'; import { hexToU8a, isHex, u8aToHex } from '@polkadot/util'; -import { DEFAULT_PREAMBLE_KIND, LATEST_EXTRINSIC_VERSION } from './constants.js'; -import type { PreambleKind } from './types.js'; +import { DEFAULT_PREAMBLE, LATEST_EXTRINSIC_VERSION } from './constants.js'; interface ExtrinsicPayloadOptions { version?: number; - pramble?: PreambleKind; + pramble?: Preamble; } // all our known types that can be returned @@ -41,12 +41,12 @@ const PREAMBLES = { }; /** @internal */ -function decodeExtrinsicPayload (registry: Registry, value?: GenericExtrinsicPayload | ExtrinsicPayloadValue | Uint8Array | string, version = LATEST_EXTRINSIC_VERSION, preambleKind: PreambleKind = DEFAULT_PREAMBLE_KIND): ExtrinsicPayloadVx { +function decodeExtrinsicPayload (registry: Registry, value?: GenericExtrinsicPayload | ExtrinsicPayloadValue | Uint8Array | string, version = LATEST_EXTRINSIC_VERSION, preamble: Preamble = DEFAULT_PREAMBLE): ExtrinsicPayloadVx { if (value instanceof GenericExtrinsicPayload) { return value.unwrap(); } - const extVersion = version === 5 ? PREAMBLES[preambleKind] : VERSIONS[version] || VERSIONS[0]; + const extVersion = version === 5 ? PREAMBLES[preamble] : VERSIONS[version] || VERSIONS[0]; /** * HACK: In order to change the assetId from `number | object` to HexString (While maintaining the true type ie Option), diff --git a/packages/types/src/extrinsic/constants.ts b/packages/types/src/extrinsic/constants.ts index 6528a1212a46..aff05aef2d3d 100644 --- a/packages/types/src/extrinsic/constants.ts +++ b/packages/types/src/extrinsic/constants.ts @@ -13,7 +13,7 @@ export const IMMORTAL_ERA = new Uint8Array([0]); export const UNMASK_VERSION = 0b01111111; -export const DEFAULT_PREAMBLE_KIND = 'signed'; +export const DEFAULT_PREAMBLE = 'signed'; // Latest extrinsic version is v5, which has backwards compatibility for v4 signed extrinsics export const LATEST_EXTRINSIC_VERSION = 5; diff --git a/packages/types/src/extrinsic/types.ts b/packages/types/src/extrinsic/types.ts index de47cb688b1c..c468dc8582d2 100644 --- a/packages/types/src/extrinsic/types.ts +++ b/packages/types/src/extrinsic/types.ts @@ -2,12 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 import type { AnyNumber } from '@polkadot/types-codec/types'; -import type { Preamble } from '../interfaces/types.js'; export interface ExtrinsicOptions { isSigned: boolean; version: number; - preambleKind: PreambleKind; } export interface ExtrinsicPayloadOptions { @@ -24,4 +22,4 @@ export interface ExtrinsicExtraValue { tip?: AnyNumber; } -export type PreambleKind = 'bare' | 'general' | 'signed'; +export type Preamble = 'signed' | 'bare' | 'general'; diff --git a/packages/types/src/extrinsic/v5/Extrinsic.ts b/packages/types/src/extrinsic/v5/Extrinsic.ts index 9429f6f4d0be..a1fd4f2ca9ce 100644 --- a/packages/types/src/extrinsic/v5/Extrinsic.ts +++ b/packages/types/src/extrinsic/v5/Extrinsic.ts @@ -2,20 +2,19 @@ // SPDX-License-Identifier: Apache-2.0 import type { HexString } from '@polkadot/util/types'; -import type { ExtrinsicSignatureV5, Preamble } from '../../interfaces/extrinsics/index.js'; +import type { ExtrinsicSignatureV5 } from '../../interfaces/extrinsics/index.js'; import type { Address, Call } from '../../interfaces/runtime/index.js'; import type { ExtrinsicPayloadValue, IExtrinsicV5Impl, IKeyringPair, Registry, SignatureOptions } from '../../types/index.js'; -import type { ExtrinsicOptions, PreambleKind } from '../types.js'; +import type { ExtrinsicOptions, Preamble } from '../types.js'; import { Struct } from '@polkadot/types-codec'; import { isU8a } from '@polkadot/util'; -import { DEFAULT_PREAMBLE_KIND } from '../constants.js'; export const EXTRINSIC_VERSION = 5; export interface ExtrinsicValueV5 { method?: Call; - preamble?: Preamble; + signature?: ExtrinsicSignatureV5; } /** @@ -24,45 +23,28 @@ export interface ExtrinsicValueV5 { * The third generation of compact extrinsics */ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { - constructor(registry: Registry, value?: Uint8Array | ExtrinsicValueV5 | Call, { preambleKind }: Partial = {}) { - + constructor (registry: Registry, value?: Uint8Array | ExtrinsicValueV5 | Call, { isSigned }: Partial = {}) { super(registry, { - preamble: 'Preamble', + signature: 'ExtrinsicSignatureV5', // eslint-disable-next-line sort-keys method: 'Call' - }, GenericExtrinsicV5.decodeExtrinsic(registry, value, preambleKind)); + }, GenericExtrinsicV5.decodeExtrinsic(registry, value, isSigned)); } /** @internal */ - public static decodeExtrinsic(registry: Registry, value?: Call | Uint8Array | ExtrinsicValueV5, preambleKind = DEFAULT_PREAMBLE_KIND): ExtrinsicValueV5 { + public static decodeExtrinsic (registry: Registry, value?: Call | Uint8Array | ExtrinsicValueV5, isSigned = false): ExtrinsicValueV5 { if (value instanceof GenericExtrinsicV5) { return value; } else if (value instanceof registry.createClassUnsafe('Call')) { return { method: value }; } else if (isU8a(value)) { - let preamble: Preamble; // here we decode manually since we need to pull through the version information - if (preambleKind === 'bare') { - preamble = registry.createTypeUnsafe('Preamble', [{ - type: 'Bare', - asBare: EXTRINSIC_VERSION - }]) - } else if (preambleKind === 'signed') { - preamble = registry.createTypeUnsafe('Preamble', [{ - type: 'Signed', - asSigned: value - }]) - } else { - preamble = registry.createTypeUnsafe('Preamble', [{ - type: 'General', - asSigned: value - }]) - } - const method = registry.createTypeUnsafe('Call', [value.subarray(preamble.encodedLength)]); + const signature = registry.createTypeUnsafe('ExtrinsicSignatureV5', [value, { isSigned }]); + const method = registry.createTypeUnsafe('Call', [value.subarray(signature.encodedLength)]); return { method, - preamble + signature }; } @@ -72,39 +54,39 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { /** * @description The length of the value when encoded as a Uint8Array */ - public override get encodedLength(): number { + public override get encodedLength (): number { return this.toU8a().length; } /** * @description The [[Call]] this extrinsic wraps */ - public get method(): Call { + public get method (): Call { return this.getT('method'); } /** * @description The [[ExtrinsicSignatureV5]] */ - public get signature(): ExtrinsicSignatureV5 { + public get signature (): ExtrinsicSignatureV5 { return this.getT('signature'); } /** * @description The version for the signature */ - public get version(): number { + public get version (): number { return EXTRINSIC_VERSION; } - public get preamble(): Preamble { + public get preamble (): Preamble { return this.getT('preamble'); } /** * @description Add an [[ExtrinsicSignatureV5]] to the extrinsic (already generated) */ - public addSignature(signer: Address | Uint8Array | string, signature: Uint8Array | HexString, payload: ExtrinsicPayloadValue | Uint8Array | HexString): GenericExtrinsicV5 { + public addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | HexString, payload: ExtrinsicPayloadValue | Uint8Array | HexString): GenericExtrinsicV5 { this.signature.addSignature(signer, signature, payload); return this; @@ -113,7 +95,7 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { /** * @description Sign the extrinsic with a specific keypair */ - public sign(account: IKeyringPair, options: SignatureOptions): GenericExtrinsicV5 { + public sign (account: IKeyringPair, options: SignatureOptions): GenericExtrinsicV5 { this.signature.sign(this.method, account, options); return this; @@ -122,7 +104,7 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { /** * @describe Adds a fake signature to the extrinsic */ - public signFake(signer: Address | Uint8Array | string, options: SignatureOptions): GenericExtrinsicV5 { + public signFake (signer: Address | Uint8Array | string, options: SignatureOptions): GenericExtrinsicV5 { this.signature.signFake(this.method, signer, options); return this; diff --git a/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts b/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts index 469d510a0caa..7ab070ff1876 100644 --- a/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts +++ b/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts @@ -38,7 +38,7 @@ export class GenericExtrinsicSignatureV5 extends Struct implements IExtrinsicSig registry, objectSpread( // eslint-disable-next-line sort-keys - { extrinsicFormatVersion: 'ExtrinsicVersion', signer: 'Address', signature: 'ExtrinsicSignature', transactionExtensionVersion: 'ExtensionVersion' }, + { signer: 'Address', signature: 'ExtrinsicSignature', transactionExtensionVersion: 'u8' }, signTypes ), GenericExtrinsicSignatureV5.decodeExtrinsicSignature(value, isSigned) diff --git a/packages/types/src/extrinsic/v5/Preamble.ts b/packages/types/src/extrinsic/v5/Preamble.ts deleted file mode 100644 index eb13a5913678..000000000000 --- a/packages/types/src/extrinsic/v5/Preamble.ts +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2017-2024 @polkadot/types authors & contributors -// SPDX-License-Identifier: Apache-2.0 - -import type { ITuple, Registry } from "@polkadot/types-codec/types"; -import type { Extension, ExtensionVersion, ExtrinsicSignatureV4, ExtrinsicVersion, MultiAddress, Preamble } from "@polkadot/types/interfaces"; - -export class GenericPreamble implements Preamble { - #preambleType: 'Bare' | 'Signed' | 'General'; - #asBare?: ExtrinsicVersion; - #asGeneral?: ITuple<[ExtensionVersion, Extension]>; - #asSigned?: ITuple<[MultiAddress, ExtrinsicSignatureV4, ExtensionVersion, Extension, ExtrinsicVersion]>; - - constructor(registry: Registry, type: 'Bare' | 'Signed' | 'General', data: any) { - this.#preambleType = type; - - if (type === 'Bare') { - this.#asBare = registry.createTypeUnsafe('u8', data); - } else if (type === 'Signed') { - this.#asSigned = registry.createTypeUnsafe>('(MultiAddress, ExtrinsicSignatureV4, ExtensionVersion, Extension, ExtrinsicVersion)', data); - } else { - this.#asGeneral = registry.createTypeUnsafe>('(ExtensionVersion, Extension)', data) - } - } - - get isBare(): boolean { - return this.#preambleType === 'Bare'; - } - - get asBare(): ExtrinsicVersion { - if (!this.isBare) { - throw new Error("Not a Bare type"); - } - return this.#asBare!; - } - - get isSigned(): boolean { - return this.#preambleType === 'Signed'; - } - - get asSigned(): ITuple<[MultiAddress, ExtrinsicSignatureV4, ExtensionVersion, Extension, ExtrinsicVersion]> { - if (!this.isSigned) { - throw new Error("Not a Signed type"); - } - return this.#asSigned!; - } - - get isGeneral(): boolean { - return this.#preambleType === 'General'; - } - - get asGeneral(): ITuple<[ExtensionVersion, Extension]> { - if (!this.isGeneral) { - throw new Error("Not a General type"); - } - return this.#asGeneral!; - } - - get type(): 'Bare' | 'Signed' | 'General' { - return this.#preambleType; - } - - -} \ No newline at end of file diff --git a/packages/types/src/interfaces/extrinsics/definitions.ts b/packages/types/src/interfaces/extrinsics/definitions.ts index a9c06d1c83f1..a0c78d17492f 100644 --- a/packages/types/src/interfaces/extrinsics/definitions.ts +++ b/packages/types/src/interfaces/extrinsics/definitions.ts @@ -40,21 +40,6 @@ export default { SignerPayload: 'GenericSignerPayload', EcdsaSignature: '[u8; 65]', Ed25519Signature: 'H512', - Sr25519Signature: 'H512', - ExtrinsicVersion: 'u8', - ExtensionVersion: 'u8', - TransactionExtension: { - identifier: 'Text', - type: 'SiLookupTypeId', - Implicit: 'SiLookupTypeId' - }, - Extension: 'TransactionExtension', - Preamble: { - _enum: { - Bare: '(ExtrinsicVersion)', - Signed: '(MultiAddress, GenericExtrinsicSignatureV4, ExtensionVersion, Extension, ExtrinsicVersion)', - General: '(ExtensionVersion, Extension)', - } - } + Sr25519Signature: 'H512' } } as Definitions; diff --git a/packages/types/src/interfaces/extrinsics/types.ts b/packages/types/src/interfaces/extrinsics/types.ts index 6b435e2e8df1..056443f08841 100644 --- a/packages/types/src/interfaces/extrinsics/types.ts +++ b/packages/types/src/interfaces/extrinsics/types.ts @@ -2,10 +2,8 @@ /* eslint-disable */ import type { GenericExtrinsic, GenericExtrinsicEra, GenericExtrinsicPayload, GenericExtrinsicPayloadUnknown, GenericExtrinsicPayloadV4, GenericExtrinsicPayloadV5, GenericExtrinsicSignatureV4, GenericExtrinsicSignatureV5, GenericExtrinsicUnknown, GenericExtrinsicV4, GenericExtrinsicV5, GenericImmortalEra, GenericMortalEra, GenericSignerPayload } from '@polkadot/types'; -import type { Enum, Struct, Text, U8aFixed, u8 } from '@polkadot/types-codec'; -import type { ITuple } from '@polkadot/types-codec/types'; -import type { H512, MultiAddress } from '@polkadot/types/interfaces/runtime'; -import type { SiLookupTypeId } from '@polkadot/types/interfaces/scaleInfo'; +import type { Enum, U8aFixed } from '@polkadot/types-codec'; +import type { H512 } from '@polkadot/types/interfaces/runtime'; /** @name AnySignature */ export interface AnySignature extends H512 {} @@ -19,12 +17,6 @@ export interface Ed25519Signature extends H512 {} /** @name Era */ export interface Era extends ExtrinsicEra {} -/** @name Extension */ -export interface Extension extends TransactionExtension {} - -/** @name ExtensionVersion */ -export interface ExtensionVersion extends u8 {} - /** @name Extrinsic */ export interface Extrinsic extends GenericExtrinsic {} @@ -61,9 +53,6 @@ export interface ExtrinsicV4 extends GenericExtrinsicV4 {} /** @name ExtrinsicV5 */ export interface ExtrinsicV5 extends GenericExtrinsicV5 {} -/** @name ExtrinsicVersion */ -export interface ExtrinsicVersion extends u8 {} - /** @name ImmortalEra */ export interface ImmortalEra extends GenericImmortalEra {} @@ -81,17 +70,6 @@ export interface MultiSignature extends Enum { readonly type: 'Ed25519' | 'Sr25519' | 'Ecdsa'; } -/** @name Preamble */ -export interface Preamble extends Enum { - readonly isBare: boolean; - readonly asBare: ExtrinsicVersion; - readonly isSigned: boolean; - readonly asSigned: ITuple<[MultiAddress, GenericExtrinsicSignatureV4, ExtensionVersion, Extension, ExtrinsicVersion]>; - readonly isGeneral: boolean; - readonly asGeneral: ITuple<[ExtensionVersion, Extension]>; - readonly type: 'Bare' | 'Signed' | 'General'; -} - /** @name Signature */ export interface Signature extends H512 {} @@ -101,11 +79,4 @@ export interface SignerPayload extends GenericSignerPayload {} /** @name Sr25519Signature */ export interface Sr25519Signature extends H512 {} -/** @name TransactionExtension */ -export interface TransactionExtension extends Struct { - readonly identifier: Text; - readonly type: SiLookupTypeId; - readonly Implicit: SiLookupTypeId; -} - export type PHANTOM_EXTRINSICS = 'extrinsics'; diff --git a/packages/types/src/types/extrinsic.ts b/packages/types/src/types/extrinsic.ts index 2845adaa92a5..ed0546aef045 100644 --- a/packages/types/src/types/extrinsic.ts +++ b/packages/types/src/types/extrinsic.ts @@ -4,7 +4,7 @@ import type { AnyJson, AnyNumber, AnyTuple, AnyU8a, Codec } from '@polkadot/types-codec/types'; import type { HexString } from '@polkadot/util/types'; import type { ExtrinsicStatus } from '../interfaces/author/index.js'; -import type { EcdsaSignature, Ed25519Signature, Preamble, Sr25519Signature } from '../interfaces/extrinsics/index.js'; +import type { EcdsaSignature, Ed25519Signature, Sr25519Signature } from '../interfaces/extrinsics/index.js'; import type { Address, Call, H256, Hash } from '../interfaces/runtime/index.js'; import type { DispatchError, DispatchInfo, EventRecord } from '../interfaces/system/index.js'; import type { ICompact, IKeyringPair, IMethod, INumber, IRuntimeVersionBase } from './interfaces.js'; @@ -249,7 +249,7 @@ export interface IExtrinsicImpl extends IExtrinsicSignable { export interface IExtrinsicV5Impl extends IExtrinsicSignable { readonly method: Call; - readonly preamble: Preamble; + readonly signature: IExtrinsicSignature; readonly version: number; } From 4667ebd4863c7839ac1cd04334d9667737b44d15 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Tue, 24 Sep 2024 14:10:59 -0400 Subject: [PATCH 32/71] Added test for decoding --- packages/types/src/extrinsic/Extrinsic.spec.ts | 14 ++++++++++++++ packages/types/src/extrinsic/Extrinsic.ts | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/packages/types/src/extrinsic/Extrinsic.spec.ts b/packages/types/src/extrinsic/Extrinsic.spec.ts index b76d97d8a208..09f2b216d2ad 100644 --- a/packages/types/src/extrinsic/Extrinsic.spec.ts +++ b/packages/types/src/extrinsic/Extrinsic.spec.ts @@ -48,4 +48,18 @@ describe('Extrinsic', (): void => { expect(extrinsic.toPrimitive()).toEqual({ method: { args: { dest: { id: '5DiuK2zR4asj2CEh77SKtUgTswTLkD8eiAKrByg5G3wL5w9b' }, value: 104560923320000 }, callIndex: '0x0600' }, signature: { era: { mortalEra: [1024, 186] }, nonce: 68, signature: { ed25519: '0xd99ffe3e610ad234e1414bda5831395a6df9098bf80b01561ce89a5065ae89d5c10e1619c6c99131b0bea4fb73ef04d07c07770e2ae9df5c325c331769ccb300' }, signer: { id: '5Hn8KKEp8qruCGWaN9MEsjTs4FXB4wv9xn7g1RWkNeKKNXCr' }, tip: 30000000000 } }); }); }); + + describe('V5', () => { + it('Signed Extrinsic', () => { + registry.setSignedExtensions(fallbackExtensions); + + const extrinsic = new Extrinsic( + registry, + '0x51028500d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01fe54549be20bf992bc41433b698e2efd2196d54d1192ce851592369811c023079e6e1c334d546549e7cc0e71f5e3982a42ea832727ffdaca62af7f1a20fc428a00a500000000000603008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480b00a0724e1809', + { preamble: 'signed', version: 5 } + ); + + expect(extrinsic.signer.toHuman()).toEqual('HNZata7iMYWmk5RvZRTiAsSDhV8366zq2YGb3tLH5Upf74F'); + }); + }); }); diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 9e886694cff3..3c3e37a4ac7c 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -92,6 +92,10 @@ function decodeU8a (registry: Registry, value: Uint8Array, version: number, prea const data = value.subarray(offset, total); + if (version === 5) { + return newFromValue(registry, data.subarray(2), data[0], preamble); + } + return newFromValue(registry, data.subarray(1), data[0], preamble); } From a10fa71db79f40827f06d7a4387aecf10c78abba Mon Sep 17 00:00:00 2001 From: tarikgul Date: Tue, 24 Sep 2024 15:58:36 -0400 Subject: [PATCH 33/71] Fix test --- packages/types/src/extrinsic/Extrinsic.spec.ts | 4 ++-- packages/types/src/extrinsic/Extrinsic.ts | 4 ---- packages/types/src/extrinsic/v5/Extrinsic.ts | 3 ++- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/types/src/extrinsic/Extrinsic.spec.ts b/packages/types/src/extrinsic/Extrinsic.spec.ts index 09f2b216d2ad..d3c31377a56c 100644 --- a/packages/types/src/extrinsic/Extrinsic.spec.ts +++ b/packages/types/src/extrinsic/Extrinsic.spec.ts @@ -55,11 +55,11 @@ describe('Extrinsic', (): void => { const extrinsic = new Extrinsic( registry, - '0x51028500d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01fe54549be20bf992bc41433b698e2efd2196d54d1192ce851592369811c023079e6e1c334d546549e7cc0e71f5e3982a42ea832727ffdaca62af7f1a20fc428a00a500000000000603008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480b00a0724e1809', + '0x51028500d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d011e0b7d9438899333c50121f8e10144952d51c3bb8d0ea11dd1f24940d8ff615ad351d95ed9f41f078748ed7cf182864a20b38eebfaef6629433365eb90c0148c007502000000000603008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480b00a0724e1809', { preamble: 'signed', version: 5 } ); - expect(extrinsic.signer.toHuman()).toEqual('HNZata7iMYWmk5RvZRTiAsSDhV8366zq2YGb3tLH5Upf74F'); + expect(extrinsic.signer.toString()).toEqual('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'); }); }); }); diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 3c3e37a4ac7c..9e886694cff3 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -92,10 +92,6 @@ function decodeU8a (registry: Registry, value: Uint8Array, version: number, prea const data = value.subarray(offset, total); - if (version === 5) { - return newFromValue(registry, data.subarray(2), data[0], preamble); - } - return newFromValue(registry, data.subarray(1), data[0], preamble); } diff --git a/packages/types/src/extrinsic/v5/Extrinsic.ts b/packages/types/src/extrinsic/v5/Extrinsic.ts index a1fd4f2ca9ce..5d7d35491c78 100644 --- a/packages/types/src/extrinsic/v5/Extrinsic.ts +++ b/packages/types/src/extrinsic/v5/Extrinsic.ts @@ -40,7 +40,8 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { } else if (isU8a(value)) { // here we decode manually since we need to pull through the version information const signature = registry.createTypeUnsafe('ExtrinsicSignatureV5', [value, { isSigned }]); - const method = registry.createTypeUnsafe('Call', [value.subarray(signature.encodedLength)]); + // We add 2 here since the length of the TransactionExtension Version needs to be accounted for + const method = registry.createTypeUnsafe('Call', [value.subarray(signature.encodedLength + 2)]); return { method, From 0f9329872410c5e98fff496cd06492336bd60720 Mon Sep 17 00:00:00 2001 From: bee344 Date: Tue, 24 Sep 2024 18:18:36 -0300 Subject: [PATCH 34/71] preamble typo --- packages/types/src/extrinsic/ExtrinsicPayload.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/types/src/extrinsic/ExtrinsicPayload.ts b/packages/types/src/extrinsic/ExtrinsicPayload.ts index 4b90787f0e5f..a088caddadba 100644 --- a/packages/types/src/extrinsic/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/ExtrinsicPayload.ts @@ -18,7 +18,7 @@ import { DEFAULT_PREAMBLE, LATEST_EXTRINSIC_VERSION } from './constants.js'; interface ExtrinsicPayloadOptions { version?: number; - pramble?: Preamble; + preamble?: Preamble; } // all our known types that can be returned @@ -76,8 +76,8 @@ function decodeExtrinsicPayload (registry: Registry, value?: GenericExtrinsicPay * on the contents included */ export class GenericExtrinsicPayload extends AbstractBase { - constructor (registry: Registry, value?: Partial | Uint8Array | string, { pramble, version }: ExtrinsicPayloadOptions = {}) { - super(registry, decodeExtrinsicPayload(registry, value as ExtrinsicPayloadValue, version, pramble)); + constructor (registry: Registry, value?: Partial | Uint8Array | string, { preamble, version }: ExtrinsicPayloadOptions = {}) { + super(registry, decodeExtrinsicPayload(registry, value as ExtrinsicPayloadValue, version, preamble)); } /** From d6ac60bb4f3404b5d4fd247af03cbc22c6aad21d Mon Sep 17 00:00:00 2001 From: bee344 Date: Tue, 24 Sep 2024 18:27:07 -0300 Subject: [PATCH 35/71] updated GeneralExtrinsic [WIP] --- packages/types/src/extrinsic/Extrinsic.ts | 2 +- .../types/src/extrinsic/v5/Extrinsic.spec.ts | 2 +- .../src/extrinsic/v5/GeneralExtrinsic.spec.ts | 68 +++++++++++++++++++ .../src/extrinsic/v5/GeneralExtrinsic.ts | 67 ++++++++++++++++++ packages/types/src/extrinsic/v5/index.ts | 1 + 5 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 packages/types/src/extrinsic/v5/GeneralExtrinsic.spec.ts create mode 100644 packages/types/src/extrinsic/v5/GeneralExtrinsic.ts diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 9e886694cff3..565552bcb935 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -40,7 +40,7 @@ const VERSIONS = [ const PREAMBLE = { bare: 'ExtrinsicV5', // Not supported yet - general: 'ExtrinsicV5', + general: 'GeneralExtrinsic', signed: 'ExtrinsicV5' }; diff --git a/packages/types/src/extrinsic/v5/Extrinsic.spec.ts b/packages/types/src/extrinsic/v5/Extrinsic.spec.ts index fe65d33aced1..8239b4b1beb0 100644 --- a/packages/types/src/extrinsic/v5/Extrinsic.spec.ts +++ b/packages/types/src/extrinsic/v5/Extrinsic.spec.ts @@ -19,7 +19,7 @@ registry.setMetadata(metadata); const tx = decorateExtrinsics(registry, metadata.asLatest, metadata.version); -describe('ExtrinsicV4', (): void => { +describe('ExtrinsicV5', (): void => { it('constructs a sane Uint8Array (default)', (): void => { const xt = new Extrinsic(registry); diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsic.spec.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsic.spec.ts new file mode 100644 index 000000000000..601bb88a72dd --- /dev/null +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsic.spec.ts @@ -0,0 +1,68 @@ +// Copyright 2017-2024 @polkadot/types authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +/// + +import rpcMetadata from '@polkadot/types-support/metadata/static-substrate'; + +import { TypeRegistry } from '../../create/index.js'; +import { decorateExtrinsics, Metadata } from '../../metadata/index.js'; +import { GeneralExtrinsic } from './GeneralExtrinsic.js'; +import { createTestPairs } from '@polkadot/keyring'; +import { fallbackExtensions } from '../signedExtensions/index.js'; + +const registry = new TypeRegistry(); +const metadata = new Metadata(registry, rpcMetadata); +const keyring = createTestPairs({ type: 'ed25519' }, false); + +registry.setMetadata(metadata); + +const tx = decorateExtrinsics(registry, metadata.asLatest, metadata.version); + + +describe('GeneralExtrinsic', (): void => { + it('has a sane inspect', (): void => { + // we don't expect this to fail, however it is actually a good + // reference for the ordering in base Substrate + expect(new GeneralExtrinsic(registry, { method: tx['timestamp']['set'](0).toHex() } as never).inspect()).toEqual({ + inner: [ + { name: 'transactionExtensionVersion', outer: [new Uint8Array(1)] }, + { inner: undefined, name: 'era', outer: [new Uint8Array([0]), new Uint8Array([0])] }, + { name: 'nonce', outer: [new Uint8Array([0])] }, + { name: 'tip', outer: [new Uint8Array([0])] }, + { name: 'assetId', outer: [new Uint8Array([0])] }, + { name: 'mode', outer: [new Uint8Array([0])] }, + ] + }); + }); + + it('creates a general extrinsic', (): void => { + registry.setSignedExtensions(fallbackExtensions); + const ext = new GeneralExtrinsic( + registry, + tx['balances']['transferAllowDeath'](keyring.bob.publicKey, 6969n) + ); + + expect( + ext.toHex() + ).toEqual( + '0x' + + '0600' + // balance.transferAllowDeath + '00' + + 'd7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9' + + 'e56c' + ); + + expect(ext.toHuman()).toEqual({ + args: { + dest: { + Id: '5Gw3s7q4QLkSWwknsiPtjujPv3XM4Trxi5d4PgKMMk3gfGqm' + }, + value: '6,969' + }, + callIndex: '0x0600' + } + ) + }); +}); + diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts new file mode 100644 index 000000000000..a0e7a0193457 --- /dev/null +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts @@ -0,0 +1,67 @@ +import { Struct } from "@polkadot/types-codec"; +import { objectProperties, objectSpread } from "@polkadot/util"; + +import type { AnyU8a, ICompact, INumber, IOption, Registry } from "@polkadot/types-codec/types"; +import type { Call, MultiLocation } from "@polkadot/types/interfaces"; +import { EMPTY_U8A } from "../constants.js"; +import type { GenericExtrinsicEra } from "../ExtrinsicEra.js"; + +export interface GeneralExtrinsicValueV5 { + method?: Call; +} + +export class GeneralExtrinsic extends Struct { + + readonly #transactionExtensionVersion: number; + readonly #signKeys: string[]; + + constructor(registry: Registry, value?: AnyU8a | Call | Uint8Array) { + const signTypes = registry.getSignedExtensionTypes(); + const signedVersion = registry.getTransactionExtensionVersion(); + + super( + registry, + objectSpread( + // eslint-disable-next-line sort-keys + { transactionExtensionVersion: 'u8' }, + signTypes + ), + GeneralExtrinsic.decodeGeneralExtrinsic(value) + ); + + this.#transactionExtensionVersion = signedVersion; + this.#signKeys = Object.keys(signTypes); + + objectProperties(this, this.#signKeys, (k) => this.get(k)); + } + + public static decodeGeneralExtrinsic(value?: AnyU8a | Call | Uint8Array): GeneralExtrinsic | Uint8Array | Call | AnyU8a { + if (!value) { + return EMPTY_U8A + } else if (value instanceof GeneralExtrinsic) { + return value; + } + return value + } + + public get era(): GenericExtrinsicEra { + return this.getT('era') + } + + public get nonce(): ICompact { + return this.getT('nonce') + } + + public get tip(): ICompact { + return this.getT('tip') + } + + public get assetId(): IOption { + return this.getT('assetId') + } + + public get mode(): INumber { + return this.getT('mode') + } + +} \ No newline at end of file diff --git a/packages/types/src/extrinsic/v5/index.ts b/packages/types/src/extrinsic/v5/index.ts index d99511d9a93c..fe840d68c5cc 100644 --- a/packages/types/src/extrinsic/v5/index.ts +++ b/packages/types/src/extrinsic/v5/index.ts @@ -4,3 +4,4 @@ export { GenericExtrinsicV5 } from './Extrinsic.js'; export { GenericExtrinsicPayloadV5 } from './ExtrinsicPayload.js'; export { GenericExtrinsicSignatureV5 } from './ExtrinsicSignature.js'; +export { GeneralExtrinsic } from './GeneralExtrinsic.js'; \ No newline at end of file From bac50e850a23b91f53b3fdb2d3f11ee6b76fbb33 Mon Sep 17 00:00:00 2001 From: bee344 Date: Tue, 24 Sep 2024 22:40:55 -0300 Subject: [PATCH 36/71] added generalExtrinsic --- .../src/extrinsic/v5/GeneralExtrinsic.spec.ts | 76 ++++++------ .../src/extrinsic/v5/GeneralExtrinsic.ts | 106 ++++++++-------- .../extrinsic/v5/GeneralExtrinsicPayload.ts | 115 ++++++++++++++++++ packages/types/src/extrinsic/v5/index.ts | 2 +- 4 files changed, 211 insertions(+), 88 deletions(-) create mode 100644 packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.ts diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsic.spec.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsic.spec.ts index 601bb88a72dd..62b37c01301e 100644 --- a/packages/types/src/extrinsic/v5/GeneralExtrinsic.spec.ts +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsic.spec.ts @@ -3,13 +3,13 @@ /// +import { createTestPairs } from '@polkadot/keyring'; import rpcMetadata from '@polkadot/types-support/metadata/static-substrate'; import { TypeRegistry } from '../../create/index.js'; import { decorateExtrinsics, Metadata } from '../../metadata/index.js'; -import { GeneralExtrinsic } from './GeneralExtrinsic.js'; -import { createTestPairs } from '@polkadot/keyring'; import { fallbackExtensions } from '../signedExtensions/index.js'; +import { GeneralExtrinsic } from './GeneralExtrinsic.js'; const registry = new TypeRegistry(); const metadata = new Metadata(registry, rpcMetadata); @@ -19,50 +19,48 @@ registry.setMetadata(metadata); const tx = decorateExtrinsics(registry, metadata.asLatest, metadata.version); - describe('GeneralExtrinsic', (): void => { - it('has a sane inspect', (): void => { - // we don't expect this to fail, however it is actually a good - // reference for the ordering in base Substrate - expect(new GeneralExtrinsic(registry, { method: tx['timestamp']['set'](0).toHex() } as never).inspect()).toEqual({ - inner: [ - { name: 'transactionExtensionVersion', outer: [new Uint8Array(1)] }, - { inner: undefined, name: 'era', outer: [new Uint8Array([0]), new Uint8Array([0])] }, - { name: 'nonce', outer: [new Uint8Array([0])] }, - { name: 'tip', outer: [new Uint8Array([0])] }, - { name: 'assetId', outer: [new Uint8Array([0])] }, - { name: 'mode', outer: [new Uint8Array([0])] }, - ] - }); + it('has a sane inspect', (): void => { + // we don't expect this to fail, however it is actually a good + // reference for the ordering in base Substrate + expect(new GeneralExtrinsic(registry, { method: tx['timestamp']['set'](0).toHex() } as never).inspect()).toEqual({ + inner: [ + { name: 'transactionExtensionVersion', outer: [new Uint8Array(1)] }, + { inner: undefined, name: 'era', outer: [new Uint8Array([0]), new Uint8Array([0])] }, + { name: 'nonce', outer: [new Uint8Array([0])] }, + { name: 'tip', outer: [new Uint8Array([0])] }, + { name: 'assetId', outer: [new Uint8Array([0])] }, + { name: 'mode', outer: [new Uint8Array([0])] } + ] }); + }); - it('creates a general extrinsic', (): void => { - registry.setSignedExtensions(fallbackExtensions); - const ext = new GeneralExtrinsic( - registry, - tx['balances']['transferAllowDeath'](keyring.bob.publicKey, 6969n) - ); + it('creates a general extrinsic', (): void => { + registry.setSignedExtensions(fallbackExtensions); + const ext = new GeneralExtrinsic( + registry, + tx['balances']['transferAllowDeath'](keyring.bob.publicKey, 6969n) + ); - expect( - ext.toHex() - ).toEqual( - '0x' + + expect( + ext.toHex() + ).toEqual( + '0x' + '0600' + // balance.transferAllowDeath '00' + 'd7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9' + 'e56c' - ); + ); - expect(ext.toHuman()).toEqual({ - args: { - dest: { - Id: '5Gw3s7q4QLkSWwknsiPtjujPv3XM4Trxi5d4PgKMMk3gfGqm' - }, - value: '6,969' - }, - callIndex: '0x0600' - } - ) - }); + expect(ext.toHuman()).toEqual({ + args: { + dest: { + Id: '5Gw3s7q4QLkSWwknsiPtjujPv3XM4Trxi5d4PgKMMk3gfGqm' + }, + value: '6,969' + }, + callIndex: '0x0600' + } + ); + }); }); - diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts index a0e7a0193457..951daf79ba15 100644 --- a/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts @@ -1,67 +1,77 @@ -import { Struct } from "@polkadot/types-codec"; -import { objectProperties, objectSpread } from "@polkadot/util"; +// Copyright 2017-2024 @polkadot/types authors & contributors +// SPDX-License-Identifier: Apache-2.0 -import type { AnyU8a, ICompact, INumber, IOption, Registry } from "@polkadot/types-codec/types"; -import type { Call, MultiLocation } from "@polkadot/types/interfaces"; -import { EMPTY_U8A } from "../constants.js"; -import type { GenericExtrinsicEra } from "../ExtrinsicEra.js"; +import type { Call, MultiLocation } from '@polkadot/types/interfaces'; +import type { ICompact, INumber, IOption, Registry } from '@polkadot/types-codec/types'; +import type { GenericExtrinsicEra } from '../ExtrinsicEra.js'; + +import { Struct } from '@polkadot/types-codec'; +import { objectProperties, objectSpread } from '@polkadot/util'; + +import { EMPTY_U8A } from '../constants.js'; export interface GeneralExtrinsicValueV5 { - method?: Call; + method?: Call; } export class GeneralExtrinsic extends Struct { + readonly #transactionExtensionVersion: number; + readonly #signKeys: string[]; - readonly #transactionExtensionVersion: number; - readonly #signKeys: string[]; + constructor (registry: Registry, value?: Call | Uint8Array) { + const signTypes = registry.getSignedExtensionTypes(); + const signedVersion = registry.getTransactionExtensionVersion(); - constructor(registry: Registry, value?: AnyU8a | Call | Uint8Array) { - const signTypes = registry.getSignedExtensionTypes(); - const signedVersion = registry.getTransactionExtensionVersion(); + super( + registry, + objectSpread( + // eslint-disable-next-line sort-keys + { transactionExtensionVersion: 'u8' }, + signTypes + ), + GeneralExtrinsic.decodeGeneralExtrinsic(value) + ); - super( - registry, - objectSpread( - // eslint-disable-next-line sort-keys - { transactionExtensionVersion: 'u8' }, - signTypes - ), - GeneralExtrinsic.decodeGeneralExtrinsic(value) - ); + this.#transactionExtensionVersion = signedVersion; + this.#signKeys = Object.keys(signTypes); - this.#transactionExtensionVersion = signedVersion; - this.#signKeys = Object.keys(signTypes); + objectProperties(this, this.#signKeys, (k) => this.get(k)); + } - objectProperties(this, this.#signKeys, (k) => this.get(k)); + public static decodeGeneralExtrinsic (value?: Call | Uint8Array): GeneralExtrinsic | Uint8Array | Call { + if (!value) { + return EMPTY_U8A; + } else if (value instanceof GeneralExtrinsic) { + return value; } - public static decodeGeneralExtrinsic(value?: AnyU8a | Call | Uint8Array): GeneralExtrinsic | Uint8Array | Call | AnyU8a { - if (!value) { - return EMPTY_U8A - } else if (value instanceof GeneralExtrinsic) { - return value; - } - return value - } + return value; + } - public get era(): GenericExtrinsicEra { - return this.getT('era') - } + public get era (): GenericExtrinsicEra { + return this.getT('era'); + } - public get nonce(): ICompact { - return this.getT('nonce') - } + public get nonce (): ICompact { + return this.getT('nonce'); + } - public get tip(): ICompact { - return this.getT('tip') - } + public get tip (): ICompact { + return this.getT('tip'); + } - public get assetId(): IOption { - return this.getT('assetId') - } + public get assetId (): IOption { + return this.getT('assetId'); + } - public get mode(): INumber { - return this.getT('mode') - } + public get mode (): INumber { + return this.getT('mode'); + } -} \ No newline at end of file + /** + * @description The [[u8]] for the TransactionExtension version + */ + public get transactionExtensionVersion (): INumber { + return this.getT('transactionExtensionVersion'); + } +} diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.ts new file mode 100644 index 000000000000..3a8e98dad0fb --- /dev/null +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.ts @@ -0,0 +1,115 @@ +// Copyright 2017-2024 @polkadot/types authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { Hash, MultiLocation } from '@polkadot/types/interfaces'; +import type { Bytes } from '@polkadot/types-codec'; +import type { Inspect, Registry } from '@polkadot/types-codec/types'; +import type { HexString } from '@polkadot/util/types'; +import type { BlockHash } from '../../interfaces/chain/index.js'; +import type { ExtrinsicEra } from '../../interfaces/extrinsics/index.js'; +import type { ExtrinsicPayloadValue, ICompact, INumber, IOption } from '../../types/index.js'; + +import { Struct } from '@polkadot/types-codec'; +import { objectSpread } from '@polkadot/util'; + +/** + * @name GeneralExtrinsicPayload + * @description + * A signing payload for an [[Extrinsic]]. For the final encoding, it is + * variable length based on the contents included + */ +export class GeneralExtrinsicPayload extends Struct { + readonly #transactionExtensionVersion: number; + readonly #signKeys: string[]; + + constructor (registry: Registry, value?: ExtrinsicPayloadValue | Uint8Array | HexString) { + const signTypes = registry.getSignedExtensionTypes(); + const signedVersion = registry.getTransactionExtensionVersion(); + + super(registry, objectSpread( + { method: 'Bytes' }, + registry.getSignedExtensionTypes(), + registry.getSignedExtensionExtra() + ), value); + + this.#transactionExtensionVersion = signedVersion; + this.#signKeys = Object.keys(signTypes); + } + + /** + * @description Returns a breakdown of the hex encoding for this Codec + */ + public override inspect (): Inspect { + return super.inspect({ method: true }); + } + + /** + * @description The block [[BlockHash]] the signature applies to (mortal/immortal) + */ + public get blockHash (): BlockHash { + return this.getT('blockHash'); + } + + /** + * @description The [[ExtrinsicEra]] + */ + public get era (): ExtrinsicEra { + return this.getT('era'); + } + + /** + * @description The genesis [[BlockHash]] the signature applies to (mortal/immortal) + */ + public get genesisHash (): BlockHash { + return this.getT('genesisHash'); + } + + /** + * @description The [[Bytes]] contained in the payload + */ + public get method (): Bytes { + return this.getT('method'); + } + + /** + * @description The [[Index]] + */ + public get nonce (): ICompact { + return this.getT('nonce'); + } + + /** + * @description The specVersion for this signature + */ + public get specVersion (): INumber { + return this.getT('specVersion'); + } + + /** + * @description The tip [[Balance]] + */ + public get tip (): ICompact { + return this.getT('tip'); + } + + /** + * @description The transactionVersion for this signature + */ + public get transactionVersion (): INumber { + return this.getT('transactionVersion'); + } + + /** + * @description The (optional) asset id for this signature for chains that support transaction fees in assets + */ + public get assetId (): IOption { + return this.getT('assetId'); + } + + /** + * @description The (optional) asset id for this signature for chains that support transaction fees in assets + */ + public get metadataHash (): IOption { + return this.getT('metadataHash'); + } +} diff --git a/packages/types/src/extrinsic/v5/index.ts b/packages/types/src/extrinsic/v5/index.ts index fe840d68c5cc..63d44b65c146 100644 --- a/packages/types/src/extrinsic/v5/index.ts +++ b/packages/types/src/extrinsic/v5/index.ts @@ -4,4 +4,4 @@ export { GenericExtrinsicV5 } from './Extrinsic.js'; export { GenericExtrinsicPayloadV5 } from './ExtrinsicPayload.js'; export { GenericExtrinsicSignatureV5 } from './ExtrinsicSignature.js'; -export { GeneralExtrinsic } from './GeneralExtrinsic.js'; \ No newline at end of file +export { GeneralExtrinsic } from './GeneralExtrinsic.js'; From e2592e996ec3266c94e8b10a8154a3eeebad571f Mon Sep 17 00:00:00 2001 From: bee344 Date: Fri, 27 Sep 2024 13:44:38 -0300 Subject: [PATCH 37/71] General Extrinsic --- packages/types/src/extrinsic/util.ts | 6 + .../src/extrinsic/v5/GeneralExtrinsic.spec.ts | 66 ---------- .../src/extrinsic/v5/GeneralExtrinsic.ts | 84 ++++++------- .../extrinsic/v5/GeneralExtrinsicEncoded.ts | 118 ++++++++++++++++++ .../extrinsic/v5/GeneralExtrinsicPayload.ts | 74 ++++++----- 5 files changed, 201 insertions(+), 147 deletions(-) delete mode 100644 packages/types/src/extrinsic/v5/GeneralExtrinsic.spec.ts create mode 100644 packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.ts diff --git a/packages/types/src/extrinsic/util.ts b/packages/types/src/extrinsic/util.ts index 3a1fbd323f18..476e8404f62f 100644 --- a/packages/types/src/extrinsic/util.ts +++ b/packages/types/src/extrinsic/util.ts @@ -20,3 +20,9 @@ export function signV5 (registry: Registry, signerPair: IKeyringPair, u8a: Uint8 return signerPair.sign(encoded, options); } + +export function signGeneral (registry: Registry, u8a: Uint8Array): Uint8Array { + const encoded = registry.hash(u8a); + + return encoded; +} diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsic.spec.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsic.spec.ts deleted file mode 100644 index 62b37c01301e..000000000000 --- a/packages/types/src/extrinsic/v5/GeneralExtrinsic.spec.ts +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2017-2024 @polkadot/types authors & contributors -// SPDX-License-Identifier: Apache-2.0 - -/// - -import { createTestPairs } from '@polkadot/keyring'; -import rpcMetadata from '@polkadot/types-support/metadata/static-substrate'; - -import { TypeRegistry } from '../../create/index.js'; -import { decorateExtrinsics, Metadata } from '../../metadata/index.js'; -import { fallbackExtensions } from '../signedExtensions/index.js'; -import { GeneralExtrinsic } from './GeneralExtrinsic.js'; - -const registry = new TypeRegistry(); -const metadata = new Metadata(registry, rpcMetadata); -const keyring = createTestPairs({ type: 'ed25519' }, false); - -registry.setMetadata(metadata); - -const tx = decorateExtrinsics(registry, metadata.asLatest, metadata.version); - -describe('GeneralExtrinsic', (): void => { - it('has a sane inspect', (): void => { - // we don't expect this to fail, however it is actually a good - // reference for the ordering in base Substrate - expect(new GeneralExtrinsic(registry, { method: tx['timestamp']['set'](0).toHex() } as never).inspect()).toEqual({ - inner: [ - { name: 'transactionExtensionVersion', outer: [new Uint8Array(1)] }, - { inner: undefined, name: 'era', outer: [new Uint8Array([0]), new Uint8Array([0])] }, - { name: 'nonce', outer: [new Uint8Array([0])] }, - { name: 'tip', outer: [new Uint8Array([0])] }, - { name: 'assetId', outer: [new Uint8Array([0])] }, - { name: 'mode', outer: [new Uint8Array([0])] } - ] - }); - }); - - it('creates a general extrinsic', (): void => { - registry.setSignedExtensions(fallbackExtensions); - const ext = new GeneralExtrinsic( - registry, - tx['balances']['transferAllowDeath'](keyring.bob.publicKey, 6969n) - ); - - expect( - ext.toHex() - ).toEqual( - '0x' + - '0600' + // balance.transferAllowDeath - '00' + - 'd7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9' + - 'e56c' - ); - - expect(ext.toHuman()).toEqual({ - args: { - dest: { - Id: '5Gw3s7q4QLkSWwknsiPtjujPv3XM4Trxi5d4PgKMMk3gfGqm' - }, - value: '6,969' - }, - callIndex: '0x0600' - } - ); - }); -}); diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts index 951daf79ba15..93c72f84d04d 100644 --- a/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts @@ -1,77 +1,67 @@ // Copyright 2017-2024 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { Call, MultiLocation } from '@polkadot/types/interfaces'; -import type { ICompact, INumber, IOption, Registry } from '@polkadot/types-codec/types'; -import type { GenericExtrinsicEra } from '../ExtrinsicEra.js'; +import type { Call } from '../../interfaces/runtime/index.js'; +import type { Registry, SignatureOptions as EncodingOptions } from '../../types/index.js'; +import type { Preamble } from '../types.js'; +import type { GeneralExtrinsicEncoded } from './GeneralExtrinsicEncoded.js'; import { Struct } from '@polkadot/types-codec'; -import { objectProperties, objectSpread } from '@polkadot/util'; +import { isU8a } from '@polkadot/util'; -import { EMPTY_U8A } from '../constants.js'; +export const EXTRINSIC_VERSION = 5; -export interface GeneralExtrinsicValueV5 { +export interface GeneralExtrinsicValue { method?: Call; } export class GeneralExtrinsic extends Struct { - readonly #transactionExtensionVersion: number; - readonly #signKeys: string[]; - - constructor (registry: Registry, value?: Call | Uint8Array) { - const signTypes = registry.getSignedExtensionTypes(); - const signedVersion = registry.getTransactionExtensionVersion(); - - super( - registry, - objectSpread( - // eslint-disable-next-line sort-keys - { transactionExtensionVersion: 'u8' }, - signTypes - ), - GeneralExtrinsic.decodeGeneralExtrinsic(value) - ); - - this.#transactionExtensionVersion = signedVersion; - this.#signKeys = Object.keys(signTypes); - - objectProperties(this, this.#signKeys, (k) => this.get(k)); + constructor (registry: Registry, value?: Uint8Array | GeneralExtrinsicValue | Call) { + super(registry, { + method: 'Call' + }, GeneralExtrinsic.decodeExtrinsic(registry, value)); } - public static decodeGeneralExtrinsic (value?: Call | Uint8Array): GeneralExtrinsic | Uint8Array | Call { - if (!value) { - return EMPTY_U8A; - } else if (value instanceof GeneralExtrinsic) { + /** @internal */ + public static decodeExtrinsic (registry: Registry, value?: Call | Uint8Array | GeneralExtrinsicValue): GeneralExtrinsicValue { + if (value instanceof GeneralExtrinsic) { return value; + } else if (value instanceof registry.createClassUnsafe('Call')) { + return { method: value }; + } else if (isU8a(value)) { + const method = registry.createTypeUnsafe('Call', [value]); + + return { + method + }; } - return value; + return value || {}; } - public get era (): GenericExtrinsicEra { - return this.getT('era'); + public override get encodedLength (): number { + return this.toU8a().length; } - public get nonce (): ICompact { - return this.getT('nonce'); + public get method (): Call { + return this.getT('method'); } - public get tip (): ICompact { - return this.getT('tip'); + public get encoded (): GeneralExtrinsicEncoded { + return this.getT('encoded'); } - public get assetId (): IOption { - return this.getT('assetId'); + public get version (): number { + return EXTRINSIC_VERSION; } - public get mode (): INumber { - return this.getT('mode'); + public get preamble (): Preamble { + return this.getT('preamble'); } - /** - * @description The [[u8]] for the TransactionExtension version - */ - public get transactionExtensionVersion (): INumber { - return this.getT('transactionExtensionVersion'); + public encode (options: EncodingOptions): GeneralExtrinsic { + this.encoded.encode(this.method, options); + + return this; } } diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.ts new file mode 100644 index 000000000000..86ed445fca1b --- /dev/null +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.ts @@ -0,0 +1,118 @@ +// Copyright 2017-2024 @polkadot/types authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { MultiLocation } from '@polkadot/types/interfaces'; +import type { ExtrinsicEra } from '../../interfaces/extrinsics/index.js'; +import type { Call, Hash } from '../../interfaces/runtime/index.js'; +import type { ExtrinsicPayloadValue, ICompact, INumber, IOption, Registry, SignatureOptions as EncodingOptions } from '../../types/index.js'; + +import { Struct } from '@polkadot/types-codec'; +import { isUndefined, objectProperties, objectSpread } from '@polkadot/util'; + +import { EMPTY_U8A, IMMORTAL_ERA } from '../constants.js'; +import { GenericExtrinsicPayloadV5 } from './ExtrinsicPayload.js'; + +export class GeneralExtrinsicEncoded extends Struct { + #signKeys: string[]; + #transactionExtensionVersion: number; + + constructor (registry: Registry, value?: GeneralExtrinsicEncoded | Uint8Array) { + const signTypes = registry.getSignedExtensionTypes(); + const signedVersion = registry.getTransactionExtensionVersion(); + + super( + registry, + objectSpread( + { transactionExtensionVersion: 'u8' }, + signTypes + ), + GeneralExtrinsicEncoded.decodeExtrinsicSignature(value) + ); + + this.#transactionExtensionVersion = signedVersion; + this.#signKeys = Object.keys(signTypes); + + objectProperties(this, this.#signKeys, (k) => this.get(k)); + } + + /** @internal */ + public static decodeExtrinsicSignature (value?: GeneralExtrinsicEncoded | Uint8Array): GeneralExtrinsicEncoded | Uint8Array { + if (!value) { + return EMPTY_U8A; + } else if (value instanceof GeneralExtrinsicEncoded) { + return value; + } + + return value + } + + public override get encodedLength (): number { + return super.encodedLength; + } + + public get era (): ExtrinsicEra { + return this.getT('era'); + } + + public get nonce (): ICompact { + return this.getT('nonce'); + } + + public get tip (): ICompact { + return this.getT('tip'); + } + + public get assetId (): IOption { + return this.getT('assetId'); + } + + public get mode (): INumber { + return this.getT('mode'); + } + + public get metadataHash (): IOption { + return this.getT('metadataHash'); + } + + public get transactionExtensionVersion (): INumber { + return this.getT('transactionExtensionVersion'); + } + + protected _buildEncoded (payload: GenericExtrinsicPayloadV5) { + for (let i = 0, count = this.#signKeys.length; i < count; i++) { + const k = this.#signKeys[i]; + const v = payload.get(k); + + if (k === 'transactionExtensionVersion') { + this.set(k, this.registry.createType('u8', this.#transactionExtensionVersion)); + } else if (!isUndefined(v)) { + this.set(k, v); + } + } + + return this; + } + + public createPayload (method: Call, options: EncodingOptions): GenericExtrinsicPayloadV5 { + const { era, runtimeVersion: { specVersion, transactionVersion } } = options; + + return new GenericExtrinsicPayloadV5(this.registry, objectSpread({}, options, { + era: era || IMMORTAL_ERA, + method: method.toHex(), + specVersion, + transactionVersion + })); + } + + public encode (method: Call, options: EncodingOptions) { + const payload = this.createPayload(method, options); + + return this._buildEncoded( + payload + ); + } + + public override toU8a (isBare?: boolean): Uint8Array { + return super.toU8a(isBare); + } +} diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.ts index 3a8e98dad0fb..1c896171ae5a 100644 --- a/packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.ts @@ -12,104 +12,110 @@ import type { ExtrinsicPayloadValue, ICompact, INumber, IOption } from '../../ty import { Struct } from '@polkadot/types-codec'; import { objectSpread } from '@polkadot/util'; +import { signGeneral } from '../util.js'; + /** - * @name GeneralExtrinsicPayload + * @name GenericExtrinsicPayloadV5 * @description * A signing payload for an [[Extrinsic]]. For the final encoding, it is * variable length based on the contents included */ -export class GeneralExtrinsicPayload extends Struct { - readonly #transactionExtensionVersion: number; - readonly #signKeys: string[]; - +export class GenericExtrinsicPayloadV5 extends Struct { constructor (registry: Registry, value?: ExtrinsicPayloadValue | Uint8Array | HexString) { - const signTypes = registry.getSignedExtensionTypes(); - const signedVersion = registry.getTransactionExtensionVersion(); - super(registry, objectSpread( { method: 'Bytes' }, registry.getSignedExtensionTypes(), - registry.getSignedExtensionExtra() + registry.getSignedExtensionExtra(), + { transactionExtensionVersion: 'u8' } ), value); - - this.#transactionExtensionVersion = signedVersion; - this.#signKeys = Object.keys(signTypes); } /** - * @description Returns a breakdown of the hex encoding for this Codec - */ + * @description Returns a breakdown of the hex encoding for this Codec + */ public override inspect (): Inspect { return super.inspect({ method: true }); } /** - * @description The block [[BlockHash]] the signature applies to (mortal/immortal) - */ + * @description The block [[BlockHash]] the signature applies to (mortal/immortal) + */ public get blockHash (): BlockHash { return this.getT('blockHash'); } /** - * @description The [[ExtrinsicEra]] - */ + * @description The [[ExtrinsicEra]] + */ public get era (): ExtrinsicEra { return this.getT('era'); } /** - * @description The genesis [[BlockHash]] the signature applies to (mortal/immortal) - */ + * @description The genesis [[BlockHash]] the signature applies to (mortal/immortal) + */ public get genesisHash (): BlockHash { return this.getT('genesisHash'); } /** - * @description The [[Bytes]] contained in the payload - */ + * @description The [[Bytes]] contained in the payload + */ public get method (): Bytes { return this.getT('method'); } /** - * @description The [[Index]] - */ + * @description The [[Index]] + */ public get nonce (): ICompact { return this.getT('nonce'); } /** - * @description The specVersion for this signature - */ + * @description The specVersion for this signature + */ public get specVersion (): INumber { return this.getT('specVersion'); } /** - * @description The tip [[Balance]] - */ + * @description The tip [[Balance]] + */ public get tip (): ICompact { return this.getT('tip'); } /** - * @description The transactionVersion for this signature - */ + * @description The transactionVersion for this signature + */ public get transactionVersion (): INumber { return this.getT('transactionVersion'); } /** - * @description The (optional) asset id for this signature for chains that support transaction fees in assets - */ + * @description The (optional) asset id for this signature for chains that support transaction fees in assets + */ public get assetId (): IOption { return this.getT('assetId'); } /** - * @description The (optional) asset id for this signature for chains that support transaction fees in assets - */ + * @description The (optional) asset id for this signature for chains that support transaction fees in assets + */ public get metadataHash (): IOption { return this.getT('metadataHash'); } + + /** + * @description Sign the payload with the keypair + */ + public sign (): Uint8Array { + // NOTE The `toU8a({ method: 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..V3, if we have a V6, carry this comment + return signGeneral(this.registry, this.toU8a({ method: true })); + } } From d235092b971ec4cf7e8bfecf14fbed6103a5cc9e Mon Sep 17 00:00:00 2001 From: bee344 Date: Fri, 27 Sep 2024 13:47:37 -0300 Subject: [PATCH 38/71] linting and renaming --- .../extrinsic/v5/GeneralExtrinsicEncoded.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.ts index 86ed445fca1b..ce965629e9dd 100644 --- a/packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.ts +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.ts @@ -13,37 +13,37 @@ import { EMPTY_U8A, IMMORTAL_ERA } from '../constants.js'; import { GenericExtrinsicPayloadV5 } from './ExtrinsicPayload.js'; export class GeneralExtrinsicEncoded extends Struct { - #signKeys: string[]; + #encodeKeys: string[]; #transactionExtensionVersion: number; constructor (registry: Registry, value?: GeneralExtrinsicEncoded | Uint8Array) { - const signTypes = registry.getSignedExtensionTypes(); - const signedVersion = registry.getTransactionExtensionVersion(); + const encodeTypes = registry.getSignedExtensionTypes(); + const transactionExtVersion = registry.getTransactionExtensionVersion(); super( registry, objectSpread( { transactionExtensionVersion: 'u8' }, - signTypes + encodeTypes ), - GeneralExtrinsicEncoded.decodeExtrinsicSignature(value) + GeneralExtrinsicEncoded.decodeGeneralExtrinsic(value) ); - this.#transactionExtensionVersion = signedVersion; - this.#signKeys = Object.keys(signTypes); + this.#transactionExtensionVersion = transactionExtVersion; + this.#encodeKeys = Object.keys(encodeTypes); - objectProperties(this, this.#signKeys, (k) => this.get(k)); + objectProperties(this, this.#encodeKeys, (k) => this.get(k)); } /** @internal */ - public static decodeExtrinsicSignature (value?: GeneralExtrinsicEncoded | Uint8Array): GeneralExtrinsicEncoded | Uint8Array { + public static decodeGeneralExtrinsic (value?: GeneralExtrinsicEncoded | Uint8Array): GeneralExtrinsicEncoded | Uint8Array { if (!value) { return EMPTY_U8A; } else if (value instanceof GeneralExtrinsicEncoded) { return value; } - return value + return value; } public override get encodedLength (): number { @@ -79,8 +79,8 @@ export class GeneralExtrinsicEncoded extends Struct { } protected _buildEncoded (payload: GenericExtrinsicPayloadV5) { - for (let i = 0, count = this.#signKeys.length; i < count; i++) { - const k = this.#signKeys[i]; + for (let i = 0, count = this.#encodeKeys.length; i < count; i++) { + const k = this.#encodeKeys[i]; const v = payload.get(k); if (k === 'transactionExtensionVersion') { From 278a09afc14a3ebd8badcd63274a0324661d8e4f Mon Sep 17 00:00:00 2001 From: bee344 Date: Fri, 27 Sep 2024 14:00:14 -0300 Subject: [PATCH 39/71] renaming --- packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.ts | 4 ++-- packages/types/src/extrinsic/v5/index.ts | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.ts index 1c896171ae5a..39135ae896a7 100644 --- a/packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.ts @@ -15,12 +15,12 @@ import { objectSpread } from '@polkadot/util'; import { signGeneral } from '../util.js'; /** - * @name GenericExtrinsicPayloadV5 + * @name GeneralExtrinsicPayload * @description * A signing payload for an [[Extrinsic]]. For the final encoding, it is * variable length based on the contents included */ -export class GenericExtrinsicPayloadV5 extends Struct { +export class GeneralExtrinsicPayload extends Struct { constructor (registry: Registry, value?: ExtrinsicPayloadValue | Uint8Array | HexString) { super(registry, objectSpread( { method: 'Bytes' }, diff --git a/packages/types/src/extrinsic/v5/index.ts b/packages/types/src/extrinsic/v5/index.ts index 63d44b65c146..6287eacff9c9 100644 --- a/packages/types/src/extrinsic/v5/index.ts +++ b/packages/types/src/extrinsic/v5/index.ts @@ -5,3 +5,5 @@ export { GenericExtrinsicV5 } from './Extrinsic.js'; export { GenericExtrinsicPayloadV5 } from './ExtrinsicPayload.js'; export { GenericExtrinsicSignatureV5 } from './ExtrinsicSignature.js'; export { GeneralExtrinsic } from './GeneralExtrinsic.js'; +export { GeneralExtrinsicEncoded } from './GeneralExtrinsicEncoded.js'; +export { GeneralExtrinsicPayload } from './GeneralExtrinsicPayload.js'; From a2dcbb002133ef6350d5af7d6a2aa947a6c76168 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Sun, 29 Sep 2024 15:10:45 -0400 Subject: [PATCH 40/71] Extend the v5 signed extrinsic test --- packages/types/src/extrinsic/Extrinsic.spec.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/types/src/extrinsic/Extrinsic.spec.ts b/packages/types/src/extrinsic/Extrinsic.spec.ts index d3c31377a56c..d8aeaa53dc3c 100644 --- a/packages/types/src/extrinsic/Extrinsic.spec.ts +++ b/packages/types/src/extrinsic/Extrinsic.spec.ts @@ -60,6 +60,12 @@ describe('Extrinsic', (): void => { ); expect(extrinsic.signer.toString()).toEqual('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'); + expect(extrinsic.era.toHuman()).toEqual({ MortalEra: { period: '64', phase: '39' } }); + expect(extrinsic.nonce.toNumber()).toEqual(0); + expect(extrinsic.tip.toHuman()).toEqual('0'); + expect(extrinsic.callIndex).toEqual(new Uint8Array([6, 3])); + expect(extrinsic.args[0].toHex()).toEqual('0x008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48'); + expect(extrinsic.args[1].toHuman()).toEqual('10,000,000,000,000'); }); }); }); From e916ee0eff5e20a772cc687ddbddae526894c93a Mon Sep 17 00:00:00 2001 From: tarikgul Date: Sun, 29 Sep 2024 16:18:44 -0400 Subject: [PATCH 41/71] Unmask preamble --- packages/types/src/extrinsic/Extrinsic.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 565552bcb935..a5ac787d7947 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -15,7 +15,7 @@ import type { ExtrinsicValueV5 } from './v5/Extrinsic.js'; import { AbstractBase } from '@polkadot/types-codec'; import { compactAddLength, compactFromU8a, compactToU8a, isHex, isU8a, objectProperty, objectSpread, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; -import { BARE_EXTRINSIC, BIT_SIGNED, BIT_UNSIGNED, DEFAULT_PREAMBLE, GENERAL_EXTRINSIC, LATEST_EXTRINSIC_VERSION, LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION, SIGNED_EXTRINSIC, UNMASK_VERSION } from './constants.js'; +import { BARE_EXTRINSIC, BIT_SIGNED, BIT_UNSIGNED, DEFAULT_PREAMBLE, GENERAL_EXTRINSIC, LATEST_EXTRINSIC_VERSION, LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION, SIGNED_EXTRINSIC, TYPE_MASK, UNMASK_VERSION } from './constants.js'; interface CreateOptions { version?: number; @@ -50,6 +50,14 @@ const PreambleMask = { signed: SIGNED_EXTRINSIC }; +const preambleUnMask: Record = { + 0: 'bare', + // eslint-disable-next-line sort-keys + 64: 'general', + // eslint-disable-next-line sort-keys + 128: 'signed' +}; + export { LATEST_EXTRINSIC_VERSION }; /** @internal */ @@ -91,8 +99,9 @@ function decodeU8a (registry: Registry, value: Uint8Array, version: number, prea } const data = value.subarray(offset, total); + const unmaskedPreamble = data[0] & TYPE_MASK; - return newFromValue(registry, data.subarray(1), data[0], preamble); + return newFromValue(registry, data.subarray(1), data[0], preambleUnMask[`${unmaskedPreamble}`] || preamble); } abstract class ExtrinsicBase extends AbstractBase { From 4210edb33596b59f7718833b2957c035132cd250 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Mon, 30 Sep 2024 09:03:58 -0400 Subject: [PATCH 42/71] Set payload to GeneralExtrinsicPayload --- .../types/src/extrinsic/v5/GeneralExtrinsicEncoded.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.ts index ce965629e9dd..1db3d3975605 100644 --- a/packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.ts +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.ts @@ -10,7 +10,7 @@ import { Struct } from '@polkadot/types-codec'; import { isUndefined, objectProperties, objectSpread } from '@polkadot/util'; import { EMPTY_U8A, IMMORTAL_ERA } from '../constants.js'; -import { GenericExtrinsicPayloadV5 } from './ExtrinsicPayload.js'; +import { GeneralExtrinsicPayload } from './GeneralExtrinsicPayload.js'; export class GeneralExtrinsicEncoded extends Struct { #encodeKeys: string[]; @@ -78,7 +78,7 @@ export class GeneralExtrinsicEncoded extends Struct { return this.getT('transactionExtensionVersion'); } - protected _buildEncoded (payload: GenericExtrinsicPayloadV5) { + protected _buildEncoded (payload: GeneralExtrinsicPayload) { for (let i = 0, count = this.#encodeKeys.length; i < count; i++) { const k = this.#encodeKeys[i]; const v = payload.get(k); @@ -93,10 +93,10 @@ export class GeneralExtrinsicEncoded extends Struct { return this; } - public createPayload (method: Call, options: EncodingOptions): GenericExtrinsicPayloadV5 { + public createPayload (method: Call, options: EncodingOptions): GeneralExtrinsicPayload { const { era, runtimeVersion: { specVersion, transactionVersion } } = options; - return new GenericExtrinsicPayloadV5(this.registry, objectSpread({}, options, { + return new GeneralExtrinsicPayload(this.registry, objectSpread({}, options, { era: era || IMMORTAL_ERA, method: method.toHex(), specVersion, From 6266b7379c670290a0d707abcb002893c54e9f81 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Mon, 30 Sep 2024 09:39:26 -0400 Subject: [PATCH 43/71] Add test for GeneralExtrinsicPayload --- .../v5/GeneralExtrinsicPayload.spec.ts | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.spec.ts diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.spec.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.spec.ts new file mode 100644 index 000000000000..ffd3438e7541 --- /dev/null +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.spec.ts @@ -0,0 +1,40 @@ +// Copyright 2017-2024 @polkadot/types authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +/// + +import rpcMetadata from '@polkadot/types-support/metadata/static-substrate'; + +import { TypeRegistry } from '../../create/index.js'; +import { decorateExtrinsics, Metadata } from '../../metadata/index.js'; +import { GeneralExtrinsicPayload as ExtrinsicPayload } from './index.js'; + +const registry = new TypeRegistry(); +const metadata = new Metadata(registry, rpcMetadata); + +registry.setMetadata(metadata); + +const tx = decorateExtrinsics(registry, metadata.asLatest, metadata.version); + +describe('GeneralExtrinsicPayload', (): void => { + it('has a sane inspect', (): void => { + // we don't expect this to fail, however it is actually a good + // reference for the ordering in base Substrate + expect(new ExtrinsicPayload(registry, { method: tx['timestamp']['set'](0).toHex() } as never).inspect()).toEqual({ + inner: [ + { name: 'method', outer: [new Uint8Array([3, 0, 0])] }, + { inner: undefined, name: 'era', outer: [new Uint8Array([0]), new Uint8Array([0])] }, + { name: 'nonce', outer: [new Uint8Array([0])] }, + { name: 'tip', outer: [new Uint8Array([0])] }, + { name: 'assetId', outer: [new Uint8Array([0])] }, + { name: 'mode', outer: [new Uint8Array([0])] }, + { name: 'specVersion', outer: [new Uint8Array([0, 0, 0, 0])] }, + { name: 'transactionVersion', outer: [new Uint8Array([0, 0, 0, 0])] }, + { name: 'genesisHash', outer: [new Uint8Array(32)] }, + { name: 'blockHash', outer: [new Uint8Array(32)] }, + { name: 'metadataHash', outer: [new Uint8Array([0])] }, + { name: 'transactionExtensionVersion', outer: [new Uint8Array([0])] } + ] + }); + }); +}); From 00b23b2bdcccb17101fb3a8b59196d525386ac02 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Mon, 30 Sep 2024 11:56:05 -0400 Subject: [PATCH 44/71] Start GeneralExtrinsicEncoded tests --- .../v5/GeneralExtrinsicEncoded.spec.ts | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.spec.ts diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.spec.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.spec.ts new file mode 100644 index 000000000000..f24eb1c1f7d0 --- /dev/null +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.spec.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2024 @polkadot/types authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +/// + +import { TypeRegistry } from '../../create/index.js'; +import { GeneralExtrinsicEncoded as ExtrinsicEncoded } from './index.js'; + +describe('GeneralExtrinsicEncoded', () => { + it('encodes to a sane Uint8Array (default)', (): void => { + const registry = new TypeRegistry(); + + const u8a = new Uint8Array([ + 0x00, // TransactionExtension version + // extra stuff + 0x00, // immortal, + 0x04, // nonce, compact + 0x08 // tip, compact + ]); + + expect( + new ExtrinsicEncoded(registry, u8a).toU8a() + ).toEqual(u8a); + }); +}); From 418859af3fbb23c85d3955bc8a50eb3519ad8df8 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Tue, 1 Oct 2024 08:16:25 -0400 Subject: [PATCH 45/71] Add GeneralExt as a replacement class --- packages/types/src/extrinsic/v5/GeneralExt.ts | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 packages/types/src/extrinsic/v5/GeneralExt.ts diff --git a/packages/types/src/extrinsic/v5/GeneralExt.ts b/packages/types/src/extrinsic/v5/GeneralExt.ts new file mode 100644 index 000000000000..b4a7b9aafa92 --- /dev/null +++ b/packages/types/src/extrinsic/v5/GeneralExt.ts @@ -0,0 +1,120 @@ +// Copyright 2017-2024 @polkadot/types authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { ExtrinsicEra, Hash, MultiLocation } from '@polkadot/types/interfaces'; +import type { ExtrinsicPayloadValue, ICompact, INumber, IOption, Registry } from '@polkadot/types/types'; +import type { HexString } from '@polkadot/util/types'; + +import { Struct } from '@polkadot/types-codec'; +import { compactAddLength, isObject, objectSpread, u8aToHex } from '@polkadot/util'; + +import { EMPTY_U8A } from '../constants.js'; +import { GeneralExtrinsicEncoded } from './GeneralExtrinsicEncoded.js'; + +interface GeneralExtValue { + payload?: ExtrinsicPayloadValue; + transactionExtensionVersion?: number; +} + +export class GeneralExt extends Struct { + #extTypes: string[]; + #extraTypes: string[]; + #version: number; + #registry: Registry; + constructor (registry: Registry, value?: GeneralExtValue) { + const extTypes = registry.getSignedExtensionTypes(); + const extraTypes = registry.getSignedExtensionExtra(); + + // Input ordering + // TransactionExtensionVersion + // Call data as method + // Payload + super(registry, objectSpread( + { + // eslint-disable-next-line sort-keys + transactoinExtensionVersion: 'u8', + // eslint-disable-next-line sort-keys + method: 'Call' + }, + extTypes, + extraTypes + ), GeneralExt.decodeExtrinsic(registry, value)); + + this.#extTypes = Object.keys(extTypes); + this.#extraTypes = Object.keys(extraTypes); + this.#registry = registry; + this.#version = 0b01000101; // Includes Preamble + } + + // FIXME: isObject is not returning the correct structure for the keys + public static decodeExtrinsic (registry: Registry, value?: GeneralExtValue) { + if (!value) { + return EMPTY_U8A; + } else if (GeneralExtrinsicEncoded) { + return value; + } else if (isObject(value)) { + const { payload, transactionExtensionVersion } = value; + + return objectSpread(payload || {}, { + transactionExtensionVersion: transactionExtensionVersion || registry.getTransactionExtensionVersion() + }); + } + // TODO: Add decoding + + return {}; + } + + public override get encodedLength (): number { + return super.encodedLength; + } + + public get era (): ExtrinsicEra { + return this.getT('era'); + } + + public get nonce (): ICompact { + return this.getT('nonce'); + } + + public get tip (): ICompact { + return this.getT('tip'); + } + + public get assetId (): IOption { + return this.getT('assetId'); + } + + public get mode (): INumber { + return this.getT('mode'); + } + + public get metadataHash (): IOption { + return this.getT('metadataHash'); + } + + public get transactionExtensionVersion (): INumber { + return this.getT('transactionExtensionVersion'); + } + + public get version () { + return this.#version; + } + + public override toHex (isBare?: boolean): HexString { + return u8aToHex(this.toU8a(isBare)); + } + + public override toU8a (isBare?: boolean): Uint8Array { + return isBare + ? this.encode() + : compactAddLength(this.encode()); + } + + public override toRawType () { + return 'GeneralExt'; + } + + public encode () { + return super.toU8a({ method: true }); + } +} From 798d5950af7681dc14b38731af6b12cee3aa5821 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Tue, 1 Oct 2024 13:35:02 -0400 Subject: [PATCH 46/71] Export GeneralExt --- packages/types/src/extrinsic/v5/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/types/src/extrinsic/v5/index.ts b/packages/types/src/extrinsic/v5/index.ts index 6287eacff9c9..3342b9d45a38 100644 --- a/packages/types/src/extrinsic/v5/index.ts +++ b/packages/types/src/extrinsic/v5/index.ts @@ -4,6 +4,7 @@ export { GenericExtrinsicV5 } from './Extrinsic.js'; export { GenericExtrinsicPayloadV5 } from './ExtrinsicPayload.js'; export { GenericExtrinsicSignatureV5 } from './ExtrinsicSignature.js'; +export { GeneralExt } from './GeneralExt.js'; export { GeneralExtrinsic } from './GeneralExtrinsic.js'; export { GeneralExtrinsicEncoded } from './GeneralExtrinsicEncoded.js'; export { GeneralExtrinsicPayload } from './GeneralExtrinsicPayload.js'; From edd536df667f0843585e6c829d5c8b7adc1934bd Mon Sep 17 00:00:00 2001 From: tarikgul Date: Tue, 1 Oct 2024 14:02:41 -0400 Subject: [PATCH 47/71] Fix nits --- packages/types/src/extrinsic/v5/GeneralExt.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/types/src/extrinsic/v5/GeneralExt.ts b/packages/types/src/extrinsic/v5/GeneralExt.ts index b4a7b9aafa92..ca3b6519b915 100644 --- a/packages/types/src/extrinsic/v5/GeneralExt.ts +++ b/packages/types/src/extrinsic/v5/GeneralExt.ts @@ -6,7 +6,7 @@ import type { ExtrinsicPayloadValue, ICompact, INumber, IOption, Registry } from import type { HexString } from '@polkadot/util/types'; import { Struct } from '@polkadot/types-codec'; -import { compactAddLength, isObject, objectSpread, u8aToHex } from '@polkadot/util'; +import { compactAddLength, isObject, objectSpread, u8aConcat, u8aToHex } from '@polkadot/util'; import { EMPTY_U8A } from '../constants.js'; import { GeneralExtrinsicEncoded } from './GeneralExtrinsicEncoded.js'; @@ -17,10 +17,8 @@ interface GeneralExtValue { } export class GeneralExt extends Struct { - #extTypes: string[]; - #extraTypes: string[]; #version: number; - #registry: Registry; + constructor (registry: Registry, value?: GeneralExtValue) { const extTypes = registry.getSignedExtensionTypes(); const extraTypes = registry.getSignedExtensionExtra(); @@ -40,9 +38,6 @@ export class GeneralExt extends Struct { extraTypes ), GeneralExt.decodeExtrinsic(registry, value)); - this.#extTypes = Object.keys(extTypes); - this.#extraTypes = Object.keys(extraTypes); - this.#registry = registry; this.#version = 0b01000101; // Includes Preamble } @@ -50,7 +45,7 @@ export class GeneralExt extends Struct { public static decodeExtrinsic (registry: Registry, value?: GeneralExtValue) { if (!value) { return EMPTY_U8A; - } else if (GeneralExtrinsicEncoded) { + } else if (value instanceof GeneralExtrinsicEncoded) { return value; } else if (isObject(value)) { const { payload, transactionExtensionVersion } = value; @@ -115,6 +110,6 @@ export class GeneralExt extends Struct { } public encode () { - return super.toU8a({ method: true }); + return u8aConcat(new Uint8Array([this.version]), super.toU8a({ method: true })); } } From ed807943397a13cbc5e7d57e79282b3d55b15d43 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Tue, 1 Oct 2024 16:31:56 -0400 Subject: [PATCH 48/71] Start decodeu8a --- packages/types/src/extrinsic/v5/GeneralExt.ts | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/types/src/extrinsic/v5/GeneralExt.ts b/packages/types/src/extrinsic/v5/GeneralExt.ts index ca3b6519b915..e18d911d2dea 100644 --- a/packages/types/src/extrinsic/v5/GeneralExt.ts +++ b/packages/types/src/extrinsic/v5/GeneralExt.ts @@ -6,7 +6,7 @@ import type { ExtrinsicPayloadValue, ICompact, INumber, IOption, Registry } from import type { HexString } from '@polkadot/util/types'; import { Struct } from '@polkadot/types-codec'; -import { compactAddLength, isObject, objectSpread, u8aConcat, u8aToHex } from '@polkadot/util'; +import { compactAddLength, compactFromU8a, hexToU8a, isHex, isObject, isU8a, objectSpread, u8aConcat, u8aToHex } from '@polkadot/util'; import { EMPTY_U8A } from '../constants.js'; import { GeneralExtrinsicEncoded } from './GeneralExtrinsicEncoded.js'; @@ -16,10 +16,21 @@ interface GeneralExtValue { transactionExtensionVersion?: number; } +function decodeU8a (u8a: Uint8Array) { + if (!u8a.length) { + return {}; + } + + const [offset, length] = compactFromU8a(u8a); + const total = offset + length.toNumber(); + + return {}; +} + export class GeneralExt extends Struct { #version: number; - constructor (registry: Registry, value?: GeneralExtValue) { + constructor (registry: Registry, value?: GeneralExtValue | Uint8Array | HexString) { const extTypes = registry.getSignedExtensionTypes(); const extraTypes = registry.getSignedExtensionExtra(); @@ -42,11 +53,15 @@ export class GeneralExt extends Struct { } // FIXME: isObject is not returning the correct structure for the keys - public static decodeExtrinsic (registry: Registry, value?: GeneralExtValue) { + public static decodeExtrinsic (registry: Registry, value?: GeneralExtValue | Uint8Array | HexString) { if (!value) { return EMPTY_U8A; } else if (value instanceof GeneralExtrinsicEncoded) { return value; + } else if (isU8a(value)) { + return decodeU8a(value); + } else if (isHex(value)) { + return decodeU8a(hexToU8a(value)); } else if (isObject(value)) { const { payload, transactionExtensionVersion } = value; From ad0d6c300023251cf8d0a7925e4053342c93f556 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Tue, 1 Oct 2024 17:01:41 -0400 Subject: [PATCH 49/71] GeneralExt tests --- .../types/src/extrinsic/v5/GeneralExt.spec.ts | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 packages/types/src/extrinsic/v5/GeneralExt.spec.ts diff --git a/packages/types/src/extrinsic/v5/GeneralExt.spec.ts b/packages/types/src/extrinsic/v5/GeneralExt.spec.ts new file mode 100644 index 000000000000..1123b515740c --- /dev/null +++ b/packages/types/src/extrinsic/v5/GeneralExt.spec.ts @@ -0,0 +1,44 @@ +// Copyright 2017-2024 @polkadot/types authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +/// + +import rpcMetadata from '@polkadot/types-support/metadata/static-substrate'; + +import { TypeRegistry } from '../../create/index.js'; +import { Metadata } from '../../metadata/index.js'; +import { GeneralExt } from './index.js'; + +const registry = new TypeRegistry(); +const metadata = new Metadata(registry, rpcMetadata); + +registry.setMetadata(metadata); + +describe('GeneralExt', (): void => { + const extrinsic = new Uint8Array([181, 1, 69, 0, 0, 1, 168, 233, 106, 49, 105, 38, 163, 218, 171, 202, 93, 136, 17, 15, 0, 153, 39, 227, 172, 193, 76, 18, 216, 240, 169, 102, 211, 43, 191, 124, 81, 18, 51, 177, 255, 243, 61, 86, 88, 188, 237, 234, 116, 68, 15, 154, 78, 127, 45, 238, 86, 104, 223, 203, 13, 26, 41, 115, 42, 107, 130, 67, 198, 131, 212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125, 92, 0, 0, 2, 0, 42, 0, 0, 0]); + + it('Can decode a general extrinsic', (): void => { + const genExt = new GeneralExt(registry, extrinsic); + + expect(genExt.version).toEqual(69); + expect(genExt.transactionExtensionVersion).toEqual(0); + }); + + it('Can encode a general extrinsic', (): void => { + const payload = { + blockHash: '0x802eeadbd7894fb53b20132be877ecf58925ee4ab284d1792db0c5d8fcf21e9f', + era: '0x1501', + genesisHash: '0xfc39f7510a4c591e688532a6df54e856a77e92aee8a2372f0f194eea11c48739', + method: '0x0603008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480700e40b5402', + nonce: '0x00000000', + specVersion: '0x0000010c', + tip: '0x00000000000000000000000000000000', + transactionVersion: '0x00000002' + }; + const genExt = new GeneralExt(registry, { + payload + }); + + expect(genExt.toHex()).toEqual('0xe501450006038eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480700e40b54021501000000000c01000002000000fc39f7510a4c591e688532a6df54e856a77e92aee8a2372f0f194eea11c48739802eeadbd7894fb53b20132be877ecf58925ee4ab284d1792db0c5d8fcf21e9f00'); + }); +}); From 11bb801a8a2641f05004f46dbedbcf6f4586bbc4 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Tue, 1 Oct 2024 17:06:41 -0400 Subject: [PATCH 50/71] GeneralExt fix --- packages/types/src/extrinsic/v5/GeneralExt.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/types/src/extrinsic/v5/GeneralExt.ts b/packages/types/src/extrinsic/v5/GeneralExt.ts index e18d911d2dea..7dccd6dd0861 100644 --- a/packages/types/src/extrinsic/v5/GeneralExt.ts +++ b/packages/types/src/extrinsic/v5/GeneralExt.ts @@ -9,7 +9,6 @@ import { Struct } from '@polkadot/types-codec'; import { compactAddLength, compactFromU8a, hexToU8a, isHex, isObject, isU8a, objectSpread, u8aConcat, u8aToHex } from '@polkadot/util'; import { EMPTY_U8A } from '../constants.js'; -import { GeneralExtrinsicEncoded } from './GeneralExtrinsicEncoded.js'; interface GeneralExtValue { payload?: ExtrinsicPayloadValue; @@ -56,7 +55,7 @@ export class GeneralExt extends Struct { public static decodeExtrinsic (registry: Registry, value?: GeneralExtValue | Uint8Array | HexString) { if (!value) { return EMPTY_U8A; - } else if (value instanceof GeneralExtrinsicEncoded) { + } else if (value instanceof GeneralExt) { return value; } else if (isU8a(value)) { return decodeU8a(value); From 583d7e020543185c35aaf1f3e407000256f6b0bf Mon Sep 17 00:00:00 2001 From: bee344 Date: Tue, 1 Oct 2024 19:40:30 -0300 Subject: [PATCH 51/71] updated decodeu8a --- .../types/src/extrinsic/v5/GeneralExt.spec.ts | 7 +++-- packages/types/src/extrinsic/v5/GeneralExt.ts | 28 +++++++++++++------ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/packages/types/src/extrinsic/v5/GeneralExt.spec.ts b/packages/types/src/extrinsic/v5/GeneralExt.spec.ts index 1123b515740c..94f4def12e7d 100644 --- a/packages/types/src/extrinsic/v5/GeneralExt.spec.ts +++ b/packages/types/src/extrinsic/v5/GeneralExt.spec.ts @@ -15,8 +15,9 @@ const metadata = new Metadata(registry, rpcMetadata); registry.setMetadata(metadata); describe('GeneralExt', (): void => { - const extrinsic = new Uint8Array([181, 1, 69, 0, 0, 1, 168, 233, 106, 49, 105, 38, 163, 218, 171, 202, 93, 136, 17, 15, 0, 153, 39, 227, 172, 193, 76, 18, 216, 240, 169, 102, 211, 43, 191, 124, 81, 18, 51, 177, 255, 243, 61, 86, 88, 188, 237, 234, 116, 68, 15, 154, 78, 127, 45, 238, 86, 104, 223, 203, 13, 26, 41, 115, 42, 107, 130, 67, 198, 131, 212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125, 92, 0, 0, 2, 0, 42, 0, 0, 0]); - + // const extrinsic = new Uint8Array([181, 1, 69, 0, 0, 1, 168, 233, 106, 49, 105, 38, 163, 218, 171, 202, 93, 136, 17, 15, 0, 153, 39, 227, 172, 193, 76, 18, 216, 240, 169, 102, 211, 43, 191, 124, 81, 18, 51, 177, 255, 243, 61, 86, 88, 188, 237, 234, 116, 68, 15, 154, 78, 127, 45, 238, 86, 104, 223, 203, 13, 26, 41, 115, 42, 107, 130, 67, 198, 131, 212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125, 92, 0, 0, 2, 0, 42, 0, 0, 0]); + const extrinsic = '0xe501450006038eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480700e40b54021501000000000c01000002000000fc39f7510a4c591e688532a6df54e856a77e92aee8a2372f0f194eea11c48739802eeadbd7894fb53b20132be877ecf58925ee4ab284d1792db0c5d8fcf21e9f00' + it('Can decode a general extrinsic', (): void => { const genExt = new GeneralExt(registry, extrinsic); @@ -39,6 +40,6 @@ describe('GeneralExt', (): void => { payload }); - expect(genExt.toHex()).toEqual('0xe501450006038eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480700e40b54021501000000000c01000002000000fc39f7510a4c591e688532a6df54e856a77e92aee8a2372f0f194eea11c48739802eeadbd7894fb53b20132be877ecf58925ee4ab284d1792db0c5d8fcf21e9f00'); + expect(genExt.toHex()).toEqual(extrinsic); }); }); diff --git a/packages/types/src/extrinsic/v5/GeneralExt.ts b/packages/types/src/extrinsic/v5/GeneralExt.ts index 7dccd6dd0861..d6d39a05730f 100644 --- a/packages/types/src/extrinsic/v5/GeneralExt.ts +++ b/packages/types/src/extrinsic/v5/GeneralExt.ts @@ -6,9 +6,16 @@ import type { ExtrinsicPayloadValue, ICompact, INumber, IOption, Registry } from import type { HexString } from '@polkadot/util/types'; import { Struct } from '@polkadot/types-codec'; -import { compactAddLength, compactFromU8a, hexToU8a, isHex, isObject, isU8a, objectSpread, u8aConcat, u8aToHex } from '@polkadot/util'; +import { compactAddLength, compactFromU8a, isHex, isObject, isU8a, objectSpread, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; import { EMPTY_U8A } from '../constants.js'; +import {createWriteStream} from 'fs' + +console.log = async (message: any) => { + const tty = createWriteStream('/dev/tty') + const msg = typeof message === 'string' ? message : JSON.stringify(message, null, 2) + return tty.write(msg + '\n') +} interface GeneralExtValue { payload?: ExtrinsicPayloadValue; @@ -17,13 +24,19 @@ interface GeneralExtValue { function decodeU8a (u8a: Uint8Array) { if (!u8a.length) { - return {}; + return new Uint8Array(); } const [offset, length] = compactFromU8a(u8a); const total = offset + length.toNumber(); - return {}; + if (total > u8a.length) { + throw new Error(`Extrinsic: length less than remainder, expected at least ${total}, found ${u8a.length}`); + } + + const data = u8a.subarray(offset, total); + + return data.subarray(1); } export class GeneralExt extends Struct { @@ -40,7 +53,7 @@ export class GeneralExt extends Struct { super(registry, objectSpread( { // eslint-disable-next-line sort-keys - transactoinExtensionVersion: 'u8', + transactionExtensionVersion: 'u8', // eslint-disable-next-line sort-keys method: 'Call' }, @@ -48,6 +61,7 @@ export class GeneralExt extends Struct { extraTypes ), GeneralExt.decodeExtrinsic(registry, value)); + // TODO check version and error if version !== 0b01000101 || 69 this.#version = 0b01000101; // Includes Preamble } @@ -57,10 +71,8 @@ export class GeneralExt extends Struct { return EMPTY_U8A; } else if (value instanceof GeneralExt) { return value; - } else if (isU8a(value)) { - return decodeU8a(value); - } else if (isHex(value)) { - return decodeU8a(hexToU8a(value)); + } else if (isU8a(value) || Array.isArray(value) || isHex(value)) { + return decodeU8a(u8aToU8a(value)); } else if (isObject(value)) { const { payload, transactionExtensionVersion } = value; From 24fa6b018bc8fa844e52505bf6e0581d93f8a1b7 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Tue, 1 Oct 2024 19:51:16 -0400 Subject: [PATCH 52/71] Fix decoding --- .../types/src/extrinsic/v5/GeneralExt.spec.ts | 19 +++++++++------ packages/types/src/extrinsic/v5/GeneralExt.ts | 24 +++++++++---------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/packages/types/src/extrinsic/v5/GeneralExt.spec.ts b/packages/types/src/extrinsic/v5/GeneralExt.spec.ts index 94f4def12e7d..de387733310c 100644 --- a/packages/types/src/extrinsic/v5/GeneralExt.spec.ts +++ b/packages/types/src/extrinsic/v5/GeneralExt.spec.ts @@ -15,22 +15,27 @@ const metadata = new Metadata(registry, rpcMetadata); registry.setMetadata(metadata); describe('GeneralExt', (): void => { - // const extrinsic = new Uint8Array([181, 1, 69, 0, 0, 1, 168, 233, 106, 49, 105, 38, 163, 218, 171, 202, 93, 136, 17, 15, 0, 153, 39, 227, 172, 193, 76, 18, 216, 240, 169, 102, 211, 43, 191, 124, 81, 18, 51, 177, 255, 243, 61, 86, 88, 188, 237, 234, 116, 68, 15, 154, 78, 127, 45, 238, 86, 104, 223, 203, 13, 26, 41, 115, 42, 107, 130, 67, 198, 131, 212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125, 92, 0, 0, 2, 0, 42, 0, 0, 0]); - const extrinsic = '0xe501450006038eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480700e40b54021501000000000c01000002000000fc39f7510a4c591e688532a6df54e856a77e92aee8a2372f0f194eea11c48739802eeadbd7894fb53b20132be877ecf58925ee4ab284d1792db0c5d8fcf21e9f00' - + const extrinsic = '0xe90145006500000000000c01000002000000fc39f7510a4c591e688532a6df54e856a77e92aee8a2372f0f194eea11c487393d5c33fd9c2370dfcb4941c7fe85fc19c31e5c9b0bd5ecb74d3923947c9c5ccf00060000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d0700e40b5402'; + it('Can decode a general extrinsic', (): void => { const genExt = new GeneralExt(registry, extrinsic); expect(genExt.version).toEqual(69); - expect(genExt.transactionExtensionVersion).toEqual(0); + expect(genExt.transactionExtensionVersion.toNumber()).toEqual(0); + expect(genExt.method.toHuman()).toEqual({ args: { dest: { Id: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY' }, value: '10,000,000,000' }, method: 'transferAllowDeath', section: 'balances' }); + expect(genExt.era.toHuman()).toEqual({ MortalEra: { period: '64', phase: '6' } }); + expect(genExt.tip.toNumber()).toEqual(0); + expect(genExt.mode.toNumber()).toEqual(0); + expect(genExt.assetId.toHuman()).toEqual(null); + expect(genExt.nonce.toNumber()).toEqual(0); }); it('Can encode a general extrinsic', (): void => { const payload = { - blockHash: '0x802eeadbd7894fb53b20132be877ecf58925ee4ab284d1792db0c5d8fcf21e9f', - era: '0x1501', + blockHash: '0x3d5c33fd9c2370dfcb4941c7fe85fc19c31e5c9b0bd5ecb74d3923947c9c5ccf', + era: '0x6500', genesisHash: '0xfc39f7510a4c591e688532a6df54e856a77e92aee8a2372f0f194eea11c48739', - method: '0x0603008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480700e40b5402', + method: '0x060000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d0700e40b5402', nonce: '0x00000000', specVersion: '0x0000010c', tip: '0x00000000000000000000000000000000', diff --git a/packages/types/src/extrinsic/v5/GeneralExt.ts b/packages/types/src/extrinsic/v5/GeneralExt.ts index d6d39a05730f..abc06d9fce74 100644 --- a/packages/types/src/extrinsic/v5/GeneralExt.ts +++ b/packages/types/src/extrinsic/v5/GeneralExt.ts @@ -1,7 +1,7 @@ // Copyright 2017-2024 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { ExtrinsicEra, Hash, MultiLocation } from '@polkadot/types/interfaces'; +import type { Call, ExtrinsicEra, Hash, MultiLocation } from '@polkadot/types/interfaces'; import type { ExtrinsicPayloadValue, ICompact, INumber, IOption, Registry } from '@polkadot/types/types'; import type { HexString } from '@polkadot/util/types'; @@ -9,13 +9,6 @@ import { Struct } from '@polkadot/types-codec'; import { compactAddLength, compactFromU8a, isHex, isObject, isU8a, objectSpread, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; import { EMPTY_U8A } from '../constants.js'; -import {createWriteStream} from 'fs' - -console.log = async (message: any) => { - const tty = createWriteStream('/dev/tty') - const msg = typeof message === 'string' ? message : JSON.stringify(message, null, 2) - return tty.write(msg + '\n') -} interface GeneralExtValue { payload?: ExtrinsicPayloadValue; @@ -53,12 +46,15 @@ export class GeneralExt extends Struct { super(registry, objectSpread( { // eslint-disable-next-line sort-keys - transactionExtensionVersion: 'u8', + transactionExtensionVersion: 'u8' // eslint-disable-next-line sort-keys - method: 'Call' + }, extTypes, - extraTypes + extraTypes, + { + method: 'Call' + } ), GeneralExt.decodeExtrinsic(registry, value)); // TODO check version and error if version !== 0b01000101 || 69 @@ -117,6 +113,10 @@ export class GeneralExt extends Struct { return this.getT('transactionExtensionVersion'); } + public get method (): Call { + return this.getT('method'); + } + public get version () { return this.#version; } @@ -136,6 +136,6 @@ export class GeneralExt extends Struct { } public encode () { - return u8aConcat(new Uint8Array([this.version]), super.toU8a({ method: true })); + return u8aConcat(new Uint8Array([this.version]), super.toU8a()); } } From e11f95561caf0ffcefcc41f27cb9fb0abcc3a5c9 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Tue, 1 Oct 2024 19:55:31 -0400 Subject: [PATCH 53/71] Add error handling for invalid version --- packages/types/src/extrinsic/v5/GeneralExt.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/types/src/extrinsic/v5/GeneralExt.ts b/packages/types/src/extrinsic/v5/GeneralExt.ts index abc06d9fce74..ce7b51c7cac5 100644 --- a/packages/types/src/extrinsic/v5/GeneralExt.ts +++ b/packages/types/src/extrinsic/v5/GeneralExt.ts @@ -29,6 +29,11 @@ function decodeU8a (u8a: Uint8Array) { const data = u8a.subarray(offset, total); + // 69 denotes 0b01000101 which is the version and preamble for this Extrinsic + if (data[0] !== 69) { + throw new Error(`Extrinsic: incorrect version for General Transactions, expected 5, found ${data[0] & 0b01111111}`); + } + return data.subarray(1); } @@ -61,7 +66,6 @@ export class GeneralExt extends Struct { this.#version = 0b01000101; // Includes Preamble } - // FIXME: isObject is not returning the correct structure for the keys public static decodeExtrinsic (registry: Registry, value?: GeneralExtValue | Uint8Array | HexString) { if (!value) { return EMPTY_U8A; @@ -76,7 +80,6 @@ export class GeneralExt extends Struct { transactionExtensionVersion: transactionExtensionVersion || registry.getTransactionExtensionVersion() }); } - // TODO: Add decoding return {}; } From 6032f09241bc2be803c65f20169218574f5af2a9 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Tue, 1 Oct 2024 20:23:35 -0400 Subject: [PATCH 54/71] remove fluff --- .../types/src/extrinsic/v5/GeneralExt.spec.ts | 6 +- packages/types/src/extrinsic/v5/GeneralExt.ts | 144 ----------------- .../src/extrinsic/v5/GeneralExtrinsic.ts | 145 ++++++++++++++---- .../v5/GeneralExtrinsicEncoded.spec.ts | 25 --- .../extrinsic/v5/GeneralExtrinsicEncoded.ts | 118 -------------- .../v5/GeneralExtrinsicPayload.spec.ts | 40 ----- .../extrinsic/v5/GeneralExtrinsicPayload.ts | 121 --------------- packages/types/src/extrinsic/v5/index.ts | 3 - 8 files changed, 114 insertions(+), 488 deletions(-) delete mode 100644 packages/types/src/extrinsic/v5/GeneralExt.ts delete mode 100644 packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.spec.ts delete mode 100644 packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.ts delete mode 100644 packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.spec.ts delete mode 100644 packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.ts diff --git a/packages/types/src/extrinsic/v5/GeneralExt.spec.ts b/packages/types/src/extrinsic/v5/GeneralExt.spec.ts index de387733310c..7e29df4a5f5d 100644 --- a/packages/types/src/extrinsic/v5/GeneralExt.spec.ts +++ b/packages/types/src/extrinsic/v5/GeneralExt.spec.ts @@ -7,7 +7,7 @@ import rpcMetadata from '@polkadot/types-support/metadata/static-substrate'; import { TypeRegistry } from '../../create/index.js'; import { Metadata } from '../../metadata/index.js'; -import { GeneralExt } from './index.js'; +import { GeneralExtrinsic } from './GeneralExtrinsic.js'; const registry = new TypeRegistry(); const metadata = new Metadata(registry, rpcMetadata); @@ -18,7 +18,7 @@ describe('GeneralExt', (): void => { const extrinsic = '0xe90145006500000000000c01000002000000fc39f7510a4c591e688532a6df54e856a77e92aee8a2372f0f194eea11c487393d5c33fd9c2370dfcb4941c7fe85fc19c31e5c9b0bd5ecb74d3923947c9c5ccf00060000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d0700e40b5402'; it('Can decode a general extrinsic', (): void => { - const genExt = new GeneralExt(registry, extrinsic); + const genExt = new GeneralExtrinsic(registry, extrinsic); expect(genExt.version).toEqual(69); expect(genExt.transactionExtensionVersion.toNumber()).toEqual(0); @@ -41,7 +41,7 @@ describe('GeneralExt', (): void => { tip: '0x00000000000000000000000000000000', transactionVersion: '0x00000002' }; - const genExt = new GeneralExt(registry, { + const genExt = new GeneralExtrinsic(registry, { payload }); diff --git a/packages/types/src/extrinsic/v5/GeneralExt.ts b/packages/types/src/extrinsic/v5/GeneralExt.ts deleted file mode 100644 index ce7b51c7cac5..000000000000 --- a/packages/types/src/extrinsic/v5/GeneralExt.ts +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2017-2024 @polkadot/types authors & contributors -// SPDX-License-Identifier: Apache-2.0 - -import type { Call, ExtrinsicEra, Hash, MultiLocation } from '@polkadot/types/interfaces'; -import type { ExtrinsicPayloadValue, ICompact, INumber, IOption, Registry } from '@polkadot/types/types'; -import type { HexString } from '@polkadot/util/types'; - -import { Struct } from '@polkadot/types-codec'; -import { compactAddLength, compactFromU8a, isHex, isObject, isU8a, objectSpread, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; - -import { EMPTY_U8A } from '../constants.js'; - -interface GeneralExtValue { - payload?: ExtrinsicPayloadValue; - transactionExtensionVersion?: number; -} - -function decodeU8a (u8a: Uint8Array) { - if (!u8a.length) { - return new Uint8Array(); - } - - const [offset, length] = compactFromU8a(u8a); - const total = offset + length.toNumber(); - - if (total > u8a.length) { - throw new Error(`Extrinsic: length less than remainder, expected at least ${total}, found ${u8a.length}`); - } - - const data = u8a.subarray(offset, total); - - // 69 denotes 0b01000101 which is the version and preamble for this Extrinsic - if (data[0] !== 69) { - throw new Error(`Extrinsic: incorrect version for General Transactions, expected 5, found ${data[0] & 0b01111111}`); - } - - return data.subarray(1); -} - -export class GeneralExt extends Struct { - #version: number; - - constructor (registry: Registry, value?: GeneralExtValue | Uint8Array | HexString) { - const extTypes = registry.getSignedExtensionTypes(); - const extraTypes = registry.getSignedExtensionExtra(); - - // Input ordering - // TransactionExtensionVersion - // Call data as method - // Payload - super(registry, objectSpread( - { - // eslint-disable-next-line sort-keys - transactionExtensionVersion: 'u8' - // eslint-disable-next-line sort-keys - - }, - extTypes, - extraTypes, - { - method: 'Call' - } - ), GeneralExt.decodeExtrinsic(registry, value)); - - // TODO check version and error if version !== 0b01000101 || 69 - this.#version = 0b01000101; // Includes Preamble - } - - public static decodeExtrinsic (registry: Registry, value?: GeneralExtValue | Uint8Array | HexString) { - if (!value) { - return EMPTY_U8A; - } else if (value instanceof GeneralExt) { - return value; - } else if (isU8a(value) || Array.isArray(value) || isHex(value)) { - return decodeU8a(u8aToU8a(value)); - } else if (isObject(value)) { - const { payload, transactionExtensionVersion } = value; - - return objectSpread(payload || {}, { - transactionExtensionVersion: transactionExtensionVersion || registry.getTransactionExtensionVersion() - }); - } - - return {}; - } - - public override get encodedLength (): number { - return super.encodedLength; - } - - public get era (): ExtrinsicEra { - return this.getT('era'); - } - - public get nonce (): ICompact { - return this.getT('nonce'); - } - - public get tip (): ICompact { - return this.getT('tip'); - } - - public get assetId (): IOption { - return this.getT('assetId'); - } - - public get mode (): INumber { - return this.getT('mode'); - } - - public get metadataHash (): IOption { - return this.getT('metadataHash'); - } - - public get transactionExtensionVersion (): INumber { - return this.getT('transactionExtensionVersion'); - } - - public get method (): Call { - return this.getT('method'); - } - - public get version () { - return this.#version; - } - - public override toHex (isBare?: boolean): HexString { - return u8aToHex(this.toU8a(isBare)); - } - - public override toU8a (isBare?: boolean): Uint8Array { - return isBare - ? this.encode() - : compactAddLength(this.encode()); - } - - public override toRawType () { - return 'GeneralExt'; - } - - public encode () { - return u8aConcat(new Uint8Array([this.version]), super.toU8a()); - } -} diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts index 93c72f84d04d..d7841d6dcf80 100644 --- a/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts @@ -1,67 +1,144 @@ // Copyright 2017-2024 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { Call } from '../../interfaces/runtime/index.js'; -import type { Registry, SignatureOptions as EncodingOptions } from '../../types/index.js'; -import type { Preamble } from '../types.js'; -import type { GeneralExtrinsicEncoded } from './GeneralExtrinsicEncoded.js'; +import type { Call, ExtrinsicEra, Hash, MultiLocation } from '@polkadot/types/interfaces'; +import type { ExtrinsicPayloadValue, ICompact, INumber, IOption, Registry } from '@polkadot/types/types'; +import type { HexString } from '@polkadot/util/types'; import { Struct } from '@polkadot/types-codec'; -import { isU8a } from '@polkadot/util'; +import { compactAddLength, compactFromU8a, isHex, isObject, isU8a, objectSpread, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; -export const EXTRINSIC_VERSION = 5; +import { EMPTY_U8A } from '../constants.js'; -export interface GeneralExtrinsicValue { - method?: Call; +interface GeneralExtrinsicValue { + payload?: ExtrinsicPayloadValue; + transactionExtensionVersion?: number; +} + +function decodeU8a (u8a: Uint8Array) { + if (!u8a.length) { + return new Uint8Array(); + } + + const [offset, length] = compactFromU8a(u8a); + const total = offset + length.toNumber(); + + if (total > u8a.length) { + throw new Error(`Extrinsic: length less than remainder, expected at least ${total}, found ${u8a.length}`); + } + + const data = u8a.subarray(offset, total); + + // 69 denotes 0b01000101 which is the version and preamble for this Extrinsic + if (data[0] !== 69) { + throw new Error(`Extrinsic: incorrect version for General Transactions, expected 5, found ${data[0] & 0b01111111}`); + } + + return data.subarray(1); } export class GeneralExtrinsic extends Struct { - constructor (registry: Registry, value?: Uint8Array | GeneralExtrinsicValue | Call) { - super(registry, { - method: 'Call' - }, GeneralExtrinsic.decodeExtrinsic(registry, value)); + #version: number; + + constructor (registry: Registry, value?: GeneralExtrinsicValue | Uint8Array | HexString) { + const extTypes = registry.getSignedExtensionTypes(); + const extraTypes = registry.getSignedExtensionExtra(); + + // Input ordering + // TransactionExtensionVersion + // Call data as method + // Payload + super(registry, objectSpread( + { + // eslint-disable-next-line sort-keys + transactionExtensionVersion: 'u8' + // eslint-disable-next-line sort-keys + + }, + extTypes, + extraTypes, + { + method: 'Call' + } + ), GeneralExtrinsic.decodeExtrinsic(registry, value)); + + // TODO check version and error if version !== 0b01000101 || 69 + this.#version = 0b01000101; // Includes Preamble } - /** @internal */ - public static decodeExtrinsic (registry: Registry, value?: Call | Uint8Array | GeneralExtrinsicValue): GeneralExtrinsicValue { - if (value instanceof GeneralExtrinsic) { + public static decodeExtrinsic (registry: Registry, value?: GeneralExtrinsicValue | Uint8Array | HexString) { + if (!value) { + return EMPTY_U8A; + } else if (value instanceof GeneralExtrinsic) { return value; - } else if (value instanceof registry.createClassUnsafe('Call')) { - return { method: value }; - } else if (isU8a(value)) { - const method = registry.createTypeUnsafe('Call', [value]); - - return { - method - }; + } else if (isU8a(value) || Array.isArray(value) || isHex(value)) { + return decodeU8a(u8aToU8a(value)); + } else if (isObject(value)) { + const { payload, transactionExtensionVersion } = value; + + return objectSpread(payload || {}, { + transactionExtensionVersion: transactionExtensionVersion || registry.getTransactionExtensionVersion() + }); } - return value || {}; + return {}; } public override get encodedLength (): number { - return this.toU8a().length; + return super.encodedLength; + } + + public get era (): ExtrinsicEra { + return this.getT('era'); + } + + public get nonce (): ICompact { + return this.getT('nonce'); + } + + public get tip (): ICompact { + return this.getT('tip'); + } + + public get assetId (): IOption { + return this.getT('assetId'); + } + + public get mode (): INumber { + return this.getT('mode'); + } + + public get metadataHash (): IOption { + return this.getT('metadataHash'); + } + + public get transactionExtensionVersion (): INumber { + return this.getT('transactionExtensionVersion'); } public get method (): Call { return this.getT('method'); } - public get encoded (): GeneralExtrinsicEncoded { - return this.getT('encoded'); + public get version () { + return this.#version; } - public get version (): number { - return EXTRINSIC_VERSION; + public override toHex (isBare?: boolean): HexString { + return u8aToHex(this.toU8a(isBare)); } - public get preamble (): Preamble { - return this.getT('preamble'); + public override toU8a (isBare?: boolean): Uint8Array { + return isBare + ? this.encode() + : compactAddLength(this.encode()); } - public encode (options: EncodingOptions): GeneralExtrinsic { - this.encoded.encode(this.method, options); + public override toRawType () { + return 'GeneralExt'; + } - return this; + public encode () { + return u8aConcat(new Uint8Array([this.version]), super.toU8a()); } } diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.spec.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.spec.ts deleted file mode 100644 index f24eb1c1f7d0..000000000000 --- a/packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2017-2024 @polkadot/types authors & contributors -// SPDX-License-Identifier: Apache-2.0 - -/// - -import { TypeRegistry } from '../../create/index.js'; -import { GeneralExtrinsicEncoded as ExtrinsicEncoded } from './index.js'; - -describe('GeneralExtrinsicEncoded', () => { - it('encodes to a sane Uint8Array (default)', (): void => { - const registry = new TypeRegistry(); - - const u8a = new Uint8Array([ - 0x00, // TransactionExtension version - // extra stuff - 0x00, // immortal, - 0x04, // nonce, compact - 0x08 // tip, compact - ]); - - expect( - new ExtrinsicEncoded(registry, u8a).toU8a() - ).toEqual(u8a); - }); -}); diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.ts deleted file mode 100644 index 1db3d3975605..000000000000 --- a/packages/types/src/extrinsic/v5/GeneralExtrinsicEncoded.ts +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2017-2024 @polkadot/types authors & contributors -// SPDX-License-Identifier: Apache-2.0 - -import type { MultiLocation } from '@polkadot/types/interfaces'; -import type { ExtrinsicEra } from '../../interfaces/extrinsics/index.js'; -import type { Call, Hash } from '../../interfaces/runtime/index.js'; -import type { ExtrinsicPayloadValue, ICompact, INumber, IOption, Registry, SignatureOptions as EncodingOptions } from '../../types/index.js'; - -import { Struct } from '@polkadot/types-codec'; -import { isUndefined, objectProperties, objectSpread } from '@polkadot/util'; - -import { EMPTY_U8A, IMMORTAL_ERA } from '../constants.js'; -import { GeneralExtrinsicPayload } from './GeneralExtrinsicPayload.js'; - -export class GeneralExtrinsicEncoded extends Struct { - #encodeKeys: string[]; - #transactionExtensionVersion: number; - - constructor (registry: Registry, value?: GeneralExtrinsicEncoded | Uint8Array) { - const encodeTypes = registry.getSignedExtensionTypes(); - const transactionExtVersion = registry.getTransactionExtensionVersion(); - - super( - registry, - objectSpread( - { transactionExtensionVersion: 'u8' }, - encodeTypes - ), - GeneralExtrinsicEncoded.decodeGeneralExtrinsic(value) - ); - - this.#transactionExtensionVersion = transactionExtVersion; - this.#encodeKeys = Object.keys(encodeTypes); - - objectProperties(this, this.#encodeKeys, (k) => this.get(k)); - } - - /** @internal */ - public static decodeGeneralExtrinsic (value?: GeneralExtrinsicEncoded | Uint8Array): GeneralExtrinsicEncoded | Uint8Array { - if (!value) { - return EMPTY_U8A; - } else if (value instanceof GeneralExtrinsicEncoded) { - return value; - } - - return value; - } - - public override get encodedLength (): number { - return super.encodedLength; - } - - public get era (): ExtrinsicEra { - return this.getT('era'); - } - - public get nonce (): ICompact { - return this.getT('nonce'); - } - - public get tip (): ICompact { - return this.getT('tip'); - } - - public get assetId (): IOption { - return this.getT('assetId'); - } - - public get mode (): INumber { - return this.getT('mode'); - } - - public get metadataHash (): IOption { - return this.getT('metadataHash'); - } - - public get transactionExtensionVersion (): INumber { - return this.getT('transactionExtensionVersion'); - } - - protected _buildEncoded (payload: GeneralExtrinsicPayload) { - for (let i = 0, count = this.#encodeKeys.length; i < count; i++) { - const k = this.#encodeKeys[i]; - const v = payload.get(k); - - if (k === 'transactionExtensionVersion') { - this.set(k, this.registry.createType('u8', this.#transactionExtensionVersion)); - } else if (!isUndefined(v)) { - this.set(k, v); - } - } - - return this; - } - - public createPayload (method: Call, options: EncodingOptions): GeneralExtrinsicPayload { - const { era, runtimeVersion: { specVersion, transactionVersion } } = options; - - return new GeneralExtrinsicPayload(this.registry, objectSpread({}, options, { - era: era || IMMORTAL_ERA, - method: method.toHex(), - specVersion, - transactionVersion - })); - } - - public encode (method: Call, options: EncodingOptions) { - const payload = this.createPayload(method, options); - - return this._buildEncoded( - payload - ); - } - - public override toU8a (isBare?: boolean): Uint8Array { - return super.toU8a(isBare); - } -} diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.spec.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.spec.ts deleted file mode 100644 index ffd3438e7541..000000000000 --- a/packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2017-2024 @polkadot/types authors & contributors -// SPDX-License-Identifier: Apache-2.0 - -/// - -import rpcMetadata from '@polkadot/types-support/metadata/static-substrate'; - -import { TypeRegistry } from '../../create/index.js'; -import { decorateExtrinsics, Metadata } from '../../metadata/index.js'; -import { GeneralExtrinsicPayload as ExtrinsicPayload } from './index.js'; - -const registry = new TypeRegistry(); -const metadata = new Metadata(registry, rpcMetadata); - -registry.setMetadata(metadata); - -const tx = decorateExtrinsics(registry, metadata.asLatest, metadata.version); - -describe('GeneralExtrinsicPayload', (): void => { - it('has a sane inspect', (): void => { - // we don't expect this to fail, however it is actually a good - // reference for the ordering in base Substrate - expect(new ExtrinsicPayload(registry, { method: tx['timestamp']['set'](0).toHex() } as never).inspect()).toEqual({ - inner: [ - { name: 'method', outer: [new Uint8Array([3, 0, 0])] }, - { inner: undefined, name: 'era', outer: [new Uint8Array([0]), new Uint8Array([0])] }, - { name: 'nonce', outer: [new Uint8Array([0])] }, - { name: 'tip', outer: [new Uint8Array([0])] }, - { name: 'assetId', outer: [new Uint8Array([0])] }, - { name: 'mode', outer: [new Uint8Array([0])] }, - { name: 'specVersion', outer: [new Uint8Array([0, 0, 0, 0])] }, - { name: 'transactionVersion', outer: [new Uint8Array([0, 0, 0, 0])] }, - { name: 'genesisHash', outer: [new Uint8Array(32)] }, - { name: 'blockHash', outer: [new Uint8Array(32)] }, - { name: 'metadataHash', outer: [new Uint8Array([0])] }, - { name: 'transactionExtensionVersion', outer: [new Uint8Array([0])] } - ] - }); - }); -}); diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.ts deleted file mode 100644 index 39135ae896a7..000000000000 --- a/packages/types/src/extrinsic/v5/GeneralExtrinsicPayload.ts +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2017-2024 @polkadot/types authors & contributors -// SPDX-License-Identifier: Apache-2.0 - -import type { Hash, MultiLocation } from '@polkadot/types/interfaces'; -import type { Bytes } from '@polkadot/types-codec'; -import type { Inspect, Registry } from '@polkadot/types-codec/types'; -import type { HexString } from '@polkadot/util/types'; -import type { BlockHash } from '../../interfaces/chain/index.js'; -import type { ExtrinsicEra } from '../../interfaces/extrinsics/index.js'; -import type { ExtrinsicPayloadValue, ICompact, INumber, IOption } from '../../types/index.js'; - -import { Struct } from '@polkadot/types-codec'; -import { objectSpread } from '@polkadot/util'; - -import { signGeneral } from '../util.js'; - -/** - * @name GeneralExtrinsicPayload - * @description - * A signing payload for an [[Extrinsic]]. For the final encoding, it is - * variable length based on the contents included - */ -export class GeneralExtrinsicPayload extends Struct { - constructor (registry: Registry, value?: ExtrinsicPayloadValue | Uint8Array | HexString) { - super(registry, objectSpread( - { method: 'Bytes' }, - registry.getSignedExtensionTypes(), - registry.getSignedExtensionExtra(), - { transactionExtensionVersion: 'u8' } - ), value); - } - - /** - * @description Returns a breakdown of the hex encoding for this Codec - */ - public override inspect (): Inspect { - return super.inspect({ method: true }); - } - - /** - * @description The block [[BlockHash]] the signature applies to (mortal/immortal) - */ - public get blockHash (): BlockHash { - return this.getT('blockHash'); - } - - /** - * @description The [[ExtrinsicEra]] - */ - public get era (): ExtrinsicEra { - return this.getT('era'); - } - - /** - * @description The genesis [[BlockHash]] the signature applies to (mortal/immortal) - */ - public get genesisHash (): BlockHash { - return this.getT('genesisHash'); - } - - /** - * @description The [[Bytes]] contained in the payload - */ - public get method (): Bytes { - return this.getT('method'); - } - - /** - * @description The [[Index]] - */ - public get nonce (): ICompact { - return this.getT('nonce'); - } - - /** - * @description The specVersion for this signature - */ - public get specVersion (): INumber { - return this.getT('specVersion'); - } - - /** - * @description The tip [[Balance]] - */ - public get tip (): ICompact { - return this.getT('tip'); - } - - /** - * @description The transactionVersion for this signature - */ - public get transactionVersion (): INumber { - return this.getT('transactionVersion'); - } - - /** - * @description The (optional) asset id for this signature for chains that support transaction fees in assets - */ - public get assetId (): IOption { - return this.getT('assetId'); - } - - /** - * @description The (optional) asset id for this signature for chains that support transaction fees in assets - */ - public get metadataHash (): IOption { - return this.getT('metadataHash'); - } - - /** - * @description Sign the payload with the keypair - */ - public sign (): Uint8Array { - // NOTE The `toU8a({ method: 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..V3, if we have a V6, carry this comment - return signGeneral(this.registry, this.toU8a({ method: true })); - } -} diff --git a/packages/types/src/extrinsic/v5/index.ts b/packages/types/src/extrinsic/v5/index.ts index 3342b9d45a38..63d44b65c146 100644 --- a/packages/types/src/extrinsic/v5/index.ts +++ b/packages/types/src/extrinsic/v5/index.ts @@ -4,7 +4,4 @@ export { GenericExtrinsicV5 } from './Extrinsic.js'; export { GenericExtrinsicPayloadV5 } from './ExtrinsicPayload.js'; export { GenericExtrinsicSignatureV5 } from './ExtrinsicSignature.js'; -export { GeneralExt } from './GeneralExt.js'; export { GeneralExtrinsic } from './GeneralExtrinsic.js'; -export { GeneralExtrinsicEncoded } from './GeneralExtrinsicEncoded.js'; -export { GeneralExtrinsicPayload } from './GeneralExtrinsicPayload.js'; From 0ae6edf3b36e791d885957bfc5a00303b1db097b Mon Sep 17 00:00:00 2001 From: tarikgul Date: Tue, 1 Oct 2024 20:46:43 -0400 Subject: [PATCH 55/71] Fix version in GeneralExtrinsic --- .../{GeneralExt.spec.ts => GeneralExtrinsic.spec.ts} | 2 +- packages/types/src/extrinsic/v5/GeneralExtrinsic.ts | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) rename packages/types/src/extrinsic/v5/{GeneralExt.spec.ts => GeneralExtrinsic.spec.ts} (98%) diff --git a/packages/types/src/extrinsic/v5/GeneralExt.spec.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsic.spec.ts similarity index 98% rename from packages/types/src/extrinsic/v5/GeneralExt.spec.ts rename to packages/types/src/extrinsic/v5/GeneralExtrinsic.spec.ts index 7e29df4a5f5d..40a0f4badb0e 100644 --- a/packages/types/src/extrinsic/v5/GeneralExt.spec.ts +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsic.spec.ts @@ -20,7 +20,7 @@ describe('GeneralExt', (): void => { it('Can decode a general extrinsic', (): void => { const genExt = new GeneralExtrinsic(registry, extrinsic); - expect(genExt.version).toEqual(69); + expect(genExt.version).toEqual(5); expect(genExt.transactionExtensionVersion.toNumber()).toEqual(0); expect(genExt.method.toHuman()).toEqual({ args: { dest: { Id: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY' }, value: '10,000,000,000' }, method: 'transferAllowDeath', section: 'balances' }); expect(genExt.era.toHuman()).toEqual({ MortalEra: { period: '64', phase: '6' } }); diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts index d7841d6dcf80..5bddc02a1998 100644 --- a/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts @@ -39,6 +39,7 @@ function decodeU8a (u8a: Uint8Array) { export class GeneralExtrinsic extends Struct { #version: number; + #preamble: number; constructor (registry: Registry, value?: GeneralExtrinsicValue | Uint8Array | HexString) { const extTypes = registry.getSignedExtensionTypes(); @@ -62,8 +63,8 @@ export class GeneralExtrinsic extends Struct { } ), GeneralExtrinsic.decodeExtrinsic(registry, value)); - // TODO check version and error if version !== 0b01000101 || 69 - this.#version = 0b01000101; // Includes Preamble + this.#version = 0b00000101; // Includes Preamble + this.#preamble = 0b01000000; } public static decodeExtrinsic (registry: Registry, value?: GeneralExtrinsicValue | Uint8Array | HexString) { @@ -124,6 +125,10 @@ export class GeneralExtrinsic extends Struct { return this.#version; } + public get preamble () { + return this.#preamble + } + public override toHex (isBare?: boolean): HexString { return u8aToHex(this.toU8a(isBare)); } @@ -139,6 +144,6 @@ export class GeneralExtrinsic extends Struct { } public encode () { - return u8aConcat(new Uint8Array([this.version]), super.toU8a()); + return u8aConcat(new Uint8Array([this.version | this.preamble]), super.toU8a()); } } From ccd38d4866dbb3d332c8058b3a7cc8402d8a9fdb Mon Sep 17 00:00:00 2001 From: tarikgul Date: Tue, 1 Oct 2024 20:54:04 -0400 Subject: [PATCH 56/71] fixes --- packages/types/src/extrinsic/Extrinsic.ts | 2 +- packages/types/src/extrinsic/v5/GeneralExtrinsic.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index a5ac787d7947..5f2057222efa 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -107,7 +107,7 @@ function decodeU8a (registry: Registry, value: Uint8Array, version: number, prea abstract class ExtrinsicBase extends AbstractBase { readonly #preamble: Preamble; - constructor (registry: Registry, value: ExtrinsicV5 | ExtrinsicUnknown, initialU8aLength?: number, preamble?: Preamble) { + constructor (registry: Registry, value: ExtrinsicVx | ExtrinsicUnknown, initialU8aLength?: number, preamble?: Preamble) { super(registry, value, initialU8aLength); const signKeys = Object.keys(registry.getSignedExtensionTypes()); diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts index 5bddc02a1998..187210d21d1c 100644 --- a/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts @@ -126,7 +126,7 @@ export class GeneralExtrinsic extends Struct { } public get preamble () { - return this.#preamble + return this.#preamble; } public override toHex (isBare?: boolean): HexString { From aefc3dfdc0c294ddc52c7a7dcc14164e51e1bbfc Mon Sep 17 00:00:00 2001 From: tarikgul Date: Tue, 1 Oct 2024 21:18:24 -0400 Subject: [PATCH 57/71] add parent class methods as errors --- .../types/src/extrinsic/v5/GeneralExtrinsic.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts index 187210d21d1c..95d54f5a79d7 100644 --- a/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts @@ -146,4 +146,20 @@ export class GeneralExtrinsic extends Struct { public encode () { return u8aConcat(new Uint8Array([this.version | this.preamble]), super.toU8a()); } + + public signFake () { + throw new Error('Extrinsic: Type GeneralExtrinsic does not have signFake implemented'); + } + + public addSignature () { + throw new Error('Extrinsic: Type GeneralExtrinsic does not have addSignature implemented'); + } + + public sign () { + throw new Error('Extrinsic: Type GeneralExtrinsic does not have sign implemented'); + } + + public signature () { + throw new Error('Extrinsic: Type GeneralExtrinsic does not have the signature getter'); + } } From 0d908bf03e576902e630fc89c4166c4f62d906cd Mon Sep 17 00:00:00 2001 From: tarikgul Date: Tue, 1 Oct 2024 21:18:37 -0400 Subject: [PATCH 58/71] Reorg some types --- packages/types/src/extrinsic/Extrinsic.ts | 66 +++++++++++++++++------ 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 5f2057222efa..0af44af4f315 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -3,7 +3,8 @@ import type { AnyJson, AnyTuple, AnyU8a, ArgsDef, IMethod, Inspect, IOption } from '@polkadot/types-codec/types'; import type { HexString } from '@polkadot/util/types'; -import type { EcdsaSignature, Ed25519Signature, ExtrinsicUnknown, ExtrinsicV5, Sr25519Signature } from '../interfaces/extrinsics/index.js'; +import type { GeneralExtrinsic } from '../index.types.js'; +import type { EcdsaSignature, Ed25519Signature, ExtrinsicSignatureV5, ExtrinsicUnknown, ExtrinsicV5, Sr25519Signature } from '../interfaces/extrinsics/index.js'; import type { FunctionMetadataLatest } from '../interfaces/metadata/index.js'; import type { Address, Call, CodecHash, Hash } from '../interfaces/runtime/index.js'; import type { MultiLocation } from '../interfaces/types.js'; @@ -25,7 +26,7 @@ interface CreateOptions { // NOTE The following 2 types, as well as the VERSION structure and the latest export // is to be changed with the addition of a new extrinsic version -type ExtrinsicVx = ExtrinsicV5; +type ExtrinsicVx = ExtrinsicV5 | GeneralExtrinsic; type ExtrinsicValue = ExtrinsicValueV5; const VERSIONS = [ @@ -111,15 +112,24 @@ abstract class ExtrinsicBase extends AbstractBase this.inner.signature[key as 'signer']; - // This is on the abstract class, ensuring that hasOwnProperty operates - // correctly, i.e. it needs to be on the base class exposing it - for (let i = 0, count = signKeys.length; i < count; i++) { - objectProperty(this, signKeys[i], getter); + if (this.version === 5 && preamble !== 'general') { + const getter = (key: string) => (this.inner.signature as unknown as ExtrinsicSignatureV5)[key as 'signer']; + + // This is on the abstract class, ensuring that hasOwnProperty operates + // correctly, i.e. it needs to be on the base class exposing it + for (let i = 0, count = signKeys.length; i < count; i++) { + objectProperty(this, signKeys[i], getter); + } } - this.#preamble = preamble || DEFAULT_PREAMBLE; + const unmaskedPreamble = this.type & TYPE_MASK; + + this.#preamble = preamble || preambleUnMask[`${unmaskedPreamble}`]; + } + + public isGeneral () { + return this.#preamble === 'general'; } /** @@ -154,7 +164,9 @@ abstract class ExtrinsicBase extends AbstractBase extends AbstractBase extends AbstractBase { - return this.inner.signature.nonce; + return this.isGeneral() + ? (this.inner as unknown as GeneralExtrinsic).nonce + : (this.inner.signature as unknown as ExtrinsicSignatureV5).nonce; } /** * @description The actual [[EcdsaSignature]], [[Ed25519Signature]] or [[Sr25519Signature]] */ public get signature (): EcdsaSignature | Ed25519Signature | Sr25519Signature { - return this.inner.signature.signature; + if (this.isGeneral()) { + throw new Error('Extrinsic: GeneralExtrinsic does not have signature implemented'); + } + + return (this.inner.signature as unknown as ExtrinsicSignatureV5).signature; } /** * @description The [[Address]] that signed */ public get signer (): Address { - return this.inner.signature.signer; + if (this.isGeneral()) { + throw new Error('Extrinsic: GeneralExtrinsic does not have signer implemented'); + } + + return (this.inner.signature as unknown as ExtrinsicSignatureV5).signer; } /** * @description Forwards compat */ public get tip (): ICompact { - return this.inner.signature.tip; + return this.isGeneral() + ? (this.inner as unknown as GeneralExtrinsic).tip + : (this.inner.signature as unknown as ExtrinsicSignatureV5).tip; } /** * @description Forward compat */ public get assetId (): IOption { - return this.inner.signature.assetId; + return this.isGeneral() + ? (this.inner as unknown as GeneralExtrinsic).assetId + : (this.inner.signature as unknown as ExtrinsicSignatureV5).assetId; } /** * @description Forward compat */ public get metadataHash (): IOption { - return this.inner.signature.metadataHash; + return this.isGeneral() + ? (this.inner as unknown as GeneralExtrinsic).metadataHash + : (this.inner.signature as unknown as ExtrinsicSignatureV5).metadataHash; } /** * @description Forward compat */ public get mode (): INumber { - return this.inner.signature.mode; + return this.isGeneral() + ? (this.inner as unknown as GeneralExtrinsic).mode + : (this.inner.signature as unknown as ExtrinsicSignatureV5).mode; } /** From 44855854b5ea0b5eb2bf708dfb08f41a8c0ae82e Mon Sep 17 00:00:00 2001 From: tarikgul Date: Tue, 1 Oct 2024 22:15:14 -0400 Subject: [PATCH 59/71] Add test for GeneralExtrinsic --- .../types/src/extrinsic/Extrinsic.spec.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/types/src/extrinsic/Extrinsic.spec.ts b/packages/types/src/extrinsic/Extrinsic.spec.ts index d8aeaa53dc3c..398a7887e996 100644 --- a/packages/types/src/extrinsic/Extrinsic.spec.ts +++ b/packages/types/src/extrinsic/Extrinsic.spec.ts @@ -67,5 +67,27 @@ describe('Extrinsic', (): void => { expect(extrinsic.args[0].toHex()).toEqual('0x008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48'); expect(extrinsic.args[1].toHuman()).toEqual('10,000,000,000,000'); }); + + it('General Extrinsic', () => { + // Ensure it does not have its registry modified by the fallback extensions. + const registry = new TypeRegistry(); + const metadata = new Metadata(registry, rpcMetadata); + + registry.setMetadata(metadata); + const extrinsic = new Extrinsic( + registry, + '0xe90145006500000000000c01000002000000fc39f7510a4c591e688532a6df54e856a77e92aee8a2372f0f194eea11c487393d5c33fd9c2370dfcb4941c7fe85fc19c31e5c9b0bd5ecb74d3923947c9c5ccf00060000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d0700e40b5402', + { preamble: 'general', version: 5 } + ); + + expect(extrinsic.version).toEqual(69); + // expect(extrinsic.transactionExtensionVersion.toNumber()).toEqual(0); + expect(extrinsic.method.toHuman()).toEqual({ args: { dest: { Id: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY' }, value: '10,000,000,000' }, method: 'transferAllowDeath', section: 'balances' }); + expect(extrinsic.era.toHuman()).toEqual({ MortalEra: { period: '64', phase: '6' } }); + expect(extrinsic.tip.toNumber()).toEqual(0); + expect(extrinsic.mode.toNumber()).toEqual(0); + expect(extrinsic.assetId.toHuman()).toEqual(null); + expect(extrinsic.nonce.toNumber()).toEqual(0); + }); }); }); From 104c58ee16f15f6161c7c80b4913496d3265d4e3 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Tue, 1 Oct 2024 22:15:53 -0400 Subject: [PATCH 60/71] Fix compatibility with GeneralExtrinsic --- packages/types/src/extrinsic/Extrinsic.ts | 11 ++++++++--- packages/types/src/extrinsic/v5/GeneralExtrinsic.ts | 11 ++--------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 0af44af4f315..1f42ae6c859f 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -16,7 +16,7 @@ import type { ExtrinsicValueV5 } from './v5/Extrinsic.js'; import { AbstractBase } from '@polkadot/types-codec'; import { compactAddLength, compactFromU8a, compactToU8a, isHex, isU8a, objectProperty, objectSpread, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; -import { BARE_EXTRINSIC, BIT_SIGNED, BIT_UNSIGNED, DEFAULT_PREAMBLE, GENERAL_EXTRINSIC, LATEST_EXTRINSIC_VERSION, LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION, SIGNED_EXTRINSIC, TYPE_MASK, UNMASK_VERSION } from './constants.js'; +import { BARE_EXTRINSIC, BIT_SIGNED, BIT_UNSIGNED, DEFAULT_PREAMBLE, GENERAL_EXTRINSIC, LATEST_EXTRINSIC_VERSION, LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION, SIGNED_EXTRINSIC, TYPE_MASK, VERSION_MASK } from './constants.js'; interface CreateOptions { version?: number; @@ -68,7 +68,7 @@ function newFromValue (registry: Registry, value: any, version: number, preamble } const isSigned = (version & BIT_SIGNED) === BIT_SIGNED; - const type = (version & UNMASK_VERSION) === 5 ? PREAMBLE[preamble] : VERSIONS[version & UNMASK_VERSION] || VERSIONS[0]; + const type = (version & VERSION_MASK) === 5 ? PREAMBLE[preamble] : VERSIONS[version & VERSION_MASK] || VERSIONS[0]; // we cast here since the VERSION definition is incredibly broad - we don't have a // slice for "only add extrinsic types", and more string definitions become unwieldy @@ -102,7 +102,12 @@ function decodeU8a (registry: Registry, value: Uint8Array, version: number, prea const data = value.subarray(offset, total); const unmaskedPreamble = data[0] & TYPE_MASK; - return newFromValue(registry, data.subarray(1), data[0], preambleUnMask[`${unmaskedPreamble}`] || preamble); + if (preambleUnMask[`${unmaskedPreamble}`] === 'general') { + // NOTE: GeneralExtrinsic needs to have the full data to validate the preamble and version + return newFromValue(registry, value, data[0], preambleUnMask[`${unmaskedPreamble}`] || preamble); + } else { + return newFromValue(registry, data.subarray(1), data[0], preambleUnMask[`${unmaskedPreamble}`] || preamble); + } } abstract class ExtrinsicBase extends AbstractBase { diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts index 95d54f5a79d7..de7edf6deb13 100644 --- a/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts @@ -41,20 +41,13 @@ export class GeneralExtrinsic extends Struct { #version: number; #preamble: number; - constructor (registry: Registry, value?: GeneralExtrinsicValue | Uint8Array | HexString) { + constructor (registry: Registry, value?: GeneralExtrinsicValue | Uint8Array | HexString, opt?: { version: number }) { const extTypes = registry.getSignedExtensionTypes(); const extraTypes = registry.getSignedExtensionExtra(); - // Input ordering - // TransactionExtensionVersion - // Call data as method - // Payload super(registry, objectSpread( { - // eslint-disable-next-line sort-keys transactionExtensionVersion: 'u8' - // eslint-disable-next-line sort-keys - }, extTypes, extraTypes, @@ -63,7 +56,7 @@ export class GeneralExtrinsic extends Struct { } ), GeneralExtrinsic.decodeExtrinsic(registry, value)); - this.#version = 0b00000101; // Includes Preamble + this.#version = opt?.version || 0b00000101; this.#preamble = 0b01000000; } From 7601b941cda08fba7d691037c81510e82c4dcec1 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Wed, 2 Oct 2024 08:41:43 -0400 Subject: [PATCH 61/71] fix structure of GeneralExtrinsic --- .../types/src/extrinsic/Extrinsic.spec.ts | 2 +- .../src/extrinsic/v5/GeneralExtrinsic.spec.ts | 5 +--- .../src/extrinsic/v5/GeneralExtrinsic.ts | 25 ++++++++++++++----- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/packages/types/src/extrinsic/Extrinsic.spec.ts b/packages/types/src/extrinsic/Extrinsic.spec.ts index 398a7887e996..32acdc50ab3b 100644 --- a/packages/types/src/extrinsic/Extrinsic.spec.ts +++ b/packages/types/src/extrinsic/Extrinsic.spec.ts @@ -76,7 +76,7 @@ describe('Extrinsic', (): void => { registry.setMetadata(metadata); const extrinsic = new Extrinsic( registry, - '0xe90145006500000000000c01000002000000fc39f7510a4c591e688532a6df54e856a77e92aee8a2372f0f194eea11c487393d5c33fd9c2370dfcb4941c7fe85fc19c31e5c9b0bd5ecb74d3923947c9c5ccf00060000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d0700e40b5402', + '0xc44500650000000000060000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d0700e40b5402', { preamble: 'general', version: 5 } ); diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsic.spec.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsic.spec.ts index 40a0f4badb0e..2691e8c79ef7 100644 --- a/packages/types/src/extrinsic/v5/GeneralExtrinsic.spec.ts +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsic.spec.ts @@ -15,7 +15,7 @@ const metadata = new Metadata(registry, rpcMetadata); registry.setMetadata(metadata); describe('GeneralExt', (): void => { - const extrinsic = '0xe90145006500000000000c01000002000000fc39f7510a4c591e688532a6df54e856a77e92aee8a2372f0f194eea11c487393d5c33fd9c2370dfcb4941c7fe85fc19c31e5c9b0bd5ecb74d3923947c9c5ccf00060000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d0700e40b5402'; + const extrinsic = '0xc44500650000000000060000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d0700e40b5402'; it('Can decode a general extrinsic', (): void => { const genExt = new GeneralExtrinsic(registry, extrinsic); @@ -32,12 +32,9 @@ describe('GeneralExt', (): void => { it('Can encode a general extrinsic', (): void => { const payload = { - blockHash: '0x3d5c33fd9c2370dfcb4941c7fe85fc19c31e5c9b0bd5ecb74d3923947c9c5ccf', era: '0x6500', - genesisHash: '0xfc39f7510a4c591e688532a6df54e856a77e92aee8a2372f0f194eea11c48739', method: '0x060000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d0700e40b5402', nonce: '0x00000000', - specVersion: '0x0000010c', tip: '0x00000000000000000000000000000000', transactionVersion: '0x00000002' }; diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts index de7edf6deb13..b59e94fbd04a 100644 --- a/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts @@ -2,16 +2,31 @@ // SPDX-License-Identifier: Apache-2.0 import type { Call, ExtrinsicEra, Hash, MultiLocation } from '@polkadot/types/interfaces'; -import type { ExtrinsicPayloadValue, ICompact, INumber, IOption, Registry } from '@polkadot/types/types'; +import type { AnyNumber, AnyU8a, ICompact, IExtrinsicEra, INumber, IOption, Registry } from '@polkadot/types/types'; +import type { AnyTuple, IMethod } from '@polkadot/types-codec/types'; import type { HexString } from '@polkadot/util/types'; import { Struct } from '@polkadot/types-codec'; import { compactAddLength, compactFromU8a, isHex, isObject, isU8a, objectSpread, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; -import { EMPTY_U8A } from '../constants.js'; +import { EMPTY_U8A, UNMASK_VERSION } from '../constants.js'; + +interface TransactionExtensionValues { + era: AnyU8a | IExtrinsicEra; + nonce: AnyNumber; + tip: AnyNumber; + transactionVersion: AnyNumber; + assetId?: HexString; + mode?: AnyNumber; + metadataHash?: AnyU8a; +} + +interface GeneralExtrinsicPayloadValues extends TransactionExtensionValues { + method: AnyU8a | IMethod; +} interface GeneralExtrinsicValue { - payload?: ExtrinsicPayloadValue; + payload?: GeneralExtrinsicPayloadValues; transactionExtensionVersion?: number; } @@ -31,7 +46,7 @@ function decodeU8a (u8a: Uint8Array) { // 69 denotes 0b01000101 which is the version and preamble for this Extrinsic if (data[0] !== 69) { - throw new Error(`Extrinsic: incorrect version for General Transactions, expected 5, found ${data[0] & 0b01111111}`); + throw new Error(`Extrinsic: incorrect version for General Transactions, expected 5, found ${data[0] & UNMASK_VERSION}`); } return data.subarray(1); @@ -43,14 +58,12 @@ export class GeneralExtrinsic extends Struct { constructor (registry: Registry, value?: GeneralExtrinsicValue | Uint8Array | HexString, opt?: { version: number }) { const extTypes = registry.getSignedExtensionTypes(); - const extraTypes = registry.getSignedExtensionExtra(); super(registry, objectSpread( { transactionExtensionVersion: 'u8' }, extTypes, - extraTypes, { method: 'Call' } From 10b81c1f8963688035c220fa5f68391c755b2741 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Wed, 2 Oct 2024 08:54:51 -0400 Subject: [PATCH 62/71] Add another test --- .../types/src/extrinsic/Extrinsic.spec.ts | 47 +++++++++++++------ 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/packages/types/src/extrinsic/Extrinsic.spec.ts b/packages/types/src/extrinsic/Extrinsic.spec.ts index 32acdc50ab3b..25796f63c842 100644 --- a/packages/types/src/extrinsic/Extrinsic.spec.ts +++ b/packages/types/src/extrinsic/Extrinsic.spec.ts @@ -68,26 +68,45 @@ describe('Extrinsic', (): void => { expect(extrinsic.args[1].toHuman()).toEqual('10,000,000,000,000'); }); - it('General Extrinsic', () => { + describe('GeneralExtrinsic', () => { // Ensure it does not have its registry modified by the fallback extensions. const registry = new TypeRegistry(); const metadata = new Metadata(registry, rpcMetadata); registry.setMetadata(metadata); - const extrinsic = new Extrinsic( - registry, - '0xc44500650000000000060000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d0700e40b5402', - { preamble: 'general', version: 5 } - ); - expect(extrinsic.version).toEqual(69); - // expect(extrinsic.transactionExtensionVersion.toNumber()).toEqual(0); - expect(extrinsic.method.toHuman()).toEqual({ args: { dest: { Id: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY' }, value: '10,000,000,000' }, method: 'transferAllowDeath', section: 'balances' }); - expect(extrinsic.era.toHuman()).toEqual({ MortalEra: { period: '64', phase: '6' } }); - expect(extrinsic.tip.toNumber()).toEqual(0); - expect(extrinsic.mode.toNumber()).toEqual(0); - expect(extrinsic.assetId.toHuman()).toEqual(null); - expect(extrinsic.nonce.toNumber()).toEqual(0); + it('Should work when the version and preamble is passed in', () => { + const extrinsic = new Extrinsic( + registry, + '0xc44500650000000000060000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d0700e40b5402', + { preamble: 'general', version: 5 } + ); + + expect(extrinsic.version).toEqual(69); + // expect(extrinsic.transactionExtensionVersion.toNumber()).toEqual(0); + expect(extrinsic.method.toHuman()).toEqual({ args: { dest: { Id: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY' }, value: '10,000,000,000' }, method: 'transferAllowDeath', section: 'balances' }); + expect(extrinsic.era.toHuman()).toEqual({ MortalEra: { period: '64', phase: '6' } }); + expect(extrinsic.tip.toNumber()).toEqual(0); + expect(extrinsic.mode.toNumber()).toEqual(0); + expect(extrinsic.assetId.toHuman()).toEqual(null); + expect(extrinsic.nonce.toNumber()).toEqual(0); + }); + + it('Should work when there is no version and preamble is passed in', () => { + const extrinsic = new Extrinsic( + registry, + '0xc44500650000000000060000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d0700e40b5402' + ); + + expect(extrinsic.version).toEqual(69); + // expect(extrinsic.transactionExtensionVersion.toNumber()).toEqual(0); + expect(extrinsic.method.toHuman()).toEqual({ args: { dest: { Id: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY' }, value: '10,000,000,000' }, method: 'transferAllowDeath', section: 'balances' }); + expect(extrinsic.era.toHuman()).toEqual({ MortalEra: { period: '64', phase: '6' } }); + expect(extrinsic.tip.toNumber()).toEqual(0); + expect(extrinsic.mode.toNumber()).toEqual(0); + expect(extrinsic.assetId.toHuman()).toEqual(null); + expect(extrinsic.nonce.toNumber()).toEqual(0); + }); }); }); }); From 6d8baed63bac7aa1a94c85db9bb3591606086761 Mon Sep 17 00:00:00 2001 From: bee344 Date: Wed, 2 Oct 2024 10:30:34 -0300 Subject: [PATCH 63/71] cleanup --- packages/types/src/extrinsic/Extrinsic.ts | 1 - packages/types/src/extrinsic/constants.ts | 2 - packages/types/src/extrinsic/v5/Extrinsic.ts | 5 ++- .../src/extrinsic/v5/ExtrinsicPayload.ts | 2 +- .../src/extrinsic/v5/ExtrinsicSignature.ts | 2 +- .../src/extrinsic/v5/GeneralExtrinsic.ts | 37 +++++++++++++++++++ 6 files changed, 43 insertions(+), 6 deletions(-) diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 1f42ae6c859f..52c74d94c622 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -40,7 +40,6 @@ const VERSIONS = [ const PREAMBLE = { bare: 'ExtrinsicV5', - // Not supported yet general: 'GeneralExtrinsic', signed: 'ExtrinsicV5' }; diff --git a/packages/types/src/extrinsic/constants.ts b/packages/types/src/extrinsic/constants.ts index aff05aef2d3d..5f97b5f25d93 100644 --- a/packages/types/src/extrinsic/constants.ts +++ b/packages/types/src/extrinsic/constants.ts @@ -7,8 +7,6 @@ export const BIT_UNSIGNED = 0; export const EMPTY_U8A = new Uint8Array(); -export const DEFAULT_VERSION = 4; - export const IMMORTAL_ERA = new Uint8Array([0]); export const UNMASK_VERSION = 0b01111111; diff --git a/packages/types/src/extrinsic/v5/Extrinsic.ts b/packages/types/src/extrinsic/v5/Extrinsic.ts index 5d7d35491c78..7c1ee68b1f3d 100644 --- a/packages/types/src/extrinsic/v5/Extrinsic.ts +++ b/packages/types/src/extrinsic/v5/Extrinsic.ts @@ -20,7 +20,7 @@ export interface ExtrinsicValueV5 { /** * @name GenericExtrinsicV5 * @description - * The third generation of compact extrinsics + * The fourth generation of compact extrinsics */ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { constructor (registry: Registry, value?: Uint8Array | ExtrinsicValueV5 | Call, { isSigned }: Partial = {}) { @@ -80,6 +80,9 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { return EXTRINSIC_VERSION; } + /** + * @description The [[Preamble]] for the extrinsic + */ public get preamble (): Preamble { return this.getT('preamble'); } diff --git a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts index 3cc32fc50b5a..1d2222b3eb0f 100644 --- a/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts +++ b/packages/types/src/extrinsic/v5/ExtrinsicPayload.ts @@ -110,7 +110,7 @@ export class GenericExtrinsicPayloadV5 extends Struct { } /** - * @description The (optional) asset id for this signature for chains that support transaction fees in assets + * @description The (optional) metadataHash proof for the CheckMetadataHash TransactionExtension */ public get metadataHash (): IOption { return this.getT('metadataHash'); diff --git a/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts b/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts index 7ab070ff1876..9191850e47c8 100644 --- a/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts +++ b/packages/types/src/extrinsic/v5/ExtrinsicSignature.ts @@ -137,7 +137,7 @@ export class GenericExtrinsicSignatureV5 extends Struct implements IExtrinsicSig } /** - * @description The [[Hash]] for the metadata + * @description The (optional) [[Hash]] for the metadata proof */ public get metadataHash (): IOption { return this.getT('metadataHash'); diff --git a/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts b/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts index b59e94fbd04a..d1ba6b225618 100644 --- a/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts +++ b/packages/types/src/extrinsic/v5/GeneralExtrinsic.ts @@ -91,46 +91,79 @@ export class GeneralExtrinsic extends Struct { return {}; } + /** + * @description The length of the value when encoded as a Uint8Array + */ public override get encodedLength (): number { return super.encodedLength; } + /** + * @description The [[ExtrinsicEra]] + */ public get era (): ExtrinsicEra { return this.getT('era'); } + /** + * @description The [[Index]] + */ public get nonce (): ICompact { return this.getT('nonce'); } + /** + * @description The tip [[Balance]] + */ public get tip (): ICompact { return this.getT('tip'); } + /** + * @description The (optional) asset id for this signature for chains that support transaction fees in assets + */ public get assetId (): IOption { return this.getT('assetId'); } + /** + * @description The mode used for the CheckMetadataHash TransactionExtension + */ public get mode (): INumber { return this.getT('mode'); } + /** + * @description The (optional) [[Hash]] for the metadata proof + */ public get metadataHash (): IOption { return this.getT('metadataHash'); } + /** + * @description The version of the TransactionExtensions used in this extrinsic + */ public get transactionExtensionVersion (): INumber { return this.getT('transactionExtensionVersion'); } + /** + * @description The [[Call]] this extrinsic wraps + */ public get method (): Call { return this.getT('method'); } + /** + * @description The extrinsic's version + */ public get version () { return this.#version; } + /** + * @description The [[Preamble]] for the extrinsic + */ public get preamble () { return this.#preamble; } @@ -149,6 +182,10 @@ export class GeneralExtrinsic extends Struct { return 'GeneralExt'; } + /** + * + * @description Returns an encoded GeneralExtrinsic + */ public encode () { return u8aConcat(new Uint8Array([this.version | this.preamble]), super.toU8a()); } From eb7b309554a8cf8e5efe758786a194ced8ef6180 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Wed, 2 Oct 2024 11:55:38 -0400 Subject: [PATCH 64/71] Fix decoding bug for bare --- packages/types/src/extrinsic/v5/Extrinsic.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/types/src/extrinsic/v5/Extrinsic.ts b/packages/types/src/extrinsic/v5/Extrinsic.ts index 7c1ee68b1f3d..66bc083143e8 100644 --- a/packages/types/src/extrinsic/v5/Extrinsic.ts +++ b/packages/types/src/extrinsic/v5/Extrinsic.ts @@ -41,7 +41,7 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { // here we decode manually since we need to pull through the version information const signature = registry.createTypeUnsafe('ExtrinsicSignatureV5', [value, { isSigned }]); // We add 2 here since the length of the TransactionExtension Version needs to be accounted for - const method = registry.createTypeUnsafe('Call', [value.subarray(signature.encodedLength + 2)]); + const method = registry.createTypeUnsafe('Call', [value.subarray(isSigned ? signature.encodedLength + 2 : signature.encodedLength)]); return { method, From 70d298b252d0c0d56e7f4c7896d3532288207431 Mon Sep 17 00:00:00 2001 From: bee344 Date: Wed, 2 Oct 2024 13:43:43 -0300 Subject: [PATCH 65/71] fix change from bare to signed --- packages/types/src/extrinsic/Extrinsic.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 52c74d94c622..8b625130c52f 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -295,7 +295,7 @@ abstract class ExtrinsicBase extends AbstractBase Date: Wed, 2 Oct 2024 13:47:35 -0300 Subject: [PATCH 66/71] linting --- packages/types/src/extrinsic/Extrinsic.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 8b625130c52f..2507b57e89df 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -295,7 +295,7 @@ abstract class ExtrinsicBase extends AbstractBase Date: Wed, 2 Oct 2024 14:53:44 -0400 Subject: [PATCH 67/71] Fix encodinglength --- packages/types/src/extrinsic/v5/Extrinsic.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/types/src/extrinsic/v5/Extrinsic.ts b/packages/types/src/extrinsic/v5/Extrinsic.ts index 66bc083143e8..473732ce58af 100644 --- a/packages/types/src/extrinsic/v5/Extrinsic.ts +++ b/packages/types/src/extrinsic/v5/Extrinsic.ts @@ -41,7 +41,7 @@ export class GenericExtrinsicV5 extends Struct implements IExtrinsicV5Impl { // here we decode manually since we need to pull through the version information const signature = registry.createTypeUnsafe('ExtrinsicSignatureV5', [value, { isSigned }]); // We add 2 here since the length of the TransactionExtension Version needs to be accounted for - const method = registry.createTypeUnsafe('Call', [value.subarray(isSigned ? signature.encodedLength + 2 : signature.encodedLength)]); + const method = registry.createTypeUnsafe('Call', [value.subarray(signature.encodedLength)]); return { method, From 51d7232e14a473d0bc06048860785efae0d99124 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Wed, 2 Oct 2024 14:54:02 -0400 Subject: [PATCH 68/71] Set default to preamble to bre --- packages/types/src/extrinsic/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/types/src/extrinsic/constants.ts b/packages/types/src/extrinsic/constants.ts index 5f97b5f25d93..19811962054a 100644 --- a/packages/types/src/extrinsic/constants.ts +++ b/packages/types/src/extrinsic/constants.ts @@ -11,7 +11,7 @@ export const IMMORTAL_ERA = new Uint8Array([0]); export const UNMASK_VERSION = 0b01111111; -export const DEFAULT_PREAMBLE = 'signed'; +export const DEFAULT_PREAMBLE = 'bare'; // Latest extrinsic version is v5, which has backwards compatibility for v4 signed extrinsics export const LATEST_EXTRINSIC_VERSION = 5; From 67b23700988d77fd3762d9110c2df7f632186aea Mon Sep 17 00:00:00 2001 From: tarikgul Date: Wed, 2 Oct 2024 14:56:33 -0400 Subject: [PATCH 69/71] Fix tests --- packages/types/src/extrinsic/Extrinsic.spec.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/types/src/extrinsic/Extrinsic.spec.ts b/packages/types/src/extrinsic/Extrinsic.spec.ts index 25796f63c842..77b6dcac1df4 100644 --- a/packages/types/src/extrinsic/Extrinsic.spec.ts +++ b/packages/types/src/extrinsic/Extrinsic.spec.ts @@ -50,9 +50,13 @@ describe('Extrinsic', (): void => { }); describe('V5', () => { - it('Signed Extrinsic', () => { - registry.setSignedExtensions(fallbackExtensions); + // Ensure it does not have its registry modified by the fallback extensions. + const registry = new TypeRegistry(); + const metadata = new Metadata(registry, rpcMetadata); + + registry.setMetadata(metadata); + it('Signed Extrinsic', () => { const extrinsic = new Extrinsic( registry, '0x51028500d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d011e0b7d9438899333c50121f8e10144952d51c3bb8d0ea11dd1f24940d8ff615ad351d95ed9f41f078748ed7cf182864a20b38eebfaef6629433365eb90c0148c007502000000000603008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480b00a0724e1809', @@ -69,12 +73,6 @@ describe('Extrinsic', (): void => { }); describe('GeneralExtrinsic', () => { - // Ensure it does not have its registry modified by the fallback extensions. - const registry = new TypeRegistry(); - const metadata = new Metadata(registry, rpcMetadata); - - registry.setMetadata(metadata); - it('Should work when the version and preamble is passed in', () => { const extrinsic = new Extrinsic( registry, From 9bcca0b920682267444ee1b5da31eea87a1d7cd2 Mon Sep 17 00:00:00 2001 From: bee344 Date: Wed, 2 Oct 2024 16:01:57 -0300 Subject: [PATCH 70/71] fixed version setting --- packages/types/src/extrinsic/Extrinsic.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 2507b57e89df..a1d17faa4ac0 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -295,7 +295,7 @@ abstract class ExtrinsicBase extends AbstractBase Date: Wed, 2 Oct 2024 15:11:10 -0400 Subject: [PATCH 71/71] Add test --- .../types/src/extrinsic/Extrinsic.spec.ts | 44 +++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/packages/types/src/extrinsic/Extrinsic.spec.ts b/packages/types/src/extrinsic/Extrinsic.spec.ts index 77b6dcac1df4..5ff4a5dccb71 100644 --- a/packages/types/src/extrinsic/Extrinsic.spec.ts +++ b/packages/types/src/extrinsic/Extrinsic.spec.ts @@ -56,20 +56,38 @@ describe('Extrinsic', (): void => { registry.setMetadata(metadata); - it('Signed Extrinsic', () => { - const extrinsic = new Extrinsic( - registry, - '0x51028500d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d011e0b7d9438899333c50121f8e10144952d51c3bb8d0ea11dd1f24940d8ff615ad351d95ed9f41f078748ed7cf182864a20b38eebfaef6629433365eb90c0148c007502000000000603008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480b00a0724e1809', - { preamble: 'signed', version: 5 } - ); + describe('SignedExtrinsic', () => { + it('Should work when the version and preamble is passed in', () => { + const extrinsic = new Extrinsic( + registry, + '0x51028500d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d011e0b7d9438899333c50121f8e10144952d51c3bb8d0ea11dd1f24940d8ff615ad351d95ed9f41f078748ed7cf182864a20b38eebfaef6629433365eb90c0148c007502000000000603008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480b00a0724e1809', + { preamble: 'signed', version: 5 } + ); + + expect(extrinsic.signer.toString()).toEqual('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'); + expect(extrinsic.era.toHuman()).toEqual({ MortalEra: { period: '64', phase: '39' } }); + expect(extrinsic.nonce.toNumber()).toEqual(0); + expect(extrinsic.tip.toHuman()).toEqual('0'); + expect(extrinsic.callIndex).toEqual(new Uint8Array([6, 3])); + expect(extrinsic.args[0].toHex()).toEqual('0x008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48'); + expect(extrinsic.args[1].toHuman()).toEqual('10,000,000,000,000'); + }); + + it('Should work when the version and preamble is not passed in', () => { + const extrinsic = new Extrinsic( + registry, + '0x51028500d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d011e0b7d9438899333c50121f8e10144952d51c3bb8d0ea11dd1f24940d8ff615ad351d95ed9f41f078748ed7cf182864a20b38eebfaef6629433365eb90c0148c007502000000000603008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480b00a0724e1809' + ); - expect(extrinsic.signer.toString()).toEqual('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'); - expect(extrinsic.era.toHuman()).toEqual({ MortalEra: { period: '64', phase: '39' } }); - expect(extrinsic.nonce.toNumber()).toEqual(0); - expect(extrinsic.tip.toHuman()).toEqual('0'); - expect(extrinsic.callIndex).toEqual(new Uint8Array([6, 3])); - expect(extrinsic.args[0].toHex()).toEqual('0x008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48'); - expect(extrinsic.args[1].toHuman()).toEqual('10,000,000,000,000'); + expect(extrinsic.version).toEqual(133); + expect(extrinsic.signer.toString()).toEqual('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'); + expect(extrinsic.era.toHuman()).toEqual({ MortalEra: { period: '64', phase: '39' } }); + expect(extrinsic.nonce.toNumber()).toEqual(0); + expect(extrinsic.tip.toHuman()).toEqual('0'); + expect(extrinsic.callIndex).toEqual(new Uint8Array([6, 3])); + expect(extrinsic.args[0].toHex()).toEqual('0x008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48'); + expect(extrinsic.args[1].toHuman()).toEqual('10,000,000,000,000'); + }); }); describe('GeneralExtrinsic', () => {