Skip to content
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
b5c95b1
WIP
gui1117 Dec 12, 2024
582cbb9
WIP
gui1117 Dec 15, 2024
32159f9
WIP
gui1117 Dec 15, 2024
2350e93
WIP
gui1117 Dec 16, 2024
143b50a
WIP
gui1117 Dec 16, 2024
17a8dc0
cleanup
gui1117 Dec 16, 2024
a38fac3
WIP
gui1117 Dec 17, 2024
3f657ac
WIP
gui1117 Dec 17, 2024
06da89b
Structure done: todo: renames and format
gui1117 Dec 18, 2024
b03b4eb
Merge remote-tracking branch 'origin/master' into gui-transaction-ext…
gui1117 Dec 23, 2024
cfddfa1
renames
gui1117 Dec 23, 2024
3aacd46
reorganize
gui1117 Dec 24, 2024
e060cac
fixes
gui1117 Dec 24, 2024
2c1e2fc
description draft
gui1117 Dec 24, 2024
d39156e
Merge remote-tracking branch 'origin/master' into gui-transaction-ext…
gui1117 Jan 6, 2025
0cc8f0c
refactor
gui1117 Jan 6, 2025
049ec5d
doc
gui1117 Jan 6, 2025
d9eb5b8
tests + fmt
gui1117 Jan 6, 2025
8e32689
multi version support 26 versions
gui1117 Jan 6, 2025
c6aa890
simplify bounds
gui1117 Jan 7, 2025
bfadf05
refactor test
gui1117 Jan 7, 2025
dc7944f
remove unneeded bounds
gui1117 Jan 7, 2025
ad9e9ba
more tests
gui1117 Jan 7, 2025
1812860
make invalid version safer
gui1117 Jan 8, 2025
9967ab0
Merge remote-tracking branch 'origin/master' into gui-transaction-ext…
gui1117 Jan 8, 2025
082ab90
fmt
gui1117 Jan 8, 2025
a4a87f5
prdoc
gui1117 Jan 8, 2025
b657487
prdoc fmt
gui1117 Jan 8, 2025
4d7e494
semver
gui1117 Jan 8, 2025
0b0d980
remove unused
gui1117 Jan 8, 2025
90b8297
explicit re-export + fix doc
gui1117 Jan 8, 2025
d1bdbd9
explicit reexport
gui1117 Jan 8, 2025
a59da7b
refactor and doc
gui1117 Jan 8, 2025
370bb33
Merge remote-tracking branch 'origin/master' into gui-transaction-ext…
gui1117 Dec 31, 2025
ef48bc7
remove useless type info breaking subxt
gui1117 Jan 1, 2026
f263366
doc
gui1117 Jan 1, 2026
a66d24d
fix rust doc link
gui1117 Jan 1, 2026
ecc7cc7
fix rustdoc test
gui1117 Jan 1, 2026
4b6ef76
better type info
gui1117 Jan 1, 2026
31ea43d
better rename + impl revive
gui1117 Jan 1, 2026
b2621eb
prdoc
gui1117 Jan 1, 2026
8284dc8
fix revive
gui1117 Jan 1, 2026
acf6904
Apply suggestions from code review
gui1117 Jan 9, 2026
4b08c49
Update substrate/frame/support/test/tests/tx_ext_multi_version.rs
gui1117 Jan 9, 2026
316e623
Update substrate/primitives/runtime/src/generic/unchecked_extrinsic.rs
gui1117 Jan 9, 2026
64cfd4c
Apply suggestion from @franciscoaguirre
gui1117 Jan 9, 2026
0071ee2
better doc and comments
gui1117 Jan 9, 2026
e071249
use unreachable
gui1117 Jan 22, 2026
a9942a8
remove metadata ir extension v0 type
gui1117 Jan 22, 2026
a49294c
remove ExtensionV0 type in metadata
gui1117 Jan 22, 2026
5bb9d50
rename
gui1117 Jan 22, 2026
fcafaac
fix unused
gui1117 Jan 25, 2026
7a6eaf0
Merge branch 'master' into gui-transaction-extension-multiple-version
gui1117 Jan 25, 2026
1879c54
fmt
gui1117 Jan 27, 2026
5451991
Update substrate/primitives/runtime/src/traits/vers_tx_ext/mod.rs
gui1117 Mar 15, 2026
cfc4375
Update substrate/frame/revive/src/evm/runtime.rs
gui1117 Mar 15, 2026
22f7bcc
improve comments
gui1117 Mar 15, 2026
2abb369
merge PipelineWeight directly into Pipeline
gui1117 Mar 15, 2026
1d822d9
Merge remote-tracking branch 'origin/master' into gui-transaction-ext…
gui1117 Mar 16, 2026
1a81f0a
extrinsic metadata better doc
gui1117 Mar 16, 2026
c1009cf
fmt + taplo
gui1117 Mar 16, 2026
5b5f23a
Merge branch 'master' into gui-transaction-extension-multiple-version
gui1117 Mar 20, 2026
17c1f8f
fix test: have a correct metadata ir
gui1117 Mar 30, 2026
5c353e4
Merge branch 'master' into gui-transaction-extension-multiple-version
gui1117 Mar 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1439,9 +1439,10 @@ pub struct EthExtraImpl;

impl EthExtra for EthExtraImpl {
type Config = Runtime;
type Extension = TxExtension;
type ExtensionV0 = TxExtension;
type ExtensionOtherVersions = sp_runtime::traits::InvalidVersion;

fn get_eth_extension(nonce: u32, tip: Balance) -> Self::Extension {
fn get_eth_extension(nonce: u32, tip: Balance) -> Self::ExtensionV0 {
(
frame_system::AuthorizeCall::<Runtime>::new(),
frame_system::CheckNonZeroSender::<Runtime>::new(),
Expand Down
5 changes: 3 additions & 2 deletions cumulus/parachains/runtimes/testing/penpal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,10 @@ pub struct EthExtraImpl;

impl EthExtra for EthExtraImpl {
type Config = Runtime;
type Extension = TxExtension;
type ExtensionV0 = TxExtension;
type ExtensionOtherVersions = sp_runtime::traits::InvalidVersion;

fn get_eth_extension(nonce: u32, tip: Balance) -> Self::Extension {
fn get_eth_extension(nonce: u32, tip: Balance) -> Self::ExtensionV0 {
(
frame_system::AuthorizeCall::<Runtime>::new(),
frame_system::CheckNonZeroSender::<Runtime>::new(),
Expand Down
9 changes: 5 additions & 4 deletions docs/mermaid/extrinsics.mmd
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
flowchart TD
E(Extrinsic) ---> I(Inherent);
E --> T(Transaction)
T --> ST("Signed (aka. Transaction)")
T --> UT(Unsigned)
E(Extrinsic) ---> B(Bare);
E --> S(Signed Transaction)
E --> G(General Transaction)
B --> I(Inherent);
B --> U(Unsigned Transaction (deprecated))
57 changes: 57 additions & 0 deletions prdoc/pr_7035.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
title: Allow declaration and usage of multiple transaction extension version in FRAME and primitives
Comment thread
gui1117 marked this conversation as resolved.
Outdated

doc:
- audience: Runtime Dev
description: |
This PR enhance `UncheckedExtrinsic` type with a new optional generic: `ExtensionOtherVersions`.
This generic defaults to `InvalidVersion` meaning there is not other version than the regular version 0. This is the same behavior as before this PR.

# Breaking change

The types `Preamble`, `CheckedExtrinsic` and `ExtrinsicFormat` also have this new optional generic. Their type definition also have changed a bit, the `General` variant was 2 fields, the version and the extension, it is now only one field, the extension, and the version can be retrieve by calling `extension.version()`
Comment thread
gui1117 marked this conversation as resolved.
Outdated

Some trait such as `ExtrinsicMetadata` and `EthExtraImpl` changed their associated type named `Extension` to `ExtensionV0` and have new associated type `ExtensionOtherVersions`. This is because multiple version are now supported.
You can always use `InvalidVersions` for `ExtensionOtherVersions` and keep the old `Extension` for `ExtensionV0` to keep the same behavior as before this PR.
Comment thread
gui1117 marked this conversation as resolved.
Outdated

The type inference for those types may fail because of this PR, to update the code by writing the concrete types.

# New feature

To use this new feature, you can use the new types `TxExtLineAtVers` and `MultiVersion` to define a transaction extension with multiple version:

```rust
pub type TransactionExtensionV0 = ();
pub type TransactionExtensionV4 = ();
pub type TransactionExtensionV7 = ();

pub type OtherVersions = MultiVersion<
TxExtLineAtVers<4, TransactionExtensionV4>;
TxExtLineAtVers<7, TransactionExtensionV7>;
>;

pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<
AccountId,
RuntimeCall,
UintAuthorityId,
TransactionExtensionV0, // The version 0, same as before
OtherVersions, // The other versions.
>;
```

crates:
- name: node-testing
bump: major
- name: pallet-revive
bump: major
- name: frame-support-procedural
bump: major
- name: frame-support
bump: major
- name: frame-support-test
bump: major
- name: sp-runtime
bump: major
- name: sp-metadata-ir
bump: major
- name: pallet-transaction-payment
bump: major
5 changes: 3 additions & 2 deletions substrate/bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2910,9 +2910,10 @@ pub struct EthExtraImpl;

impl EthExtra for EthExtraImpl {
type Config = Runtime;
type Extension = TxExtension;
type ExtensionV0 = TxExtension;
type ExtensionOtherVersions = sp_runtime::traits::InvalidVersion;

fn get_eth_extension(nonce: u32, tip: Balance) -> Self::Extension {
fn get_eth_extension(nonce: u32, tip: Balance) -> Self::ExtensionV0 {
(
frame_system::AuthorizeCall::<Runtime>::new(),
frame_system::CheckNonZeroSender::<Runtime>::new(),
Expand Down
9 changes: 3 additions & 6 deletions substrate/bin/node/testing/src/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -600,12 +600,9 @@ impl BenchKeyring {
.into()
},
ExtrinsicFormat::Bare => generic::UncheckedExtrinsic::new_bare(xt.function).into(),
ExtrinsicFormat::General(ext_version, tx_ext) =>
generic::UncheckedExtrinsic::from_parts(
xt.function,
Preamble::General(ext_version, tx_ext),
)
.into(),
ExtrinsicFormat::General(tx_ext) =>
generic::UncheckedExtrinsic::from_parts(xt.function, Preamble::General(tx_ext))
.into(),
}
}

Expand Down
8 changes: 3 additions & 5 deletions substrate/bin/node/testing/src/keyring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,8 @@ pub fn sign(
.into()
},
ExtrinsicFormat::Bare => generic::UncheckedExtrinsic::new_bare(xt.function).into(),
ExtrinsicFormat::General(ext_version, tx_ext) => generic::UncheckedExtrinsic::from_parts(
xt.function,
generic::Preamble::General(ext_version, tx_ext),
)
.into(),
ExtrinsicFormat::General(tx_ext) =>
generic::UncheckedExtrinsic::from_parts(xt.function, generic::Preamble::General(tx_ext))
.into(),
}
}
5 changes: 3 additions & 2 deletions substrate/frame/revive/dev-node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,10 @@ pub struct EthExtraImpl;

impl EthExtra for EthExtraImpl {
type Config = Runtime;
type Extension = TxExtension;
type ExtensionV0 = TxExtension;
type ExtensionOtherVersions = sp_runtime::traits::InvalidVersion;

fn get_eth_extension(nonce: u32, tip: Balance) -> Self::Extension {
fn get_eth_extension(nonce: u32, tip: Balance) -> Self::ExtensionV0 {
(
frame_system::CheckNonZeroSender::<Runtime>::new(),
frame_system::CheckSpecVersion::<Runtime>::new(),
Expand Down
11 changes: 9 additions & 2 deletions substrate/frame/revive/src/evm/fees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,15 @@ where
<E::Config as frame_system::Config>::RuntimeCall:
Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
CallOf<E::Config>: SetWeightLimit,
<<E::Config as SysConfig>::Block as BlockT>::Extrinsic:
From<UncheckedExtrinsic<Address, CallOf<E::Config>, Signature, E::Extension>>,
<<E::Config as SysConfig>::Block as BlockT>::Extrinsic: From<
UncheckedExtrinsic<
Address,
CallOf<E::Config>,
Signature,
E::ExtensionV0,
E::ExtensionOtherVersions,
>,
>,
<<E::Config as TxConfig>::OnChargeTransaction as TxCreditHold<E::Config>>::Credit:
SuppressedDrop<Inner = CreditOf<E::Config>>,
{
Expand Down
121 changes: 95 additions & 26 deletions substrate/frame/revive/src/evm/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ use sp_runtime::{
generic::{self, CheckedExtrinsic, ExtrinsicFormat},
traits::{
Checkable, ExtrinsicCall, ExtrinsicLike, ExtrinsicMetadata, LazyExtrinsic,
TransactionExtension,
TransactionExtension, VersTxExtLine,
},
transaction_validity::{InvalidTransaction, TransactionValidityError},
Debug, OpaqueExtrinsic, Weight,
Expand All @@ -57,28 +57,58 @@ pub trait SetWeightLimit {
/// [`crate::Call::eth_transact`] extrinsic.
#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Eq, Debug)]
pub struct UncheckedExtrinsic<Address, Signature, E: EthExtra>(
pub generic::UncheckedExtrinsic<Address, CallOf<E::Config>, Signature, E::Extension>,
pub generic::UncheckedExtrinsic<
Comment thread
gui1117 marked this conversation as resolved.
Address,
CallOf<E::Config>,
Signature,
E::ExtensionV0,
E::ExtensionOtherVersions,
>,
);

impl<Address, Signature, E: EthExtra> TypeInfo for UncheckedExtrinsic<Address, Signature, E>
where
Address: StaticTypeInfo,
Signature: StaticTypeInfo,
E::Extension: StaticTypeInfo,
E::ExtensionV0: StaticTypeInfo,
{
type Identity =
generic::UncheckedExtrinsic<Address, CallOf<E::Config>, Signature, E::Extension>;
type Identity = generic::UncheckedExtrinsic<
Address,
CallOf<E::Config>,
Signature,
E::ExtensionV0,
E::ExtensionOtherVersions,
>;
fn type_info() -> scale_info::Type {
generic::UncheckedExtrinsic::<Address, CallOf<E::Config>, Signature, E::Extension>::type_info()
generic::UncheckedExtrinsic::<
Address,
CallOf<E::Config>,
Signature,
E::ExtensionV0,
E::ExtensionOtherVersions,
>::type_info()
}
}

impl<Address, Signature, E: EthExtra>
From<generic::UncheckedExtrinsic<Address, CallOf<E::Config>, Signature, E::Extension>>
for UncheckedExtrinsic<Address, Signature, E>
From<
generic::UncheckedExtrinsic<
Address,
CallOf<E::Config>,
Signature,
E::ExtensionV0,
E::ExtensionOtherVersions,
>,
> for UncheckedExtrinsic<Address, Signature, E>
{
fn from(
utx: generic::UncheckedExtrinsic<Address, CallOf<E::Config>, Signature, E::Extension>,
utx: generic::UncheckedExtrinsic<
Address,
CallOf<E::Config>,
Signature,
E::ExtensionV0,
E::ExtensionOtherVersions,
>,
) -> Self {
Self(utx)
}
Expand All @@ -99,9 +129,17 @@ impl<Address, Signature, E: EthExtra> ExtrinsicMetadata
Address,
CallOf<E::Config>,
Signature,
E::Extension,
E::ExtensionV0,
E::ExtensionOtherVersions,
>::VERSIONS;
type TransactionExtensions = E::Extension;
type TransactionExtensionsV0 = E::ExtensionV0;
type TransactionExtensionsVersions = <generic::UncheckedExtrinsic<
Address,
CallOf<E::Config>,
Signature,
E::ExtensionV0,
E::ExtensionOtherVersions,
> as ExtrinsicMetadata>::TransactionExtensionsVersions;
}

impl<Address: TypeInfo, Signature: TypeInfo, E: EthExtra> ExtrinsicCall
Expand All @@ -126,13 +164,28 @@ where
<E::Config as frame_system::Config>::Nonce: TryFrom<U256>,
CallOf<E::Config>: SetWeightLimit,
// required by Checkable for `generic::UncheckedExtrinsic`
generic::UncheckedExtrinsic<LookupSource, CallOf<E::Config>, Signature, E::Extension>:
Checkable<
Lookup,
Checked = CheckedExtrinsic<AccountIdOf<E::Config>, CallOf<E::Config>, E::Extension>,
generic::UncheckedExtrinsic<
LookupSource,
CallOf<E::Config>,
Signature,
E::ExtensionV0,
E::ExtensionOtherVersions,
>: Checkable<
Lookup,
Checked = CheckedExtrinsic<
AccountIdOf<E::Config>,
CallOf<E::Config>,
E::ExtensionV0,
E::ExtensionOtherVersions,
>,
>,
{
type Checked = CheckedExtrinsic<AccountIdOf<E::Config>, CallOf<E::Config>, E::Extension>;
type Checked = CheckedExtrinsic<
AccountIdOf<E::Config>,
CallOf<E::Config>,
E::ExtensionV0,
E::ExtensionOtherVersions,
>;

fn check(self, lookup: &Lookup) -> Result<Self::Checked, TransactionValidityError> {
if !self.0.is_signed() {
Expand Down Expand Up @@ -190,17 +243,17 @@ impl<Address, Signature, E: EthExtra> SignedTransactionBuilder
where
Address: TypeInfo,
Signature: TypeInfo,
E::Extension: TypeInfo,
E::ExtensionV0: TypeInfo,
{
type Address = Address;
type Signature = Signature;
type Extension = E::Extension;
type Extension = E::ExtensionV0;

fn new_signed_transaction(
call: Self::Call,
signed: Address,
signature: Signature,
tx_ext: E::Extension,
tx_ext: E::ExtensionV0,
) -> Self {
generic::UncheckedExtrinsic::new_signed(call, signed, signature, tx_ext).into()
}
Expand All @@ -210,7 +263,7 @@ impl<Address, Signature, E: EthExtra> InherentBuilder for UncheckedExtrinsic<Add
where
Address: TypeInfo,
Signature: TypeInfo,
E::Extension: TypeInfo,
E::ExtensionV0: TypeInfo,
{
fn new_inherent(call: Self::Call) -> Self {
generic::UncheckedExtrinsic::new_bare(call).into()
Expand All @@ -222,7 +275,7 @@ impl<Address, Signature, E: EthExtra> From<UncheckedExtrinsic<Address, Signature
where
Address: Encode,
Signature: Encode,
E::Extension: Encode,
E::ExtensionV0: Encode,
{
fn from(extrinsic: UncheckedExtrinsic<Address, Signature, E>) -> Self {
extrinsic.0.into()
Expand All @@ -231,7 +284,13 @@ where

impl<Address, Signature, E: EthExtra> LazyExtrinsic for UncheckedExtrinsic<Address, Signature, E>
where
generic::UncheckedExtrinsic<Address, CallOf<E::Config>, Signature, E::Extension>: LazyExtrinsic,
generic::UncheckedExtrinsic<
Address,
CallOf<E::Config>,
Signature,
E::ExtensionV0,
E::ExtensionOtherVersions,
>: LazyExtrinsic,
{
fn decode_unprefixed(data: &[u8]) -> Result<Self, codec::Error> {
Ok(Self(LazyExtrinsic::decode_unprefixed(data)?))
Expand All @@ -243,11 +302,16 @@ pub trait EthExtra {
/// The Runtime configuration.
type Config: Config + TxConfig;

/// The Runtime's transaction extension.
/// The Runtime's transaction extension version 0.
/// It should include at least:
/// - [`frame_system::CheckNonce`] to ensure that the nonce from the Ethereum transaction is
/// correct.
type Extension: TransactionExtension<CallOf<Self::Config>>;
type ExtensionV0: TransactionExtension<CallOf<Self::Config>>;

/// The Runtime's transaction extension versions other than 0.
///
/// Use [`sp_runtime::traits::InvalidVersion`] if no other versions should be supported.
type ExtensionOtherVersions: VersTxExtLine<CallOf<Self::Config>>;

/// Get the transaction extension to apply to an unsigned [`crate::Call::eth_transact`]
/// extrinsic.
Expand All @@ -258,7 +322,7 @@ pub trait EthExtra {
fn get_eth_extension(
nonce: <Self::Config as frame_system::Config>::Nonce,
tip: BalanceOf<Self::Config>,
) -> Self::Extension;
) -> Self::ExtensionV0;

/// Convert the unsigned [`crate::Call::eth_transact`] into a [`CheckedExtrinsic`].
/// and ensure that the fees from the Ethereum transaction correspond to the fees computed from
Expand All @@ -271,7 +335,12 @@ pub trait EthExtra {
payload: &[u8],
encoded_len: usize,
) -> Result<
CheckedExtrinsic<AccountIdOf<Self::Config>, CallOf<Self::Config>, Self::Extension>,
CheckedExtrinsic<
AccountIdOf<Self::Config>,
CallOf<Self::Config>,
Self::ExtensionV0,
Self::ExtensionOtherVersions,
>,
InvalidTransaction,
>
where
Expand Down
Loading
Loading