From e0f8b5576ce9884671b18d118f2100040feb2d6d Mon Sep 17 00:00:00 2001 From: muharem Date: Thu, 24 Oct 2024 14:38:14 +0200 Subject: [PATCH 01/28] init impl --- Cargo.lock | 20 ++ Cargo.toml | 2 + substrate/frame/meta-tx/Cargo.toml | 58 ++++++ substrate/frame/meta-tx/src/lib.rs | 268 +++++++++++++++++++++++++++ substrate/frame/meta-tx/src/mock.rs | 108 +++++++++++ substrate/frame/meta-tx/src/tests.rs | 154 +++++++++++++++ 6 files changed, 610 insertions(+) create mode 100644 substrate/frame/meta-tx/Cargo.toml create mode 100644 substrate/frame/meta-tx/src/lib.rs create mode 100644 substrate/frame/meta-tx/src/mock.rs create mode 100644 substrate/frame/meta-tx/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index a42f5baa4765e..3c972a11b4243 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11943,6 +11943,26 @@ dependencies = [ "sp-weights 27.0.0", ] +[[package]] +name = "pallet-meta-tx" +version = "0.0.1" +dependencies = [ + "docify", + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "pallet-transaction-payment", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core 28.0.0", + "sp-io 30.0.0", + "sp-keyring", + "sp-runtime 31.0.1", + "sp-std 14.0.0", +] + [[package]] name = "pallet-migrations" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index 049de32b54cfc..965269319903d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -370,6 +370,7 @@ members = [ "substrate/frame/membership", "substrate/frame/merkle-mountain-range", "substrate/frame/message-queue", + "substrate/frame/meta-tx", "substrate/frame/metadata-hash-extension", "substrate/frame/migrations", "substrate/frame/mixnet", @@ -938,6 +939,7 @@ pallet-insecure-randomness-collective-flip = { path = "substrate/frame/insecure- pallet-lottery = { default-features = false, path = "substrate/frame/lottery" } pallet-membership = { path = "substrate/frame/membership", default-features = false } pallet-message-queue = { path = "substrate/frame/message-queue", default-features = false } +pallet-meta-tx = { path = "substrate/frame/meta-tx", default-features = false } pallet-migrations = { path = "substrate/frame/migrations", default-features = false } pallet-minimal-template = { path = "templates/minimal/pallets/template", default-features = false } pallet-mixnet = { default-features = false, path = "substrate/frame/mixnet" } diff --git a/substrate/frame/meta-tx/Cargo.toml b/substrate/frame/meta-tx/Cargo.toml new file mode 100644 index 0000000000000..192e32f2dc384 --- /dev/null +++ b/substrate/frame/meta-tx/Cargo.toml @@ -0,0 +1,58 @@ +[package] +name = "pallet-meta-tx" +description = "Dispatch Meta Transaction" +license = "Apache-2.0" +version = "0.0.1" +edition.workspace = true +authors.workspace = true +homepage.workspace = true +repository.workspace = true + +[lints] +workspace = true + +[dependencies] +codec = { workspace = true, features = ["max-encoded-len"] } +docify = { workspace = true } +scale-info = { workspace = true, features = ["derive"] } +serde = { features = ["derive"], optional = true, workspace = true, default-features = true } + +frame-support = { workspace = true} +frame-system = { workspace = true } +sp-core = { workspace = true } +sp-runtime = { workspace = true} +sp-std = { workspace = true} +frame-benchmarking = { workspace = true, optional = true } + +[dev-dependencies] +pallet-balances = { workspace = true, features = ["std"] } +sp-io = { workspace = true, features = ["std"] } +sp-keyring = { workspace = true, features = ["std"] } +pallet-transaction-payment = { workspace = true, features = ["std"] } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "serde", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] +runtime-benchmarks = [ + "pallet-balances/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = [ + "pallet-balances/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/substrate/frame/meta-tx/src/lib.rs b/substrate/frame/meta-tx/src/lib.rs new file mode 100644 index 0000000000000..ef79a480a84d4 --- /dev/null +++ b/substrate/frame/meta-tx/src/lib.rs @@ -0,0 +1,268 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Meta Tx (Meta Transaction) Pallet +//! +//! This pallet enables the dispatch of transactions that are authorized by one party (the signer) +//! and executed by an untrusted third party (the relayer), who covers the transaction fees. +//! +//! ## Pallet API +//! +//! See the [`pallet`] module for more information about the interfaces this pallet exposes, +//! including its configuration trait, dispatchables, storage items, events and errors. +//! +//! ## Overview +//! +//! The pallet provides a client-level API, typically not meant for direct use by end users. +//! A meta transaction, constructed with the help of a wallet, contains a target call, necessary +//! extensions, and the signer's signature. This transaction is then broadcast, and any interested +//! relayer can pick it up and execute it. The relayer submits a regular transaction via the +//! [`dispatch`](`Pallet::dispatch`) function, passing the meta transaction as an argument to +//! execute the target call on behalf of the signer while covering the fees. +//! +//! ### Example +#![doc = docify::embed!("src/tests.rs", sign_and_execute_meta_tx)] +//! +//! ## Low-Level / Implementation Details +//! +//! The structure of a meta transaction is identical to a regular transaction. It includes the +//! signer's address, signature, target call, and a configurable set of extensions. The signed +//! payload consists of the call, extensions, and any implicit data required by the extensions. +//! This payload can be represented using the [`sp_runtime::generic::SignedPayload`] type. The +//! extensions follow the same [`TransactionExtension`] contract, and common types such as +//! [`frame_system::CheckGenesis`], [`frame_system::CheckMortality`], [`frame_system::CheckNonce`], +//! etc., are applicable in the context of meta transactions. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; +pub use pallet::*; + +use core::ops::Add; +use frame_support::{ + dispatch::{DispatchErrorWithPostInfo, DispatchInfo, GetDispatchInfo, PostDispatchInfo}, + pallet_prelude::*, + traits::OriginTrait, +}; +use frame_system::pallet_prelude::*; +use sp_runtime::traits::AsTransactionAuthorizedOrigin; +use sp_runtime::traits::ExtensionPostDispatchWeightHandler; +use sp_runtime::traits::{Dispatchable, IdentifyAccount, TransactionExtension, Verify}; +use sp_std::prelude::*; + +/// Meta Transaction type. +/// +/// The data that is provided and signed by the signer and shared with the relayer. +#[derive(Encode, Decode, PartialEq, Eq, TypeInfo, Clone, RuntimeDebug)] +pub struct MetaTx { + /// The proof of the authenticity of the meta transaction. + proof: Proof, + /// The target call to be executed on behalf of the signer. + call: Box, + /// The required extension/s. + /// + /// This might include the nonce check, expiration, etc. + extension: Extension, +} + +impl MetaTx { + /// Create a new meta transaction. + pub fn new_signed( + address: Address, + signature: Signature, + call: Call, + extension: Extension, + ) -> Self { + Self { proof: Proof::Signed(address, signature), call: Box::new(call), extension } + } +} + +/// Proof of the authenticity of the meta transaction. +// It could potentially be extended to support additional types of proofs, similar to the +// sp_runtime::generic::Preamble::Bare transaction type. +// TODO: check if even need this +#[derive(Encode, Decode, PartialEq, Eq, TypeInfo, Clone, RuntimeDebug)] +pub enum Proof { + /// Signature of the meta transaction payload and the signer's address. + Signed(Address, Signature), +} + +/// The [`MetaTx`] for the given config. +pub type MetaTxFor = MetaTx< + <::PublicKey as IdentifyAccount>::AccountId, + ::Signature, + ::RuntimeCall, + ::Extension, +>; + +/// The [`sp_runtime::generic::SignedPayload`] for the given config. +pub type SignedPayloadFor = + sp_runtime::generic::SignedPayload<::RuntimeCall, ::Extension>; + +#[frame_support::pallet(dev_mode)] +pub mod pallet { + use frame_support::weights; + + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config + where + Self::RuntimeOrigin: AsTransactionAuthorizedOrigin, + { + /// The overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// The overarching call type. + type RuntimeCall: Parameter + + GetDispatchInfo + + Dispatchable< + Info = DispatchInfo, + PostInfo = PostDispatchInfo, + RuntimeOrigin = Self::RuntimeOrigin, + > + IsType<::RuntimeCall>; + /// Signature type for meta transactions. + type Signature: Parameter + Verify; + /// Public key type used for signature verification. + /// + /// The `Signer` of the [`Config::Signature`]. + type PublicKey: IdentifyAccount; + /// Transaction extension/s for meta transactions. + /// + /// The extensions that must be present in every meta transaction. This + /// generally includes extensions like [frame_system::CheckSpecVersion], + /// [frame_system::CheckTxVersion], [frame_system::CheckGenesis], + /// [frame_system::CheckMortality], [frame_system::CheckNonce], etc. + type Extension: TransactionExtension<::RuntimeCall>; + } + + #[pallet::error] + pub enum Error { + /// Invalid proof (e.g. signature). + BadProof, + /// The meta transaction is not yet valid (e.g. nonce too high). + Future, + /// The meta transaction is outdated (e.g. nonce too low). + Stale, + /// The meta transactions's birth block is ancient. + AncientBirthBlock, + /// The meta transaction is invalid. + Invalid, + } + + #[pallet::event] + #[pallet::generate_deposit(pub(crate) fn deposit_event)] + pub enum Event + where + T::RuntimeOrigin: AsTransactionAuthorizedOrigin, + { + /// A meta transaction has been dispatched. + /// + /// Contains the dispatch result of the meta transaction along with post-dispatch + /// information. + Dispatched { result: DispatchResultWithPostInfo }, + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::call] + impl Pallet + where + T::RuntimeOrigin: AsTransactionAuthorizedOrigin, + { + /// Dispatch a given meta transaction. + /// + /// - `_origin`: Can be any kind of origin. + /// - `meta_tx`: Meta Transaction with a target call to be dispatched. + #[pallet::call_index(0)] + #[pallet::weight({ + let dispatch_info = meta_tx.call.get_dispatch_info(); + let extension_weight = meta_tx.extension.weight(meta_tx.call.as_ref()); + // TODO: + dispatch weight + ( + dispatch_info.call_weight.add(extension_weight), + dispatch_info.class, + ) + })] + pub fn dispatch( + _origin: OriginFor, + meta_tx: MetaTxFor, + ) -> DispatchResultWithPostInfo { + let meta_tx_size = meta_tx.encoded_size(); + + let (signer, signature) = match meta_tx.proof { + Proof::Signed(signer, signature) => (signer, signature), + }; + + let signed_payload = SignedPayloadFor::::new(*meta_tx.call, meta_tx.extension) + .map_err(|_| Error::::Invalid)?; + + if !signed_payload.using_encoded(|payload| signature.verify(payload, &signer)) { + return Err(Error::::BadProof.into()); + } + + let origin = T::RuntimeOrigin::signed(signer); + let (call, extension, _) = signed_payload.deconstruct(); + // `info` with worst-case call weight and extension weight. + let info = { + let mut info = call.get_dispatch_info(); + info.extension_weight = extension.weight(&call); + info + }; + + use sp_runtime::traits::DispatchTransaction; + + // dispatch the meta transaction. + let meta_dispatch_res = extension + .dispatch_transaction(origin, call, &info, meta_tx_size) + .map_err(Error::::from)?; + + Self::deposit_event(Event::Dispatched { result: meta_dispatch_res }); + + // meta weight after possible refunds. + let meta_weight = meta_dispatch_res + .map_or_else(|err| err.post_info.actual_weight, |info| info.actual_weight) + .unwrap_or(info.total_weight()); + + // // TODO: post_info + T::WeightInfo::dispatch_weight_without_call_and_ext_weight() + let dispatch_weight = Weight::from_all(1); + + Ok((Some(dispatch_weight + meta_weight), true.into()).into()) + } + } + + /// Implements [`From`] for [`Error`] by mapping the relevant error + /// variants. + impl From for Error { + fn from(err: TransactionValidityError) -> Self { + use TransactionValidityError::*; + match err { + Unknown(_) => Error::::Invalid, + Invalid(err) => match err { + InvalidTransaction::BadProof => Error::::BadProof, + InvalidTransaction::Future => Error::::Future, + InvalidTransaction::Stale => Error::::Stale, + InvalidTransaction::AncientBirthBlock => Error::::AncientBirthBlock, + _ => Error::::Invalid, + }, + } + } + } +} diff --git a/substrate/frame/meta-tx/src/mock.rs b/substrate/frame/meta-tx/src/mock.rs new file mode 100644 index 0000000000000..1e566aa9f1096 --- /dev/null +++ b/substrate/frame/meta-tx/src/mock.rs @@ -0,0 +1,108 @@ +// This file is part of Substrate. +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Mock setup for tests. + +#![cfg(any(test, feature = "runtime-benchmarks"))] + +use crate as pallet_meta_tx; +use crate::*; +use frame_support::{ + construct_runtime, derive_impl, + weights::{FixedFee, NoFee}, +}; +use sp_core::ConstU8; +use sp_runtime::{traits::IdentityLookup, MultiSignature}; + +pub type Balance = u64; + +pub type Signature = MultiSignature; +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; + +pub type Extension = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckMortality, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, +); + +pub type UncheckedExtrinsic = + sp_runtime::generic::UncheckedExtrinsic; + +pub type MetaTxExtension = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckMortality, + frame_system::CheckNonce, +); + +impl Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Signature = Signature; + type PublicKey = ::Signer; + type Extension = MetaTxExtension; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Runtime { + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Block = frame_system::mocking::MockBlock; + type AccountData = pallet_balances::AccountData<::Balance>; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Runtime { + type ReserveIdentifier = [u8; 8]; + type AccountStore = System; +} + +pub const TX_FEE: u32 = 10; + +impl pallet_transaction_payment::Config for Runtime { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = pallet_transaction_payment::FungibleAdapter; + type OperationalFeeMultiplier = ConstU8<1>; + type WeightToFee = FixedFee; + type LengthToFee = NoFee; + type FeeMultiplierUpdate = (); +} + +construct_runtime!( + pub enum Runtime { + System: frame_system, + Balances: pallet_balances, + MetaTx: pallet_meta_tx, + TxPayment: pallet_transaction_payment, + } +); + +pub(crate) fn new_test_ext() -> sp_io::TestExternalities { + let mut ext = sp_io::TestExternalities::new(Default::default()); + ext.execute_with(|| { + frame_system::GenesisConfig::::default().build(); + System::set_block_number(1); + }); + ext +} diff --git a/substrate/frame/meta-tx/src/tests.rs b/substrate/frame/meta-tx/src/tests.rs new file mode 100644 index 0000000000000..6b37dba721671 --- /dev/null +++ b/substrate/frame/meta-tx/src/tests.rs @@ -0,0 +1,154 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::*; +use frame_support::traits::tokens::fungible::Inspect; +use mock::*; +use sp_keyring::AccountKeyring; +use sp_runtime::{ + traits::{Applyable, Checkable, Hash, IdentityLookup}, + MultiSignature, +}; + +#[docify::export] +#[test] +fn sign_and_execute_meta_tx() { + new_test_ext().execute_with(|| { + // meta tx signer + let alice_keyring = AccountKeyring::Alice; + // meta tx relayer + let bob_keyring = AccountKeyring::Bob; + + let alice_account: AccountId = alice_keyring.public().into(); + let bob_account: AccountId = bob_keyring.public().into(); + + let ed = Balances::minimum_balance(); + let tx_fee: Balance = (2 * TX_FEE).into(); // base tx fee + weight fee + let alice_balance = ed * 100; + let bob_balance = ed * 100; + + { + // setup initial balances for alice and bob + Balances::force_set_balance( + RuntimeOrigin::root(), + alice_account.clone().into(), + alice_balance, + ) + .unwrap(); + Balances::force_set_balance( + RuntimeOrigin::root(), + bob_account.clone().into(), + bob_balance, + ) + .unwrap(); + } + + // Alice builds a meta transaction. + + let remark_call = + RuntimeCall::System(frame_system::Call::remark_with_event { remark: vec![1] }); + let meta_tx_ext: MetaTxExtension = ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckMortality::::from(sp_runtime::generic::Era::immortal()), + frame_system::CheckNonce::::from( + frame_system::Pallet::::account(&alice_account).nonce, + ), + ); + + let meta_tx_sig = MultiSignature::Sr25519( + (remark_call.clone(), meta_tx_ext.clone(), meta_tx_ext.implicit().unwrap()) + .using_encoded(|e| alice_keyring.sign(&e)), + ); + + let meta_tx = MetaTxFor::::new_signed( + alice_account.clone(), + meta_tx_sig, + remark_call.clone(), + meta_tx_ext.clone(), + ); + + // Encode and share with the world. + let meta_tx_encoded = meta_tx.encode(); + + // Bob acts as meta transaction relayer. + + let meta_tx = MetaTxFor::::decode(&mut &meta_tx_encoded[..]).unwrap(); + let call = RuntimeCall::MetaTx(Call::dispatch { meta_tx: meta_tx.clone() }); + let tx_ext: Extension = ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckMortality::::from(sp_runtime::generic::Era::immortal()), + frame_system::CheckNonce::::from( + frame_system::Pallet::::account(&bob_account).nonce, + ), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(0), + ); + + let tx_sig = MultiSignature::Sr25519( + (call.clone(), tx_ext.clone(), tx_ext.implicit().unwrap()) + .using_encoded(|e| bob_keyring.sign(&e)), + ); + + let uxt = UncheckedExtrinsic::new_signed(call, bob_account.clone(), tx_sig, tx_ext); + + // Check Extrinsic validity and apply it. + + let uxt_info = uxt.get_dispatch_info(); + let uxt_len = uxt.using_encoded(|e| e.len()); + + let xt = >>::check( + uxt, + &Default::default(), + ) + .unwrap(); + + let res = xt.apply::(&uxt_info, uxt_len).unwrap(); + + // Asserting the results. + + assert!(res.is_ok(), "Dispatch result is not successful: {:?}", res); + + let expected_meta_res = Ok(PostDispatchInfo { + actual_weight: Some( + remark_call + .get_dispatch_info() + .call_weight + .add(meta_tx_ext.weight(&remark_call)), + ), + pays_fee: Pays::Yes, + }); + + System::assert_has_event(RuntimeEvent::MetaTx(crate::Event::Dispatched { + result: expected_meta_res, + })); + + System::assert_has_event(RuntimeEvent::System(frame_system::Event::Remarked { + sender: alice_account.clone(), + hash: ::Hashing::hash(&[1]), + })); + + // Alice balance is unchanged, Bob paid the transaction fee. + assert_eq!(alice_balance, Balances::free_balance(alice_account)); + assert_eq!(bob_balance - tx_fee, Balances::free_balance(bob_account)); + }); +} From fee605ee546d68fa73f2058317472eb3b87fbf21 Mon Sep 17 00:00:00 2001 From: muharem Date: Thu, 24 Oct 2024 15:48:19 +0200 Subject: [PATCH 02/28] preamble --- substrate/frame/meta-tx/src/lib.rs | 57 ++++++++++++++-------------- substrate/frame/meta-tx/src/tests.rs | 4 +- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/substrate/frame/meta-tx/src/lib.rs b/substrate/frame/meta-tx/src/lib.rs index ef79a480a84d4..cf77f8e98cbac 100644 --- a/substrate/frame/meta-tx/src/lib.rs +++ b/substrate/frame/meta-tx/src/lib.rs @@ -57,14 +57,15 @@ pub use pallet::*; use core::ops::Add; use frame_support::{ - dispatch::{DispatchErrorWithPostInfo, DispatchInfo, GetDispatchInfo, PostDispatchInfo}, + dispatch::{DispatchInfo, GetDispatchInfo, PostDispatchInfo}, pallet_prelude::*, traits::OriginTrait, }; use frame_system::pallet_prelude::*; -use sp_runtime::traits::AsTransactionAuthorizedOrigin; -use sp_runtime::traits::ExtensionPostDispatchWeightHandler; -use sp_runtime::traits::{Dispatchable, IdentifyAccount, TransactionExtension, Verify}; +use sp_runtime::traits::{ + AsTransactionAuthorizedOrigin, DispatchTransaction, Dispatchable, IdentifyAccount, + TransactionExtension, Verify, +}; use sp_std::prelude::*; /// Meta Transaction type. @@ -72,14 +73,10 @@ use sp_std::prelude::*; /// The data that is provided and signed by the signer and shared with the relayer. #[derive(Encode, Decode, PartialEq, Eq, TypeInfo, Clone, RuntimeDebug)] pub struct MetaTx { - /// The proof of the authenticity of the meta transaction. - proof: Proof, + /// Information regarding the type of the meta transaction. + preamble: Preamble, /// The target call to be executed on behalf of the signer. - call: Box, - /// The required extension/s. - /// - /// This might include the nonce check, expiration, etc. - extension: Extension, + call: Call, } impl MetaTx { @@ -87,21 +84,29 @@ impl MetaTx Self { - Self { proof: Proof::Signed(address, signature), call: Box::new(call), extension } + Self { preamble: Preamble::Signed(address, signature, extension), call } + } + + /// Get the extension reference of the meta transaction. + pub fn extension_as_ref(&self) -> &Extension { + match &self.preamble { + Preamble::Signed(_, _, extension) => extension, + } } } /// Proof of the authenticity of the meta transaction. -// It could potentially be extended to support additional types of proofs, similar to the -// sp_runtime::generic::Preamble::Bare transaction type. -// TODO: check if even need this +/// +/// It could potentially be extended to support other type of meta transaction, similar to the +/// [`sp_runtime::generic::Preamble::Bare`]` transaction extrinsic type. #[derive(Encode, Decode, PartialEq, Eq, TypeInfo, Clone, RuntimeDebug)] -pub enum Proof { - /// Signature of the meta transaction payload and the signer's address. - Signed(Address, Signature), +pub enum Preamble { + /// Meta transaction that contains the signature, signer's address and the extension with it's + /// version. + Signed(Address, Signature, Extension), } /// The [`MetaTx`] for the given config. @@ -118,8 +123,6 @@ pub type SignedPayloadFor = #[frame_support::pallet(dev_mode)] pub mod pallet { - use frame_support::weights; - use super::*; #[pallet::config] @@ -194,7 +197,7 @@ pub mod pallet { #[pallet::call_index(0)] #[pallet::weight({ let dispatch_info = meta_tx.call.get_dispatch_info(); - let extension_weight = meta_tx.extension.weight(meta_tx.call.as_ref()); + let extension_weight = meta_tx.extension_as_ref().weight(&meta_tx.call); // TODO: + dispatch weight ( dispatch_info.call_weight.add(extension_weight), @@ -203,15 +206,15 @@ pub mod pallet { })] pub fn dispatch( _origin: OriginFor, - meta_tx: MetaTxFor, + meta_tx: Box>, ) -> DispatchResultWithPostInfo { let meta_tx_size = meta_tx.encoded_size(); - let (signer, signature) = match meta_tx.proof { - Proof::Signed(signer, signature) => (signer, signature), + let (signer, signature, extension) = match meta_tx.preamble { + Preamble::Signed(signer, signature, extension) => (signer, signature, extension), }; - let signed_payload = SignedPayloadFor::::new(*meta_tx.call, meta_tx.extension) + let signed_payload = SignedPayloadFor::::new(meta_tx.call, extension) .map_err(|_| Error::::Invalid)?; if !signed_payload.using_encoded(|payload| signature.verify(payload, &signer)) { @@ -227,8 +230,6 @@ pub mod pallet { info }; - use sp_runtime::traits::DispatchTransaction; - // dispatch the meta transaction. let meta_dispatch_res = extension .dispatch_transaction(origin, call, &info, meta_tx_size) diff --git a/substrate/frame/meta-tx/src/tests.rs b/substrate/frame/meta-tx/src/tests.rs index 6b37dba721671..71f11b6d5dfb1 100644 --- a/substrate/frame/meta-tx/src/tests.rs +++ b/substrate/frame/meta-tx/src/tests.rs @@ -80,8 +80,8 @@ fn sign_and_execute_meta_tx() { let meta_tx = MetaTxFor::::new_signed( alice_account.clone(), meta_tx_sig, - remark_call.clone(), meta_tx_ext.clone(), + remark_call.clone(), ); // Encode and share with the world. @@ -90,7 +90,7 @@ fn sign_and_execute_meta_tx() { // Bob acts as meta transaction relayer. let meta_tx = MetaTxFor::::decode(&mut &meta_tx_encoded[..]).unwrap(); - let call = RuntimeCall::MetaTx(Call::dispatch { meta_tx: meta_tx.clone() }); + let call = RuntimeCall::MetaTx(Call::dispatch { meta_tx: Box::new(meta_tx.clone()) }); let tx_ext: Extension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), From 75fd3ee82e804e41c66721ad8fa95b9c46fde06d Mon Sep 17 00:00:00 2001 From: muharem Date: Fri, 25 Oct 2024 17:21:24 +0200 Subject: [PATCH 03/28] benchmarks --- substrate/frame/meta-tx/Cargo.toml | 9 +- substrate/frame/meta-tx/src/benchmarking.rs | 168 ++++++++++++++++++++ substrate/frame/meta-tx/src/lib.rs | 16 +- substrate/frame/meta-tx/src/mock.rs | 5 + substrate/frame/meta-tx/src/tests.rs | 3 + 5 files changed, 195 insertions(+), 6 deletions(-) create mode 100644 substrate/frame/meta-tx/src/benchmarking.rs diff --git a/substrate/frame/meta-tx/Cargo.toml b/substrate/frame/meta-tx/Cargo.toml index 192e32f2dc384..db18b167c7d74 100644 --- a/substrate/frame/meta-tx/Cargo.toml +++ b/substrate/frame/meta-tx/Cargo.toml @@ -23,12 +23,12 @@ sp-core = { workspace = true } sp-runtime = { workspace = true} sp-std = { workspace = true} frame-benchmarking = { workspace = true, optional = true } +sp-keyring = { workspace = true, optional = true, default-features = true } [dev-dependencies] -pallet-balances = { workspace = true, features = ["std"] } -sp-io = { workspace = true, features = ["std"] } -sp-keyring = { workspace = true, features = ["std"] } -pallet-transaction-payment = { workspace = true, features = ["std"] } +pallet-balances = { workspace = true, default-features = true } +sp-io = { workspace = true, default-features = true } +pallet-transaction-payment = { workspace = true, default-features = true } [features] default = ["std"] @@ -49,6 +49,7 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "sp-keyring", ] try-runtime = [ "pallet-balances/try-runtime", diff --git a/substrate/frame/meta-tx/src/benchmarking.rs b/substrate/frame/meta-tx/src/benchmarking.rs new file mode 100644 index 0000000000000..8cf383215d865 --- /dev/null +++ b/substrate/frame/meta-tx/src/benchmarking.rs @@ -0,0 +1,168 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::traits::UnfilteredDispatchable; +use sp_runtime::impl_tx_ext_default; +use types::BenchmarkHelper; + +pub mod types { + use super::*; + use sp_keyring::AccountKeyring; + use sp_runtime::{AccountId32, MultiSignature}; + + /// Trait for the config type that facilitates the benchmarking of the pallet. + pub trait BenchmarkHelper { + /// Create a weightless call for the benchmark. + /// + /// This is used to obtain the weight for the `dispatch` call excluding the weight of the + /// meta transaction's call. + /// + /// E.g.: `frame_system::Call::remark` call with empty `remark`. + fn create_weightless_call() -> Call; + /// Create a signature for a meta transaction. + fn create_signature(call: Call, ext: Extension) -> (AccountId, Signature); + } + + type CallOf = ::RuntimeCall; + type ExtensionOf = ::Extension; + + pub struct BenchmarkHelperFor(core::marker::PhantomData); + impl BenchmarkHelper, ExtensionOf> + for BenchmarkHelperFor + where + T::RuntimeOrigin: AsTransactionAuthorizedOrigin, + CallOf: From>, + { + fn create_weightless_call() -> CallOf { + frame_system::Call::::remark { remark: vec![] }.into() + } + fn create_signature(call: CallOf, ext: ExtensionOf) -> (AccountId32, MultiSignature) { + let alice_keyring = AccountKeyring::Alice; + ( + alice_keyring.public().into(), + MultiSignature::Sr25519( + (call, ext.clone(), ext.implicit().unwrap()) + .using_encoded(|e| alice_keyring.sign(&e)), + ), + ) + } + } + + /// A weightless extension to facilitate the bare dispatch benchmark. + #[derive(TypeInfo, Eq, PartialEq, Clone, Encode, Decode)] + #[scale_info(skip_type_params(T))] + pub struct WeightlessExtension(core::marker::PhantomData); + impl core::fmt::Debug for WeightlessExtension + where + T::RuntimeOrigin: AsTransactionAuthorizedOrigin, + { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "WeightlessExtension") + } + } + impl Default for WeightlessExtension + where + T::RuntimeOrigin: AsTransactionAuthorizedOrigin, + { + fn default() -> Self { + WeightlessExtension(Default::default()) + } + } + impl TransactionExtension<::RuntimeCall> + for WeightlessExtension + where + T::RuntimeOrigin: AsTransactionAuthorizedOrigin, + { + const IDENTIFIER: &'static str = "WeightlessExtension"; + type Implicit = (); + type Pre = (); + type Val = (); + fn weight(&self, _call: &::RuntimeCall) -> Weight { + 0.into() + } + impl_tx_ext_default!(::RuntimeCall; validate prepare); + } +} + +fn assert_last_event(generic_event: ::RuntimeEvent) +where + ::RuntimeOrigin: AsTransactionAuthorizedOrigin, +{ + frame_system::Pallet::::assert_last_event(generic_event.into()); +} + +#[benchmarks( + where + T: Config, + ::Extension: Default, + ::RuntimeCall: From>, + ::RuntimeOrigin: AsTransactionAuthorizedOrigin, + )] +mod benchmarks { + use super::*; + + #[benchmark] + fn bare_dispatch() { + let meta_call = T::BenchmarkHelper::create_weightless_call(); + let meta_ext = T::Extension::default(); + + assert!( + meta_ext.weight(&meta_call).is_zero(), + "meta tx extension weight for the benchmarks must be zero.", + ); + + let (signer, meta_sig) = + T::BenchmarkHelper::create_signature(meta_call.clone(), meta_ext.clone()); + + let meta_tx = MetaTxFor::::new_signed( + signer.clone(), + meta_sig, + meta_ext.clone(), + meta_call.clone(), + ); + + let call = Call::::dispatch { meta_tx: Box::new(meta_tx) }; + let info = call.get_dispatch_info(); + let caller = whitelisted_caller(); + let origin: T::RuntimeOrigin = frame_system::RawOrigin::Signed(caller).into(); + + #[block] + { + let _ = call.dispatch_bypass_filter(origin); + } + + assert_last_event::( + Event::Dispatched { + result: Ok(PostDispatchInfo { + actual_weight: Some(info.call_weight), + pays_fee: Pays::Yes, + }), + } + .into(), + ); + } + + impl_benchmark_test_suite! { + Pallet, + crate::mock::new_test_ext(), + crate::mock::Runtime, + } +} diff --git a/substrate/frame/meta-tx/src/lib.rs b/substrate/frame/meta-tx/src/lib.rs index cf77f8e98cbac..fce29839cd0ca 100644 --- a/substrate/frame/meta-tx/src/lib.rs +++ b/substrate/frame/meta-tx/src/lib.rs @@ -49,6 +49,7 @@ #![cfg_attr(not(feature = "std"), no_std)] +mod benchmarking; #[cfg(test)] mod mock; #[cfg(test)] @@ -121,7 +122,7 @@ pub type MetaTxFor = MetaTx< pub type SignedPayloadFor = sp_runtime::generic::SignedPayload<::RuntimeCall, ::Extension>; -#[frame_support::pallet(dev_mode)] +#[frame_support::pallet] pub mod pallet { use super::*; @@ -153,6 +154,17 @@ pub mod pallet { /// [frame_system::CheckTxVersion], [frame_system::CheckGenesis], /// [frame_system::CheckMortality], [frame_system::CheckNonce], etc. type Extension: TransactionExtension<::RuntimeCall>; + /// The benchmark helper provides the necessary functions to create a call and a signature. + /// + /// For runtime using [`sp_runtime::MultiSignature`] cryptography use + /// [`benchmarking::types::BenchmarkHelperFor`] implementation. + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: benchmarking::types::BenchmarkHelper< + Self::AccountId, + Self::Signature, + ::RuntimeCall, + Self::Extension, + >; } #[pallet::error] @@ -188,7 +200,7 @@ pub mod pallet { #[pallet::call] impl Pallet where - T::RuntimeOrigin: AsTransactionAuthorizedOrigin, + ::RuntimeOrigin: AsTransactionAuthorizedOrigin, { /// Dispatch a given meta transaction. /// diff --git a/substrate/frame/meta-tx/src/mock.rs b/substrate/frame/meta-tx/src/mock.rs index 1e566aa9f1096..66cf9ed64aa81 100644 --- a/substrate/frame/meta-tx/src/mock.rs +++ b/substrate/frame/meta-tx/src/mock.rs @@ -46,6 +46,7 @@ pub type Extension = ( pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; +#[cfg(not(feature = "runtime-benchmarks"))] pub type MetaTxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, @@ -54,6 +55,8 @@ pub type MetaTxExtension = ( frame_system::CheckMortality, frame_system::CheckNonce, ); +#[cfg(feature = "runtime-benchmarks")] +pub type MetaTxExtension = crate::benchmarking::types::WeightlessExtension; impl Config for Runtime { type RuntimeEvent = RuntimeEvent; @@ -61,6 +64,8 @@ impl Config for Runtime { type Signature = Signature; type PublicKey = ::Signer; type Extension = MetaTxExtension; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = crate::benchmarking::types::BenchmarkHelperFor; } #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] diff --git a/substrate/frame/meta-tx/src/tests.rs b/substrate/frame/meta-tx/src/tests.rs index 71f11b6d5dfb1..cb349d93cd939 100644 --- a/substrate/frame/meta-tx/src/tests.rs +++ b/substrate/frame/meta-tx/src/tests.rs @@ -61,6 +61,7 @@ fn sign_and_execute_meta_tx() { let remark_call = RuntimeCall::System(frame_system::Call::remark_with_event { remark: vec![1] }); + #[cfg(not(feature = "runtime-benchmarks"))] let meta_tx_ext: MetaTxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), @@ -71,6 +72,8 @@ fn sign_and_execute_meta_tx() { frame_system::Pallet::::account(&alice_account).nonce, ), ); + #[cfg(feature = "runtime-benchmarks")] + let meta_tx_ext = crate::benchmarking::types::WeightlessExtension::::default(); let meta_tx_sig = MultiSignature::Sr25519( (remark_call.clone(), meta_tx_ext.clone(), meta_tx_ext.implicit().unwrap()) From 1c7b1b4be2df0490f180bc61adc8eb4e9ab61607 Mon Sep 17 00:00:00 2001 From: muharem Date: Tue, 29 Oct 2024 12:21:12 +0100 Subject: [PATCH 04/28] fox cargo features --- substrate/frame/meta-tx/Cargo.toml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/substrate/frame/meta-tx/Cargo.toml b/substrate/frame/meta-tx/Cargo.toml index db18b167c7d74..11a5761829265 100644 --- a/substrate/frame/meta-tx/Cargo.toml +++ b/substrate/frame/meta-tx/Cargo.toml @@ -15,23 +15,23 @@ workspace = true codec = { workspace = true, features = ["max-encoded-len"] } docify = { workspace = true } scale-info = { workspace = true, features = ["derive"] } -serde = { features = ["derive"], optional = true, workspace = true, default-features = true } +serde = { features = ["derive"], optional = true, workspace = true } -frame-support = { workspace = true} +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } frame-system = { workspace = true } sp-core = { workspace = true } -sp-runtime = { workspace = true} -sp-std = { workspace = true} -frame-benchmarking = { workspace = true, optional = true } -sp-keyring = { workspace = true, optional = true, default-features = true } +sp-runtime = { workspace = true } +sp-keyring = { workspace = true, optional = true } +sp-std = { workspace = true } +sp-io = { workspace = true } [dev-dependencies] pallet-balances = { workspace = true, default-features = true } -sp-io = { workspace = true, default-features = true } -pallet-transaction-payment = { workspace = true, default-features = true } +pallet-transaction-payment = { workspace = true, default-features = true} [features] -default = ["std"] +default = ["std"] std = [ "codec/std", "frame-benchmarking?/std", @@ -42,17 +42,17 @@ std = [ "sp-core/std", "sp-runtime/std", "sp-std/std", + "sp-keyring/std", ] runtime-benchmarks = [ - "pallet-balances/runtime-benchmarks", "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", - "sp-keyring", + "pallet-balances/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", ] try-runtime = [ - "pallet-balances/try-runtime", "frame-support/try-runtime", "frame-system/try-runtime", "sp-runtime/try-runtime", From 3d9a9e24e25e16605a4fd8293f96aa88fce1d952 Mon Sep 17 00:00:00 2001 From: muharem Date: Thu, 7 Nov 2024 16:39:59 +0100 Subject: [PATCH 05/28] integrate meta-tx pallet into runtimes --- Cargo.lock | 3 +++ polkadot/runtime/westend/Cargo.toml | 4 ++++ polkadot/runtime/westend/src/lib.rs | 23 +++++++++++++++++++++ substrate/bin/node/runtime/src/lib.rs | 23 +++++++++++++++++++++ substrate/frame/meta-tx/Cargo.toml | 11 +++++----- substrate/frame/meta-tx/src/benchmarking.rs | 18 +++++++++------- substrate/frame/meta-tx/src/lib.rs | 2 ++ substrate/frame/meta-tx/src/mock.rs | 2 ++ umbrella/Cargo.toml | 11 +++++++++- umbrella/src/lib.rs | 4 ++++ 10 files changed, 87 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c972a11b4243..6c5ad169828c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11959,6 +11959,7 @@ dependencies = [ "sp-core 28.0.0", "sp-io 30.0.0", "sp-keyring", + "sp-keystore 0.34.0", "sp-runtime 31.0.1", "sp-std 14.0.0", ] @@ -15388,6 +15389,7 @@ dependencies = [ "pallet-lottery", "pallet-membership", "pallet-message-queue", + "pallet-meta-tx", "pallet-migrations", "pallet-mixnet", "pallet-mmr", @@ -26457,6 +26459,7 @@ dependencies = [ "pallet-indices", "pallet-membership", "pallet-message-queue", + "pallet-meta-tx", "pallet-mmr", "pallet-multisig", "pallet-nomination-pools", diff --git a/polkadot/runtime/westend/Cargo.toml b/polkadot/runtime/westend/Cargo.toml index fcb5719de8955..3c0a57cc56e69 100644 --- a/polkadot/runtime/westend/Cargo.toml +++ b/polkadot/runtime/westend/Cargo.toml @@ -69,6 +69,7 @@ pallet-identity = { workspace = true } pallet-indices = { workspace = true } pallet-membership = { workspace = true } pallet-message-queue = { workspace = true } +pallet-meta-tx = { workspace = true } pallet-mmr = { workspace = true } pallet-multisig = { workspace = true } pallet-nomination-pools = { workspace = true } @@ -169,6 +170,7 @@ std = [ "pallet-indices/std", "pallet-membership/std", "pallet-message-queue/std", + "pallet-meta-tx/std", "pallet-mmr/std", "pallet-multisig/std", "pallet-nomination-pools-benchmarking?/std", @@ -259,6 +261,7 @@ runtime-benchmarks = [ "pallet-indices/runtime-benchmarks", "pallet-membership/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", + "pallet-meta-tx/runtime-benchmarks", "pallet-mmr/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-nomination-pools-benchmarking/runtime-benchmarks", @@ -321,6 +324,7 @@ try-runtime = [ "pallet-indices/try-runtime", "pallet-membership/try-runtime", "pallet-message-queue/try-runtime", + "pallet-meta-tx/try-runtime", "pallet-mmr/try-runtime", "pallet-multisig/try-runtime", "pallet-nomination-pools/try-runtime", diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 461be186ee516..388e90e173b13 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1555,6 +1555,26 @@ impl OnSwap for SwapLeases { } } +pub type MetaTxExtension = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_metadata_hash_extension::CheckMetadataHash, +); + +impl pallet_meta_tx::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Signature = Signature; + type PublicKey = ::Signer; + type Extension = MetaTxExtension; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = pallet_meta_tx::BenchmarkHelperFor; +} + #[frame_support::runtime(legacy_ordering)] mod runtime { #[runtime::runtime] @@ -1743,6 +1763,9 @@ mod runtime { #[runtime::pallet_index(102)] pub type RootTesting = pallet_root_testing; + #[runtime::pallet_index(103)] + pub type MetaTx = pallet_meta_tx::Pallet; + // BEEFY Bridges support. #[runtime::pallet_index(200)] pub type Beefy = pallet_beefy; diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index d6a17856e4704..b1df67da6f4e2 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -2323,6 +2323,26 @@ impl pallet_parameters::Config for Runtime { type WeightInfo = (); } +pub type MetaTxExtension = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_metadata_hash_extension::CheckMetadataHash, +); + +impl pallet_meta_tx::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Signature = Signature; + type PublicKey = ::Signer; + type Extension = MetaTxExtension; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = pallet_meta_tx::BenchmarkHelperFor; +} + #[frame_support::runtime] mod runtime { use super::*; @@ -2587,6 +2607,9 @@ mod runtime { #[runtime::pallet_index(81)] pub type VerifySignature = pallet_verify_signature::Pallet; + + #[runtime::pallet_index(82)] + pub type MetaTx = pallet_meta_tx::Pallet; } impl TryFrom for pallet_revive::Call { diff --git a/substrate/frame/meta-tx/Cargo.toml b/substrate/frame/meta-tx/Cargo.toml index 11a5761829265..7ebf021a33394 100644 --- a/substrate/frame/meta-tx/Cargo.toml +++ b/substrate/frame/meta-tx/Cargo.toml @@ -22,13 +22,14 @@ frame-support = { workspace = true } frame-system = { workspace = true } sp-core = { workspace = true } sp-runtime = { workspace = true } -sp-keyring = { workspace = true, optional = true } sp-std = { workspace = true } -sp-io = { workspace = true } +sp-io = { workspace = true} [dev-dependencies] pallet-balances = { workspace = true, default-features = true } -pallet-transaction-payment = { workspace = true, default-features = true} +pallet-transaction-payment = { workspace = true, default-features = true } +sp-keyring = { workspace = true, default-features = true } +sp-keystore ={ workspace = true, default-features = true } [features] default = ["std"] @@ -42,15 +43,13 @@ std = [ "sp-core/std", "sp-runtime/std", "sp-std/std", - "sp-keyring/std", + "sp-io/std", ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "pallet-transaction-payment/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", diff --git a/substrate/frame/meta-tx/src/benchmarking.rs b/substrate/frame/meta-tx/src/benchmarking.rs index 8cf383215d865..6a8da4dee6a21 100644 --- a/substrate/frame/meta-tx/src/benchmarking.rs +++ b/substrate/frame/meta-tx/src/benchmarking.rs @@ -25,8 +25,8 @@ use types::BenchmarkHelper; pub mod types { use super::*; - use sp_keyring::AccountKeyring; - use sp_runtime::{AccountId32, MultiSignature}; + use sp_io::crypto::{sr25519_generate, sr25519_sign}; + use sp_runtime::{AccountId32, MultiSignature, MultiSigner}; /// Trait for the config type that facilitates the benchmarking of the pallet. pub trait BenchmarkHelper { @@ -55,12 +55,16 @@ pub mod types { frame_system::Call::::remark { remark: vec![] }.into() } fn create_signature(call: CallOf, ext: ExtensionOf) -> (AccountId32, MultiSignature) { - let alice_keyring = AccountKeyring::Alice; + let public = sr25519_generate(0.into(), None); ( - alice_keyring.public().into(), + MultiSigner::Sr25519(public).into_account().into(), MultiSignature::Sr25519( - (call, ext.clone(), ext.implicit().unwrap()) - .using_encoded(|e| alice_keyring.sign(&e)), + sr25519_sign( + 0.into(), + &public, + &(call, ext.clone(), ext.implicit().unwrap()).encode(), + ) + .unwrap(), ), ) } @@ -96,7 +100,7 @@ pub mod types { type Pre = (); type Val = (); fn weight(&self, _call: &::RuntimeCall) -> Weight { - 0.into() + Weight::from_all(0) } impl_tx_ext_default!(::RuntimeCall; validate prepare); } diff --git a/substrate/frame/meta-tx/src/lib.rs b/substrate/frame/meta-tx/src/lib.rs index fce29839cd0ca..aa73deb56dac9 100644 --- a/substrate/frame/meta-tx/src/lib.rs +++ b/substrate/frame/meta-tx/src/lib.rs @@ -54,6 +54,8 @@ mod benchmarking; mod mock; #[cfg(test)] mod tests; +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::types::{BenchmarkHelper, BenchmarkHelperFor, WeightlessExtension}; pub use pallet::*; use core::ops::Add; diff --git a/substrate/frame/meta-tx/src/mock.rs b/substrate/frame/meta-tx/src/mock.rs index 66cf9ed64aa81..0d68eacb8e692 100644 --- a/substrate/frame/meta-tx/src/mock.rs +++ b/substrate/frame/meta-tx/src/mock.rs @@ -25,6 +25,7 @@ use frame_support::{ weights::{FixedFee, NoFee}, }; use sp_core::ConstU8; +use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; use sp_runtime::{traits::IdentityLookup, MultiSignature}; pub type Balance = u64; @@ -109,5 +110,6 @@ pub(crate) fn new_test_ext() -> sp_io::TestExternalities { frame_system::GenesisConfig::::default().build(); System::set_block_number(1); }); + ext.register_extension(KeystoreExt::new(MemoryKeystore::new())); ext } diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index 370673622b95d..ef49f60f334a7 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -98,6 +98,7 @@ std = [ "pallet-lottery?/std", "pallet-membership?/std", "pallet-message-queue?/std", + "pallet-meta-tx?/std", "pallet-migrations?/std", "pallet-mixnet?/std", "pallet-mmr?/std", @@ -290,6 +291,7 @@ runtime-benchmarks = [ "pallet-lottery?/runtime-benchmarks", "pallet-membership?/runtime-benchmarks", "pallet-message-queue?/runtime-benchmarks", + "pallet-meta-tx?/runtime-benchmarks", "pallet-migrations?/runtime-benchmarks", "pallet-mixnet?/runtime-benchmarks", "pallet-mmr?/runtime-benchmarks", @@ -424,6 +426,7 @@ try-runtime = [ "pallet-lottery?/try-runtime", "pallet-membership?/try-runtime", "pallet-message-queue?/try-runtime", + "pallet-meta-tx?/try-runtime", "pallet-migrations?/try-runtime", "pallet-mixnet?/try-runtime", "pallet-mmr?/try-runtime", @@ -494,6 +497,7 @@ serde = [ "pallet-conviction-voting?/serde", "pallet-democracy?/serde", "pallet-message-queue?/serde", + "pallet-meta-tx?/serde", "pallet-offences?/serde", "pallet-parameters?/serde", "pallet-referenda?/serde", @@ -541,7 +545,7 @@ with-tracing = [ "sp-tracing?/with-tracing", "sp-tracing?/with-tracing", ] -runtime-full = ["assets-common", "binary-merkle-tree", "bp-header-chain", "bp-messages", "bp-parachains", "bp-polkadot", "bp-polkadot-core", "bp-relayers", "bp-runtime", "bp-test-utils", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system-proc-macro", "cumulus-pallet-session-benchmarking", "cumulus-pallet-solo-to-para", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-executive", "frame-metadata-hash-extension", "frame-support", "frame-support-procedural", "frame-support-procedural-tools-derive", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", "pallet-alliance", "pallet-asset-conversion", "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", "pallet-broker", "pallet-child-bounties", "pallet-collator-selection", "pallet-collective", "pallet-collective-content", "pallet-contracts", "pallet-contracts-proc-macro", "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", "pallet-node-authorization", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-revive", "pallet-revive-fixtures", "pallet-revive-proc-macro", "pallet-revive-uapi", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", "pallet-society", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-reward-fn", "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-verify-signature", "pallet-vesting", "pallet-whitelist", "pallet-xcm", "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-metrics", "polkadot-runtime-parachains", "polkadot-sdk-frame", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", "snowbridge-outbound-queue-merkle-tree", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", "sp-consensus-pow", "sp-consensus-slots", "sp-core", "sp-crypto-ec-utils", "sp-crypto-hashing", "sp-crypto-hashing-proc-macro", "sp-debug-derive", "sp-externalities", "sp-genesis-builder", "sp-inherents", "sp-io", "sp-keyring", "sp-keystore", "sp-metadata-ir", "sp-mixnet", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-runtime-interface", "sp-runtime-interface-proc-macro", "sp-session", "sp-staking", "sp-state-machine", "sp-statement-store", "sp-std", "sp-storage", "sp-timestamp", "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", "xcm-procedural", "xcm-runtime-apis"] +runtime-full = ["assets-common", "binary-merkle-tree", "bp-header-chain", "bp-messages", "bp-parachains", "bp-polkadot", "bp-polkadot-core", "bp-relayers", "bp-runtime", "bp-test-utils", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system-proc-macro", "cumulus-pallet-session-benchmarking", "cumulus-pallet-solo-to-para", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-executive", "frame-metadata-hash-extension", "frame-support", "frame-support-procedural", "frame-support-procedural-tools-derive", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", "pallet-alliance", "pallet-asset-conversion", "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", "pallet-broker", "pallet-child-bounties", "pallet-collator-selection", "pallet-collective", "pallet-collective-content", "pallet-contracts", "pallet-contracts-proc-macro", "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", "pallet-meta-tx", "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", "pallet-node-authorization", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-revive", "pallet-revive-fixtures", "pallet-revive-proc-macro", "pallet-revive-uapi", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", "pallet-society", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-reward-fn", "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-verify-signature", "pallet-vesting", "pallet-whitelist", "pallet-xcm", "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-metrics", "polkadot-runtime-parachains", "polkadot-sdk-frame", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", "snowbridge-outbound-queue-merkle-tree", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", "sp-consensus-pow", "sp-consensus-slots", "sp-core", "sp-crypto-ec-utils", "sp-crypto-hashing", "sp-crypto-hashing-proc-macro", "sp-debug-derive", "sp-externalities", "sp-genesis-builder", "sp-inherents", "sp-io", "sp-keyring", "sp-keystore", "sp-metadata-ir", "sp-mixnet", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-runtime-interface", "sp-runtime-interface-proc-macro", "sp-session", "sp-staking", "sp-state-machine", "sp-statement-store", "sp-std", "sp-storage", "sp-timestamp", "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", "xcm-procedural", "xcm-runtime-apis"] runtime = [ "frame-benchmarking", "frame-benchmarking-pallet-pov", @@ -1077,6 +1081,11 @@ path = "../substrate/frame/message-queue" default-features = false optional = true +[dependencies.pallet-meta-tx] +path = "../substrate/frame/meta-tx" +default-features = false +optional = true + [dependencies.pallet-migrations] path = "../substrate/frame/migrations" default-features = false diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index f3fc949c66ec5..d83fbc5f4d714 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -487,6 +487,10 @@ pub use pallet_membership; #[cfg(feature = "pallet-message-queue")] pub use pallet_message_queue; +/// FRAME pallet for meta transactions. +#[cfg(feature = "pallet-meta-tx")] +pub use pallet_meta_tx; + /// FRAME pallet to execute multi-block migrations. #[cfg(feature = "pallet-migrations")] pub use pallet_migrations; From 2a95be7d658636a701766f4f31334f03c7437f57 Mon Sep 17 00:00:00 2001 From: muharem Date: Thu, 7 Nov 2024 18:20:48 +0100 Subject: [PATCH 06/28] remove preamble --- substrate/frame/meta-tx/src/benchmarking.rs | 8 +--- substrate/frame/meta-tx/src/lib.rs | 51 +++++++-------------- substrate/frame/meta-tx/src/tests.rs | 4 +- 3 files changed, 20 insertions(+), 43 deletions(-) diff --git a/substrate/frame/meta-tx/src/benchmarking.rs b/substrate/frame/meta-tx/src/benchmarking.rs index 6a8da4dee6a21..d75a13f072f8c 100644 --- a/substrate/frame/meta-tx/src/benchmarking.rs +++ b/substrate/frame/meta-tx/src/benchmarking.rs @@ -136,12 +136,8 @@ mod benchmarks { let (signer, meta_sig) = T::BenchmarkHelper::create_signature(meta_call.clone(), meta_ext.clone()); - let meta_tx = MetaTxFor::::new_signed( - signer.clone(), - meta_sig, - meta_ext.clone(), - meta_call.clone(), - ); + let meta_tx = + MetaTxFor::::new(signer.clone(), meta_sig, meta_call.clone(), meta_ext.clone()); let call = Call::::dispatch { meta_tx: Box::new(meta_tx) }; let info = call.get_dispatch_info(); diff --git a/substrate/frame/meta-tx/src/lib.rs b/substrate/frame/meta-tx/src/lib.rs index aa73deb56dac9..dc3835bf99596 100644 --- a/substrate/frame/meta-tx/src/lib.rs +++ b/substrate/frame/meta-tx/src/lib.rs @@ -76,42 +76,25 @@ use sp_std::prelude::*; /// The data that is provided and signed by the signer and shared with the relayer. #[derive(Encode, Decode, PartialEq, Eq, TypeInfo, Clone, RuntimeDebug)] pub struct MetaTx { - /// Information regarding the type of the meta transaction. - preamble: Preamble, + /// The signer's address. + address: Address, + /// The signature of the meta transaction. + signature: Signature, /// The target call to be executed on behalf of the signer. call: Call, + /// The extension/s for the meta transaction. + /// + /// The first byte can indicate the version, depending on a concrete `Extension` type. + extension: Extension, } impl MetaTx { /// Create a new meta transaction. - pub fn new_signed( - address: Address, - signature: Signature, - extension: Extension, - call: Call, - ) -> Self { - Self { preamble: Preamble::Signed(address, signature, extension), call } - } - - /// Get the extension reference of the meta transaction. - pub fn extension_as_ref(&self) -> &Extension { - match &self.preamble { - Preamble::Signed(_, _, extension) => extension, - } + pub fn new(address: Address, signature: Signature, call: Call, extension: Extension) -> Self { + Self { address, signature, call, extension } } } -/// Proof of the authenticity of the meta transaction. -/// -/// It could potentially be extended to support other type of meta transaction, similar to the -/// [`sp_runtime::generic::Preamble::Bare`]` transaction extrinsic type. -#[derive(Encode, Decode, PartialEq, Eq, TypeInfo, Clone, RuntimeDebug)] -pub enum Preamble { - /// Meta transaction that contains the signature, signer's address and the extension with it's - /// version. - Signed(Address, Signature, Extension), -} - /// The [`MetaTx`] for the given config. pub type MetaTxFor = MetaTx< <::PublicKey as IdentifyAccount>::AccountId, @@ -211,7 +194,7 @@ pub mod pallet { #[pallet::call_index(0)] #[pallet::weight({ let dispatch_info = meta_tx.call.get_dispatch_info(); - let extension_weight = meta_tx.extension_as_ref().weight(&meta_tx.call); + let extension_weight = meta_tx.extension.weight(&meta_tx.call); // TODO: + dispatch weight ( dispatch_info.call_weight.add(extension_weight), @@ -224,18 +207,16 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let meta_tx_size = meta_tx.encoded_size(); - let (signer, signature, extension) = match meta_tx.preamble { - Preamble::Signed(signer, signature, extension) => (signer, signature, extension), - }; - - let signed_payload = SignedPayloadFor::::new(meta_tx.call, extension) + let signed_payload = SignedPayloadFor::::new(meta_tx.call, meta_tx.extension) .map_err(|_| Error::::Invalid)?; - if !signed_payload.using_encoded(|payload| signature.verify(payload, &signer)) { + if !signed_payload + .using_encoded(|payload| meta_tx.signature.verify(payload, &meta_tx.address)) + { return Err(Error::::BadProof.into()); } - let origin = T::RuntimeOrigin::signed(signer); + let origin = T::RuntimeOrigin::signed(meta_tx.address); let (call, extension, _) = signed_payload.deconstruct(); // `info` with worst-case call weight and extension weight. let info = { diff --git a/substrate/frame/meta-tx/src/tests.rs b/substrate/frame/meta-tx/src/tests.rs index cb349d93cd939..9865363cbec3e 100644 --- a/substrate/frame/meta-tx/src/tests.rs +++ b/substrate/frame/meta-tx/src/tests.rs @@ -80,11 +80,11 @@ fn sign_and_execute_meta_tx() { .using_encoded(|e| alice_keyring.sign(&e)), ); - let meta_tx = MetaTxFor::::new_signed( + let meta_tx = MetaTxFor::::new( alice_account.clone(), meta_tx_sig, - meta_tx_ext.clone(), remark_call.clone(), + meta_tx_ext.clone(), ); // Encode and share with the world. From dd2afa64c40455c9fdce90cb4515b7da8df64abe Mon Sep 17 00:00:00 2001 From: muharem Date: Thu, 7 Nov 2024 18:22:33 +0100 Subject: [PATCH 07/28] update doc --- substrate/frame/meta-tx/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/meta-tx/src/lib.rs b/substrate/frame/meta-tx/src/lib.rs index dc3835bf99596..9974e33e481ad 100644 --- a/substrate/frame/meta-tx/src/lib.rs +++ b/substrate/frame/meta-tx/src/lib.rs @@ -103,7 +103,7 @@ pub type MetaTxFor = MetaTx< ::Extension, >; -/// The [`sp_runtime::generic::SignedPayload`] for the given config. +/// The payload that has been signed for the [`MetaTx`]. pub type SignedPayloadFor = sp_runtime::generic::SignedPayload<::RuntimeCall, ::Extension>; From a44b9cc149eefab58b5862fd2d5cb9beb8c93631 Mon Sep 17 00:00:00 2001 From: muharem Date: Thu, 7 Nov 2024 18:47:59 +0100 Subject: [PATCH 08/28] runtime origin config --- polkadot/runtime/westend/src/lib.rs | 1 + substrate/bin/node/runtime/src/lib.rs | 1 + substrate/frame/meta-tx/src/benchmarking.rs | 22 ++++------------ substrate/frame/meta-tx/src/lib.rs | 28 ++++++++------------- substrate/frame/meta-tx/src/mock.rs | 1 + 5 files changed, 19 insertions(+), 34 deletions(-) diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 388e90e173b13..c5d4cce1ac353 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1566,6 +1566,7 @@ pub type MetaTxExtension = ( ); impl pallet_meta_tx::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; type Signature = Signature; diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index b1df67da6f4e2..4026b3a669faf 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -2334,6 +2334,7 @@ pub type MetaTxExtension = ( ); impl pallet_meta_tx::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; type Signature = Signature; diff --git a/substrate/frame/meta-tx/src/benchmarking.rs b/substrate/frame/meta-tx/src/benchmarking.rs index d75a13f072f8c..a69d12eb6bc06 100644 --- a/substrate/frame/meta-tx/src/benchmarking.rs +++ b/substrate/frame/meta-tx/src/benchmarking.rs @@ -48,7 +48,6 @@ pub mod types { impl BenchmarkHelper, ExtensionOf> for BenchmarkHelperFor where - T::RuntimeOrigin: AsTransactionAuthorizedOrigin, CallOf: From>, { fn create_weightless_call() -> CallOf { @@ -74,26 +73,18 @@ pub mod types { #[derive(TypeInfo, Eq, PartialEq, Clone, Encode, Decode)] #[scale_info(skip_type_params(T))] pub struct WeightlessExtension(core::marker::PhantomData); - impl core::fmt::Debug for WeightlessExtension - where - T::RuntimeOrigin: AsTransactionAuthorizedOrigin, - { + impl core::fmt::Debug for WeightlessExtension { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { write!(f, "WeightlessExtension") } } - impl Default for WeightlessExtension - where - T::RuntimeOrigin: AsTransactionAuthorizedOrigin, - { + impl Default for WeightlessExtension { fn default() -> Self { WeightlessExtension(Default::default()) } } impl TransactionExtension<::RuntimeCall> for WeightlessExtension - where - T::RuntimeOrigin: AsTransactionAuthorizedOrigin, { const IDENTIFIER: &'static str = "WeightlessExtension"; type Implicit = (); @@ -106,10 +97,7 @@ pub mod types { } } -fn assert_last_event(generic_event: ::RuntimeEvent) -where - ::RuntimeOrigin: AsTransactionAuthorizedOrigin, -{ +fn assert_last_event(generic_event: ::RuntimeEvent) { frame_system::Pallet::::assert_last_event(generic_event.into()); } @@ -118,7 +106,6 @@ where T: Config, ::Extension: Default, ::RuntimeCall: From>, - ::RuntimeOrigin: AsTransactionAuthorizedOrigin, )] mod benchmarks { use super::*; @@ -142,7 +129,8 @@ mod benchmarks { let call = Call::::dispatch { meta_tx: Box::new(meta_tx) }; let info = call.get_dispatch_info(); let caller = whitelisted_caller(); - let origin: T::RuntimeOrigin = frame_system::RawOrigin::Signed(caller).into(); + let origin: ::RuntimeOrigin = + frame_system::RawOrigin::Signed(caller).into(); #[block] { diff --git a/substrate/frame/meta-tx/src/lib.rs b/substrate/frame/meta-tx/src/lib.rs index 9974e33e481ad..2790aa640132c 100644 --- a/substrate/frame/meta-tx/src/lib.rs +++ b/substrate/frame/meta-tx/src/lib.rs @@ -62,9 +62,8 @@ use core::ops::Add; use frame_support::{ dispatch::{DispatchInfo, GetDispatchInfo, PostDispatchInfo}, pallet_prelude::*, - traits::OriginTrait, }; -use frame_system::pallet_prelude::*; +use frame_system::{pallet_prelude::*, RawOrigin as SystemOrigin}; use sp_runtime::traits::{ AsTransactionAuthorizedOrigin, DispatchTransaction, Dispatchable, IdentifyAccount, TransactionExtension, Verify, @@ -112,10 +111,11 @@ pub mod pallet { use super::*; #[pallet::config] - pub trait Config: frame_system::Config - where - Self::RuntimeOrigin: AsTransactionAuthorizedOrigin, - { + pub trait Config: frame_system::Config { + /// The overarching origin type. + type RuntimeOrigin: AsTransactionAuthorizedOrigin + + From> + + IsType<::RuntimeOrigin>; /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// The overarching call type. @@ -124,7 +124,7 @@ pub mod pallet { + Dispatchable< Info = DispatchInfo, PostInfo = PostDispatchInfo, - RuntimeOrigin = Self::RuntimeOrigin, + RuntimeOrigin = ::RuntimeOrigin, > + IsType<::RuntimeCall>; /// Signature type for meta transactions. type Signature: Parameter + Verify; @@ -168,10 +168,7 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(crate) fn deposit_event)] - pub enum Event - where - T::RuntimeOrigin: AsTransactionAuthorizedOrigin, - { + pub enum Event { /// A meta transaction has been dispatched. /// /// Contains the dispatch result of the meta transaction along with post-dispatch @@ -183,10 +180,7 @@ pub mod pallet { pub struct Pallet(_); #[pallet::call] - impl Pallet - where - ::RuntimeOrigin: AsTransactionAuthorizedOrigin, - { + impl Pallet { /// Dispatch a given meta transaction. /// /// - `_origin`: Can be any kind of origin. @@ -216,7 +210,7 @@ pub mod pallet { return Err(Error::::BadProof.into()); } - let origin = T::RuntimeOrigin::signed(meta_tx.address); + let origin = SystemOrigin::Signed(meta_tx.address); let (call, extension, _) = signed_payload.deconstruct(); // `info` with worst-case call weight and extension weight. let info = { @@ -227,7 +221,7 @@ pub mod pallet { // dispatch the meta transaction. let meta_dispatch_res = extension - .dispatch_transaction(origin, call, &info, meta_tx_size) + .dispatch_transaction(origin.into(), call, &info, meta_tx_size) .map_err(Error::::from)?; Self::deposit_event(Event::Dispatched { result: meta_dispatch_res }); diff --git a/substrate/frame/meta-tx/src/mock.rs b/substrate/frame/meta-tx/src/mock.rs index 0d68eacb8e692..354111a5a0d09 100644 --- a/substrate/frame/meta-tx/src/mock.rs +++ b/substrate/frame/meta-tx/src/mock.rs @@ -60,6 +60,7 @@ pub type MetaTxExtension = ( pub type MetaTxExtension = crate::benchmarking::types::WeightlessExtension; impl Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; type Signature = Signature; From 4e65e604df24009ff5b83e3a352eaa73312e20ec Mon Sep 17 00:00:00 2001 From: muharem Date: Fri, 8 Nov 2024 14:55:43 +0100 Subject: [PATCH 09/28] tests --- substrate/frame/meta-tx/src/lib.rs | 1 + substrate/frame/meta-tx/src/mock.rs | 4 +- substrate/frame/meta-tx/src/tests.rs | 366 +++++++++++++++++++++------ 3 files changed, 294 insertions(+), 77 deletions(-) diff --git a/substrate/frame/meta-tx/src/lib.rs b/substrate/frame/meta-tx/src/lib.rs index 2790aa640132c..789dea982d122 100644 --- a/substrate/frame/meta-tx/src/lib.rs +++ b/substrate/frame/meta-tx/src/lib.rs @@ -113,6 +113,7 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { /// The overarching origin type. + // We need extra `AsTransactionAuthorizedOrigin` bound to use `DispatchTransaction` impl. type RuntimeOrigin: AsTransactionAuthorizedOrigin + From> + IsType<::RuntimeOrigin>; diff --git a/substrate/frame/meta-tx/src/mock.rs b/substrate/frame/meta-tx/src/mock.rs index 354111a5a0d09..da342436c41d6 100644 --- a/substrate/frame/meta-tx/src/mock.rs +++ b/substrate/frame/meta-tx/src/mock.rs @@ -33,7 +33,7 @@ pub type Balance = u64; pub type Signature = MultiSignature; pub type AccountId = <::Signer as IdentifyAccount>::AccountId; -pub type Extension = ( +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -45,7 +45,7 @@ pub type Extension = ( ); pub type UncheckedExtrinsic = - sp_runtime::generic::UncheckedExtrinsic; + sp_runtime::generic::UncheckedExtrinsic; #[cfg(not(feature = "runtime-benchmarks"))] pub type MetaTxExtension = ( diff --git a/substrate/frame/meta-tx/src/tests.rs b/substrate/frame/meta-tx/src/tests.rs index 9865363cbec3e..795893e354f76 100644 --- a/substrate/frame/meta-tx/src/tests.rs +++ b/substrate/frame/meta-tx/src/tests.rs @@ -20,10 +20,72 @@ use frame_support::traits::tokens::fungible::Inspect; use mock::*; use sp_keyring::AccountKeyring; use sp_runtime::{ + generic::Era, traits::{Applyable, Checkable, Hash, IdentityLookup}, - MultiSignature, + DispatchErrorWithPostInfo, MultiSignature, }; +fn create_tx_extension(account: AccountId) -> TxExtension { + ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckMortality::::from(Era::immortal()), + frame_system::CheckNonce::::from( + frame_system::Pallet::::account(&account).nonce, + ), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(0), + ) +} + +#[cfg(not(feature = "runtime-benchmarks"))] +fn create_meta_tx_extension(account: AccountId) -> MetaTxExtension { + ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckMortality::::from(Era::immortal()), + frame_system::CheckNonce::::from( + frame_system::Pallet::::account(&account).nonce, + ), + ) +} + +#[cfg(feature = "runtime-benchmarks")] +fn create_meta_tx_extension(account: AccountId) -> MetaTxExtension { + crate::benchmarking::types::WeightlessExtension::::default() +} + +fn create_signature>( + call: Call, + ext: Ext, + signer: AccountKeyring, +) -> MultiSignature { + MultiSignature::Sr25519( + (call, ext.clone(), ext.implicit().unwrap()).using_encoded(|e| signer.sign(&e)), + ) +} + +fn force_set_balance(account: AccountId) -> Balance { + let balance = Balances::minimum_balance() * 100; + Balances::force_set_balance(RuntimeOrigin::root(), account.into(), balance).unwrap(); + balance +} + +fn apply_extrinsic(uxt: UncheckedExtrinsic) -> DispatchResultWithPostInfo { + let uxt_info = uxt.get_dispatch_info(); + let uxt_len = uxt.using_encoded(|e| e.len()); + let xt = >>::check( + uxt, + &Default::default(), + ) + .unwrap(); + xt.apply::(&uxt_info, uxt_len).unwrap() +} + #[docify::export] #[test] fn sign_and_execute_meta_tx() { @@ -36,49 +98,16 @@ fn sign_and_execute_meta_tx() { let alice_account: AccountId = alice_keyring.public().into(); let bob_account: AccountId = bob_keyring.public().into(); - let ed = Balances::minimum_balance(); let tx_fee: Balance = (2 * TX_FEE).into(); // base tx fee + weight fee - let alice_balance = ed * 100; - let bob_balance = ed * 100; - - { - // setup initial balances for alice and bob - Balances::force_set_balance( - RuntimeOrigin::root(), - alice_account.clone().into(), - alice_balance, - ) - .unwrap(); - Balances::force_set_balance( - RuntimeOrigin::root(), - bob_account.clone().into(), - bob_balance, - ) - .unwrap(); - } + let alice_balance = force_set_balance(alice_account.clone()); + let bob_balance = force_set_balance(bob_account.clone()); // Alice builds a meta transaction. let remark_call = RuntimeCall::System(frame_system::Call::remark_with_event { remark: vec![1] }); - #[cfg(not(feature = "runtime-benchmarks"))] - let meta_tx_ext: MetaTxExtension = ( - frame_system::CheckNonZeroSender::::new(), - frame_system::CheckSpecVersion::::new(), - frame_system::CheckTxVersion::::new(), - frame_system::CheckGenesis::::new(), - frame_system::CheckMortality::::from(sp_runtime::generic::Era::immortal()), - frame_system::CheckNonce::::from( - frame_system::Pallet::::account(&alice_account).nonce, - ), - ); - #[cfg(feature = "runtime-benchmarks")] - let meta_tx_ext = crate::benchmarking::types::WeightlessExtension::::default(); - - let meta_tx_sig = MultiSignature::Sr25519( - (remark_call.clone(), meta_tx_ext.clone(), meta_tx_ext.implicit().unwrap()) - .using_encoded(|e| alice_keyring.sign(&e)), - ); + let meta_tx_ext = create_meta_tx_extension(alice_account.clone()); + let meta_tx_sig = create_signature(remark_call.clone(), meta_tx_ext.clone(), alice_keyring); let meta_tx = MetaTxFor::::new( alice_account.clone(), @@ -94,60 +123,247 @@ fn sign_and_execute_meta_tx() { let meta_tx = MetaTxFor::::decode(&mut &meta_tx_encoded[..]).unwrap(); let call = RuntimeCall::MetaTx(Call::dispatch { meta_tx: Box::new(meta_tx.clone()) }); - let tx_ext: Extension = ( - frame_system::CheckNonZeroSender::::new(), - frame_system::CheckSpecVersion::::new(), - frame_system::CheckTxVersion::::new(), - frame_system::CheckGenesis::::new(), - frame_system::CheckMortality::::from(sp_runtime::generic::Era::immortal()), - frame_system::CheckNonce::::from( - frame_system::Pallet::::account(&bob_account).nonce, - ), - frame_system::CheckWeight::::new(), - pallet_transaction_payment::ChargeTransactionPayment::::from(0), + let tx_ext = create_tx_extension(bob_account.clone()); + let tx_sig = create_signature(call.clone(), tx_ext.clone(), bob_keyring); + + let uxt = UncheckedExtrinsic::new_signed( + call.clone(), + bob_account.clone(), + tx_sig, + tx_ext.clone(), ); - let tx_sig = MultiSignature::Sr25519( - (call.clone(), tx_ext.clone(), tx_ext.implicit().unwrap()) - .using_encoded(|e| bob_keyring.sign(&e)), + // Check Extrinsic validity and apply it. + let result = apply_extrinsic(uxt); + + // Asserting the results and make sure the weight is correct. + + // TODO: + dispatch weight + let tx_weight = tx_ext.weight(&call) + Weight::from_all(1); + let meta_tx_weight = remark_call + .get_dispatch_info() + .call_weight + .add(meta_tx_ext.weight(&remark_call)); + + assert_eq!( + result, + Ok(PostDispatchInfo { + actual_weight: Some(meta_tx_weight + tx_weight), + pays_fee: Pays::Yes, + }) + ); + + System::assert_has_event(RuntimeEvent::MetaTx(crate::Event::Dispatched { + result: Ok(PostDispatchInfo { + actual_weight: Some(meta_tx_weight), + pays_fee: Pays::Yes, + }), + })); + + System::assert_has_event(RuntimeEvent::System(frame_system::Event::Remarked { + sender: alice_account.clone(), + hash: ::Hashing::hash(&[1]), + })); + + // Alice balance is unchanged, Bob paid the transaction fee. + assert_eq!(alice_balance, Balances::free_balance(alice_account)); + assert_eq!(bob_balance - tx_fee, Balances::free_balance(bob_account)); + }); +} + +#[test] +fn invalid_signature() { + new_test_ext().execute_with(|| { + // meta tx signer + let alice_keyring = AccountKeyring::Alice; + // meta tx relayer + let bob_keyring = AccountKeyring::Bob; + + let alice_account: AccountId = alice_keyring.public().into(); + let bob_account: AccountId = bob_keyring.public().into(); + + let tx_fee: Balance = (2 * TX_FEE).into(); // base tx fee + weight fee + let alice_balance = force_set_balance(alice_account.clone()); + let bob_balance = force_set_balance(bob_account.clone()); + + // Alice builds a meta transaction. + + let remark_call = + RuntimeCall::System(frame_system::Call::remark_with_event { remark: vec![1] }); + let meta_tx_ext = create_meta_tx_extension(alice_account.clone()); + // signature is invalid since it's signed by charlie instead of alice. + let invalid_meta_tx_sig = + create_signature(remark_call.clone(), meta_tx_ext.clone(), AccountKeyring::Charlie); + + let meta_tx = MetaTxFor::::new( + alice_account.clone(), + invalid_meta_tx_sig, + remark_call.clone(), + meta_tx_ext.clone(), ); + // Encode and share with the world. + let meta_tx_encoded = meta_tx.encode(); + + // Bob acts as meta transaction relayer. + + let meta_tx = MetaTxFor::::decode(&mut &meta_tx_encoded[..]).unwrap(); + let call = RuntimeCall::MetaTx(Call::dispatch { meta_tx: Box::new(meta_tx.clone()) }); + let tx_ext = create_tx_extension(bob_account.clone()); + let tx_sig = create_signature(call.clone(), tx_ext.clone(), bob_keyring); + let uxt = UncheckedExtrinsic::new_signed(call, bob_account.clone(), tx_sig, tx_ext); // Check Extrinsic validity and apply it. + let result = apply_extrinsic(uxt); - let uxt_info = uxt.get_dispatch_info(); - let uxt_len = uxt.using_encoded(|e| e.len()); + // Asserting the results. - let xt = >>::check( - uxt, - &Default::default(), - ) - .unwrap(); + assert_eq!(result.unwrap_err().error, Error::::BadProof.into()); - let res = xt.apply::(&uxt_info, uxt_len).unwrap(); + // Alice balance is unchanged, Bob paid the transaction fee. + assert_eq!(alice_balance, Balances::free_balance(alice_account)); + assert_eq!(bob_balance - tx_fee, Balances::free_balance(bob_account)); + }); +} + +#[test] +fn meta_tx_extension_work() { + new_test_ext().execute_with(|| { + // meta tx signer + let alice_keyring = AccountKeyring::Alice; + // meta tx relayer + let bob_keyring = AccountKeyring::Bob; + + let alice_account: AccountId = alice_keyring.public().into(); + let bob_account: AccountId = bob_keyring.public().into(); + + let tx_fee: Balance = (2 * TX_FEE).into(); // base tx fee + weight fee + let alice_balance = force_set_balance(alice_account.clone()); + let bob_balance = force_set_balance(bob_account.clone()); + + // Alice builds a meta transaction. + + let remark_call = + RuntimeCall::System(frame_system::Call::remark_with_event { remark: vec![1] }); + + let meta_tx_ext = create_meta_tx_extension(alice_account.clone()); + let meta_tx_sig = create_signature(remark_call.clone(), meta_tx_ext.clone(), alice_keyring); + + let meta_tx = MetaTxFor::::new( + alice_account.clone(), + meta_tx_sig, + remark_call.clone(), + meta_tx_ext.clone(), + ); + + // Encode and share with the world. + let meta_tx_encoded = meta_tx.encode(); + + // Bob acts as meta transaction relayer. + + let meta_tx = MetaTxFor::::decode(&mut &meta_tx_encoded[..]).unwrap(); + let call = RuntimeCall::MetaTx(Call::dispatch { meta_tx: Box::new(meta_tx.clone()) }); + let tx_ext = create_tx_extension(bob_account.clone()); + let tx_sig = create_signature(call.clone(), tx_ext.clone(), bob_keyring); + + let uxt = UncheckedExtrinsic::new_signed(call.clone(), bob_account.clone(), tx_sig, tx_ext); + + // increment alice's nonce to invalidate the meta tx and verify that the + // meta tx extension works. + frame_system::Pallet::::inc_account_nonce(alice_account.clone()); + + // Check Extrinsic validity and apply it. + let result = apply_extrinsic(uxt); // Asserting the results. + assert_eq!(result.unwrap_err().error, Error::::Stale.into()); - assert!(res.is_ok(), "Dispatch result is not successful: {:?}", res); + // Alice balance is unchanged, Bob paid the transaction fee. + assert_eq!(alice_balance, Balances::free_balance(alice_account)); + assert_eq!(bob_balance - tx_fee, Balances::free_balance(bob_account)); + }); +} - let expected_meta_res = Ok(PostDispatchInfo { - actual_weight: Some( - remark_call - .get_dispatch_info() - .call_weight - .add(meta_tx_ext.weight(&remark_call)), - ), - pays_fee: Pays::Yes, +#[test] +fn meta_tx_call_fails() { + new_test_ext().execute_with(|| { + // meta tx signer + let alice_keyring = AccountKeyring::Alice; + // meta tx relayer + let bob_keyring = AccountKeyring::Bob; + + let alice_account: AccountId = alice_keyring.public().into(); + let bob_account: AccountId = bob_keyring.public().into(); + + let tx_fee: Balance = (2 * TX_FEE).into(); // base tx fee + weight fee + let alice_balance = force_set_balance(alice_account.clone()); + let bob_balance = force_set_balance(bob_account.clone()); + + // Alice builds a meta transaction. + + // transfer more than alice has + let transfer_call = RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { + dest: bob_account.clone(), + value: alice_balance * 2, }); - System::assert_has_event(RuntimeEvent::MetaTx(crate::Event::Dispatched { - result: expected_meta_res, - })); + let meta_tx_ext = create_meta_tx_extension(alice_account.clone()); + let meta_tx_sig = + create_signature(transfer_call.clone(), meta_tx_ext.clone(), alice_keyring); - System::assert_has_event(RuntimeEvent::System(frame_system::Event::Remarked { - sender: alice_account.clone(), - hash: ::Hashing::hash(&[1]), + let meta_tx = MetaTxFor::::new( + alice_account.clone(), + meta_tx_sig, + transfer_call.clone(), + meta_tx_ext.clone(), + ); + + // Encode and share with the world. + let meta_tx_encoded = meta_tx.encode(); + + // Bob acts as meta transaction relayer. + + let meta_tx = MetaTxFor::::decode(&mut &meta_tx_encoded[..]).unwrap(); + let call = RuntimeCall::MetaTx(Call::dispatch { meta_tx: Box::new(meta_tx.clone()) }); + let tx_ext = create_tx_extension(bob_account.clone()); + let tx_sig = create_signature(call.clone(), tx_ext.clone(), bob_keyring); + + let uxt = UncheckedExtrinsic::new_signed( + call.clone(), + bob_account.clone(), + tx_sig, + tx_ext.clone(), + ); + + // Check Extrinsic validity and apply it. + let result = apply_extrinsic(uxt); + + // Asserting the results and make sure the weight is correct. + + // TODO: + dispatch weight + let tx_weight = tx_ext.weight(&call) + Weight::from_all(1); + let meta_tx_weight = transfer_call + .get_dispatch_info() + .call_weight + .add(meta_tx_ext.weight(&transfer_call)); + + assert_eq!( + result, + Ok(PostDispatchInfo { + actual_weight: Some(meta_tx_weight + tx_weight), + pays_fee: Pays::Yes, + }) + ); + + System::assert_has_event(RuntimeEvent::MetaTx(crate::Event::Dispatched { + result: Err(DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { + actual_weight: Some(meta_tx_weight), + pays_fee: Pays::Yes, + }, + error: sp_runtime::DispatchError::Token(sp_runtime::TokenError::FundsUnavailable), + }), })); // Alice balance is unchanged, Bob paid the transaction fee. From 4f8b3cf3e0d20c4d39e1b50abe5eed1dbc816324 Mon Sep 17 00:00:00 2001 From: muharem Date: Fri, 8 Nov 2024 18:32:00 +0100 Subject: [PATCH 10/28] weights --- polkadot/runtime/westend/src/lib.rs | 8 +- polkadot/runtime/westend/src/weights/mod.rs | 1 + .../westend/src/weights/pallet_meta_tx.rs | 57 +++++++++++++ substrate/bin/node/runtime/src/lib.rs | 4 + substrate/frame/meta-tx/src/benchmarking.rs | 8 +- substrate/frame/meta-tx/src/lib.rs | 25 +++--- substrate/frame/meta-tx/src/mock.rs | 1 + substrate/frame/meta-tx/src/tests.rs | 9 +- substrate/frame/meta-tx/src/weights.rs | 84 +++++++++++++++++++ 9 files changed, 177 insertions(+), 20 deletions(-) create mode 100644 polkadot/runtime/westend/src/weights/pallet_meta_tx.rs create mode 100644 substrate/frame/meta-tx/src/weights.rs diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index c5d4cce1ac353..d241422e1681d 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1130,8 +1130,7 @@ impl InstanceFilter for ProxyType { matches!( c, RuntimeCall::Staking(..) | - RuntimeCall::Session(..) | - RuntimeCall::Utility(..) | + RuntimeCall::Session(..) | RuntimeCall::Utility(..) | RuntimeCall::FastUnstake(..) | RuntimeCall::VoterList(..) | RuntimeCall::NominationPools(..) @@ -1566,13 +1565,17 @@ pub type MetaTxExtension = ( ); impl pallet_meta_tx::Config for Runtime { + type WeightInfo = weights::pallet_meta_tx::WeightInfo; type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; type Signature = Signature; type PublicKey = ::Signer; + #[cfg(not(feature = "runtime-benchmarks"))] type Extension = MetaTxExtension; #[cfg(feature = "runtime-benchmarks")] + type Extension = pallet_meta_tx::WeightlessExtension; + #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = pallet_meta_tx::BenchmarkHelperFor; } @@ -1907,6 +1910,7 @@ mod benches { [pallet_vesting, Vesting] [pallet_whitelist, Whitelist] [pallet_asset_rate, AssetRate] + [pallet_meta_tx, MetaTx] // XCM [pallet_xcm, PalletXcmExtrinsicsBenchmark::] // NOTE: Make sure you point to the individual modules below. diff --git a/polkadot/runtime/westend/src/weights/mod.rs b/polkadot/runtime/westend/src/weights/mod.rs index 8c12c1adb9caf..cf26163cf7319 100644 --- a/polkadot/runtime/westend/src/weights/mod.rs +++ b/polkadot/runtime/westend/src/weights/mod.rs @@ -28,6 +28,7 @@ pub mod pallet_fast_unstake; pub mod pallet_identity; pub mod pallet_indices; pub mod pallet_message_queue; +pub mod pallet_meta_tx; pub mod pallet_mmr; pub mod pallet_multisig; pub mod pallet_nomination_pools; diff --git a/polkadot/runtime/westend/src/weights/pallet_meta_tx.rs b/polkadot/runtime/westend/src/weights/pallet_meta_tx.rs new file mode 100644 index 0000000000000..cf182ced3ce37 --- /dev/null +++ b/polkadot/runtime/westend/src/weights/pallet_meta_tx.rs @@ -0,0 +1,57 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Autogenerated weights for `pallet_meta_tx` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-11-08, STEPS: `50`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `cob`, CPU: `` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/debug/polkadot +// benchmark +// pallet +// --chain=westend-dev +// --steps=50 +// --repeat=2 +// --pallet=pallet-meta-tx +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./polkadot/runtime/westend/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_meta_tx`. +pub struct WeightInfo(PhantomData); +impl pallet_meta_tx::WeightInfo for WeightInfo { + fn bare_dispatch() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 138_000_000 picoseconds. + Weight::from_parts(140_000_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } +} diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 4026b3a669faf..cd29ab82697b3 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -2339,8 +2339,11 @@ impl pallet_meta_tx::Config for Runtime { type RuntimeCall = RuntimeCall; type Signature = Signature; type PublicKey = ::Signer; + #[cfg(not(feature = "runtime-benchmarks"))] type Extension = MetaTxExtension; #[cfg(feature = "runtime-benchmarks")] + type Extension = pallet_meta_tx::WeightlessExtension; + #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = pallet_meta_tx::BenchmarkHelperFor; } @@ -2871,6 +2874,7 @@ mod benches { [pallet_example_mbm, PalletExampleMbms] [pallet_asset_conversion_ops, AssetConversionMigration] [pallet_verify_signature, VerifySignature] + [pallet_meta_tx, MetaTx] ); } diff --git a/substrate/frame/meta-tx/src/benchmarking.rs b/substrate/frame/meta-tx/src/benchmarking.rs index a69d12eb6bc06..fda7decb9efe8 100644 --- a/substrate/frame/meta-tx/src/benchmarking.rs +++ b/substrate/frame/meta-tx/src/benchmarking.rs @@ -117,7 +117,9 @@ mod benchmarks { assert!( meta_ext.weight(&meta_call).is_zero(), - "meta tx extension weight for the benchmarks must be zero.", + "meta tx extension weight for the benchmarks must be zero. \ + use `pallet_meta_tx::WeightlessExtension` as `pallet_meta_tx::Config::Extension` \ + with the `runtime-benchmarks` feature enabled.", ); let (signer, meta_sig) = @@ -126,17 +128,17 @@ mod benchmarks { let meta_tx = MetaTxFor::::new(signer.clone(), meta_sig, meta_call.clone(), meta_ext.clone()); - let call = Call::::dispatch { meta_tx: Box::new(meta_tx) }; - let info = call.get_dispatch_info(); let caller = whitelisted_caller(); let origin: ::RuntimeOrigin = frame_system::RawOrigin::Signed(caller).into(); + let call = Call::::dispatch { meta_tx: Box::new(meta_tx) }; #[block] { let _ = call.dispatch_bypass_filter(origin); } + let info = meta_call.get_dispatch_info(); assert_last_event::( Event::Dispatched { result: Ok(PostDispatchInfo { diff --git a/substrate/frame/meta-tx/src/lib.rs b/substrate/frame/meta-tx/src/lib.rs index 789dea982d122..c7be435553e1b 100644 --- a/substrate/frame/meta-tx/src/lib.rs +++ b/substrate/frame/meta-tx/src/lib.rs @@ -54,9 +54,11 @@ mod benchmarking; mod mock; #[cfg(test)] mod tests; +pub mod weights; #[cfg(feature = "runtime-benchmarks")] pub use benchmarking::types::{BenchmarkHelper, BenchmarkHelperFor, WeightlessExtension}; pub use pallet::*; +pub use weights::WeightInfo; use core::ops::Add; use frame_support::{ @@ -112,6 +114,8 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { + /// Weight information for calls in this pallet. + type WeightInfo: WeightInfo; /// The overarching origin type. // We need extra `AsTransactionAuthorizedOrigin` bound to use `DispatchTransaction` impl. type RuntimeOrigin: AsTransactionAuthorizedOrigin @@ -135,10 +139,14 @@ pub mod pallet { type PublicKey: IdentifyAccount; /// Transaction extension/s for meta transactions. /// - /// The extensions that must be present in every meta transaction. This - /// generally includes extensions like [frame_system::CheckSpecVersion], - /// [frame_system::CheckTxVersion], [frame_system::CheckGenesis], - /// [frame_system::CheckMortality], [frame_system::CheckNonce], etc. + /// The extensions that must be present in every meta transaction. This generally includes + /// extensions like [frame_system::CheckSpecVersion], [frame_system::CheckTxVersion], + /// [frame_system::CheckGenesis], [frame_system::CheckMortality], + /// [frame_system::CheckNonce], etc. + /// + /// The extension weight for the benchmarks must be zero. Use + /// `pallet_meta_tx::WeightlessExtension` type with the `runtime-benchmarks` feature + /// enabled. type Extension: TransactionExtension<::RuntimeCall>; /// The benchmark helper provides the necessary functions to create a call and a signature. /// @@ -190,9 +198,9 @@ pub mod pallet { #[pallet::weight({ let dispatch_info = meta_tx.call.get_dispatch_info(); let extension_weight = meta_tx.extension.weight(&meta_tx.call); - // TODO: + dispatch weight + let bare_call_weight = T::WeightInfo::bare_dispatch(); ( - dispatch_info.call_weight.add(extension_weight), + dispatch_info.call_weight.add(extension_weight).add(bare_call_weight), dispatch_info.class, ) })] @@ -232,10 +240,7 @@ pub mod pallet { .map_or_else(|err| err.post_info.actual_weight, |info| info.actual_weight) .unwrap_or(info.total_weight()); - // // TODO: post_info + T::WeightInfo::dispatch_weight_without_call_and_ext_weight() - let dispatch_weight = Weight::from_all(1); - - Ok((Some(dispatch_weight + meta_weight), true.into()).into()) + Ok((Some(T::WeightInfo::bare_dispatch() + meta_weight), true.into()).into()) } } diff --git a/substrate/frame/meta-tx/src/mock.rs b/substrate/frame/meta-tx/src/mock.rs index da342436c41d6..5db726681e832 100644 --- a/substrate/frame/meta-tx/src/mock.rs +++ b/substrate/frame/meta-tx/src/mock.rs @@ -60,6 +60,7 @@ pub type MetaTxExtension = ( pub type MetaTxExtension = crate::benchmarking::types::WeightlessExtension; impl Config for Runtime { + type WeightInfo = (); type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; diff --git a/substrate/frame/meta-tx/src/tests.rs b/substrate/frame/meta-tx/src/tests.rs index 795893e354f76..1870852af12d4 100644 --- a/substrate/frame/meta-tx/src/tests.rs +++ b/substrate/frame/meta-tx/src/tests.rs @@ -55,7 +55,7 @@ fn create_meta_tx_extension(account: AccountId) -> MetaTxExtension { } #[cfg(feature = "runtime-benchmarks")] -fn create_meta_tx_extension(account: AccountId) -> MetaTxExtension { +fn create_meta_tx_extension(_account: AccountId) -> MetaTxExtension { crate::benchmarking::types::WeightlessExtension::::default() } @@ -138,8 +138,7 @@ fn sign_and_execute_meta_tx() { // Asserting the results and make sure the weight is correct. - // TODO: + dispatch weight - let tx_weight = tx_ext.weight(&call) + Weight::from_all(1); + let tx_weight = tx_ext.weight(&call) + ::WeightInfo::bare_dispatch(); let meta_tx_weight = remark_call .get_dispatch_info() .call_weight @@ -227,6 +226,7 @@ fn invalid_signature() { }); } +#[cfg(not(feature = "runtime-benchmarks"))] #[test] fn meta_tx_extension_work() { new_test_ext().execute_with(|| { @@ -341,8 +341,7 @@ fn meta_tx_call_fails() { // Asserting the results and make sure the weight is correct. - // TODO: + dispatch weight - let tx_weight = tx_ext.weight(&call) + Weight::from_all(1); + let tx_weight = tx_ext.weight(&call) + ::WeightInfo::bare_dispatch(); let meta_tx_weight = transfer_call .get_dispatch_info() .call_weight diff --git a/substrate/frame/meta-tx/src/weights.rs b/substrate/frame/meta-tx/src/weights.rs new file mode 100644 index 0000000000000..5204e7a7d101c --- /dev/null +++ b/substrate/frame/meta-tx/src/weights.rs @@ -0,0 +1,84 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for `pallet_meta_tx` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-11-08, STEPS: `50`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `cob`, CPU: `` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` + +// Executed Command: +// ./target/debug/substrate-node +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=2 +// --pallet=pallet-meta-tx +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./substrate/frame/meta-tx/src/weights.rs +// --template=./substrate/.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for `pallet_meta_tx`. +pub trait WeightInfo { + fn bare_dispatch() -> Weight; +} + +/// Weights for `pallet_meta_tx` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + fn bare_dispatch() -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `3997` + // Minimum execution time: 218_000_000 picoseconds. + Weight::from_parts(234_000_000, 3997) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + fn bare_dispatch() -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `3997` + // Minimum execution time: 218_000_000 picoseconds. + Weight::from_parts(234_000_000, 3997) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } +} From 89698eaa81e8a462c89dcdbfdc525be439c1677c Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 11 Nov 2024 13:11:10 +0100 Subject: [PATCH 11/28] fixes --- polkadot/runtime/westend/src/lib.rs | 3 ++- substrate/frame/meta-tx/src/benchmarking.rs | 6 ++++-- substrate/frame/meta-tx/src/lib.rs | 13 ++++++++----- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index d241422e1681d..6016297c8dbdc 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1130,7 +1130,8 @@ impl InstanceFilter for ProxyType { matches!( c, RuntimeCall::Staking(..) | - RuntimeCall::Session(..) | RuntimeCall::Utility(..) | + RuntimeCall::Session(..) | + RuntimeCall::Utility(..) | RuntimeCall::FastUnstake(..) | RuntimeCall::VoterList(..) | RuntimeCall::NominationPools(..) diff --git a/substrate/frame/meta-tx/src/benchmarking.rs b/substrate/frame/meta-tx/src/benchmarking.rs index fda7decb9efe8..f7afeb9163c6e 100644 --- a/substrate/frame/meta-tx/src/benchmarking.rs +++ b/substrate/frame/meta-tx/src/benchmarking.rs @@ -114,9 +114,11 @@ mod benchmarks { fn bare_dispatch() { let meta_call = T::BenchmarkHelper::create_weightless_call(); let meta_ext = T::Extension::default(); + let meta_ext_weight = meta_ext.weight(&meta_call); + #[cfg(not(test))] assert!( - meta_ext.weight(&meta_call).is_zero(), + meta_ext_weight.is_zero(), "meta tx extension weight for the benchmarks must be zero. \ use `pallet_meta_tx::WeightlessExtension` as `pallet_meta_tx::Config::Extension` \ with the `runtime-benchmarks` feature enabled.", @@ -142,7 +144,7 @@ mod benchmarks { assert_last_event::( Event::Dispatched { result: Ok(PostDispatchInfo { - actual_weight: Some(info.call_weight), + actual_weight: Some(info.call_weight + meta_ext_weight), pays_fee: Pays::Yes, }), } diff --git a/substrate/frame/meta-tx/src/lib.rs b/substrate/frame/meta-tx/src/lib.rs index c7be435553e1b..81c5cca1c85ec 100644 --- a/substrate/frame/meta-tx/src/lib.rs +++ b/substrate/frame/meta-tx/src/lib.rs @@ -84,8 +84,6 @@ pub struct MetaTx { /// The target call to be executed on behalf of the signer. call: Call, /// The extension/s for the meta transaction. - /// - /// The first byte can indicate the version, depending on a concrete `Extension` type. extension: Extension, } @@ -132,6 +130,8 @@ pub mod pallet { RuntimeOrigin = ::RuntimeOrigin, > + IsType<::RuntimeCall>; /// Signature type for meta transactions. + /// + /// e.g., [`sp_runtime::MultiSignature`] type Signature: Parameter + Verify; /// Public key type used for signature verification. /// @@ -144,9 +144,12 @@ pub mod pallet { /// [frame_system::CheckGenesis], [frame_system::CheckMortality], /// [frame_system::CheckNonce], etc. /// - /// The extension weight for the benchmarks must be zero. Use - /// `pallet_meta_tx::WeightlessExtension` type with the `runtime-benchmarks` feature - /// enabled. + /// The types implementing the [`TransactionExtension`] trait can be composed into a tuple + /// type that will implement the same trait by piping invocations through each type. + /// + /// In the `runtime-benchmarks` environment the type must implement `Default` trait and the + /// extension weight must be zero. Use `pallet_meta_tx::WeightlessExtension` type when the + /// `runtime-benchmarks` feature enabled. type Extension: TransactionExtension<::RuntimeCall>; /// The benchmark helper provides the necessary functions to create a call and a signature. /// From 08f39289590069c268869e2f92582be8ee14bbd3 Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 11 Nov 2024 13:33:32 +0100 Subject: [PATCH 12/28] prdoc --- Cargo.lock | 2 +- prdoc/pr_6428.prdoc | 27 +++++++++++++++++++++++++++ substrate/frame/meta-tx/Cargo.toml | 2 +- 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 prdoc/pr_6428.prdoc diff --git a/Cargo.lock b/Cargo.lock index 0856a62528ee8..409685f811257 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12032,7 +12032,7 @@ dependencies = [ [[package]] name = "pallet-meta-tx" -version = "0.0.1" +version = "0.1.0" dependencies = [ "docify", "frame-benchmarking", diff --git a/prdoc/pr_6428.prdoc b/prdoc/pr_6428.prdoc new file mode 100644 index 0000000000000..4ef1e2b86a729 --- /dev/null +++ b/prdoc/pr_6428.prdoc @@ -0,0 +1,27 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "FRAME: Meta Transaction" + +doc: + - audience: Runtime Dev + description: | + Introduces the meta-tx pallet that implements Meta Transactions. + + The meta transaction follows a layout similar to that of a regular transaction and can + leverage the same extensions that implement the `TransactionExtension` trait. Once signed and + shared by the signer, it can be relayed by a relayer. The relayer then submits a regular + transaction with the `meta-tx::dispatch` call, passing the signed meta transaction as an + argument. + + To see an example, refer to the `sign_and_execute_meta_tx` test case within the pallet. + +crates: +- name: pallet-meta-tx + bump: major +- name: westend-runtime + bump: major +- name: kitchensink-runtime + bump: major +- name: polkadot-sdk + bump: major diff --git a/substrate/frame/meta-tx/Cargo.toml b/substrate/frame/meta-tx/Cargo.toml index 7ebf021a33394..821f67c0391d5 100644 --- a/substrate/frame/meta-tx/Cargo.toml +++ b/substrate/frame/meta-tx/Cargo.toml @@ -2,7 +2,7 @@ name = "pallet-meta-tx" description = "Dispatch Meta Transaction" license = "Apache-2.0" -version = "0.0.1" +version = "0.1.0" edition.workspace = true authors.workspace = true homepage.workspace = true From 44eff956fec321cf90d4c60ab4ca225203dabd01 Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 11 Nov 2024 13:34:50 +0100 Subject: [PATCH 13/28] weightinfo --- substrate/bin/node/runtime/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 03b1bcc88b550..4b0c2e48b86ca 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -2376,6 +2376,7 @@ pub type MetaTxExtension = ( ); impl pallet_meta_tx::Config for Runtime { + type WeightInfo = (); type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; From 867cbcb11c8b8b879765007b95949e1e7abbb65a Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 11 Nov 2024 15:00:15 +0100 Subject: [PATCH 14/28] propogate features --- substrate/frame/meta-tx/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/substrate/frame/meta-tx/Cargo.toml b/substrate/frame/meta-tx/Cargo.toml index 821f67c0391d5..128ab7c1aabae 100644 --- a/substrate/frame/meta-tx/Cargo.toml +++ b/substrate/frame/meta-tx/Cargo.toml @@ -55,4 +55,6 @@ try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", "sp-runtime/try-runtime", + "pallet-balances/try-runtime", + "pallet-transaction-payment/try-runtime", ] From b9ae6fae3bf03bfa96c31afc72f01c6cfc8888e8 Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 25 Nov 2024 15:33:57 +0100 Subject: [PATCH 15/28] use verify signature pallet, add ext version, update docs --- Cargo.lock | 1 + polkadot/runtime/westend/src/lib.rs | 9 +- substrate/bin/node/runtime/src/lib.rs | 24 +-- .../authorization-tx-extension/src/mock.rs | 16 +- substrate/frame/meta-tx/Cargo.toml | 4 +- substrate/frame/meta-tx/src/benchmarking.rs | 80 +++------- substrate/frame/meta-tx/src/lib.rs | 142 +++++++----------- substrate/frame/meta-tx/src/mock.rs | 85 +++++++---- substrate/frame/meta-tx/src/tests.rs | 129 +++++++++------- .../verify-signature/src/benchmarking.rs | 17 ++- 10 files changed, 238 insertions(+), 269 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0727244701022..2e422e4ea0b52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13762,6 +13762,7 @@ dependencies = [ "frame-system 28.0.0", "pallet-balances 28.0.0", "pallet-transaction-payment 28.0.0", + "pallet-verify-signature", "parity-scale-codec", "scale-info", "serde", diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 856032fcaec76..8e3819872c188 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1140,8 +1140,7 @@ impl InstanceFilter for ProxyType { matches!( c, RuntimeCall::Staking(..) | - RuntimeCall::Session(..) | - RuntimeCall::Utility(..) | + RuntimeCall::Session(..) | RuntimeCall::Utility(..) | RuntimeCall::FastUnstake(..) | RuntimeCall::VoterList(..) | RuntimeCall::NominationPools(..) @@ -1595,17 +1594,11 @@ pub type MetaTxExtension = ( impl pallet_meta_tx::Config for Runtime { type WeightInfo = weights::pallet_meta_tx::WeightInfo; - type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; - type RuntimeCall = RuntimeCall; - type Signature = Signature; - type PublicKey = ::Signer; #[cfg(not(feature = "runtime-benchmarks"))] type Extension = MetaTxExtension; #[cfg(feature = "runtime-benchmarks")] type Extension = pallet_meta_tx::WeightlessExtension; - #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = pallet_meta_tx::BenchmarkHelperFor; } #[frame_support::runtime(legacy_ordering)] diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 44df5c25a759b..9023a1f9f8d39 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -2066,28 +2066,12 @@ impl pallet_transaction_storage::Config for Runtime { ConstU32<{ pallet_transaction_storage::DEFAULT_MAX_TRANSACTION_SIZE }>; } -#[cfg(feature = "runtime-benchmarks")] -pub struct VerifySignatureBenchmarkHelper; -#[cfg(feature = "runtime-benchmarks")] -impl pallet_verify_signature::BenchmarkHelper - for VerifySignatureBenchmarkHelper -{ - fn create_signature(_entropy: &[u8], msg: &[u8]) -> (MultiSignature, AccountId) { - use sp_io::crypto::{sr25519_generate, sr25519_sign}; - use sp_runtime::traits::IdentifyAccount; - let public = sr25519_generate(0.into(), None); - let who_account: AccountId = MultiSigner::Sr25519(public).into_account().into(); - let signature = MultiSignature::Sr25519(sr25519_sign(0.into(), &public, msg).unwrap()); - (signature, who_account) - } -} - impl pallet_verify_signature::Config for Runtime { type Signature = MultiSignature; type AccountIdentifier = MultiSigner; type WeightInfo = pallet_verify_signature::weights::SubstrateWeight; #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = VerifySignatureBenchmarkHelper; + type BenchmarkHelper = (); } impl pallet_whitelist::Config for Runtime { @@ -2381,17 +2365,11 @@ pub type MetaTxExtension = ( impl pallet_meta_tx::Config for Runtime { type WeightInfo = (); - type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; - type RuntimeCall = RuntimeCall; - type Signature = Signature; - type PublicKey = ::Signer; #[cfg(not(feature = "runtime-benchmarks"))] type Extension = MetaTxExtension; #[cfg(feature = "runtime-benchmarks")] type Extension = pallet_meta_tx::WeightlessExtension; - #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = pallet_meta_tx::BenchmarkHelperFor; } #[frame_support::runtime] diff --git a/substrate/frame/examples/authorization-tx-extension/src/mock.rs b/substrate/frame/examples/authorization-tx-extension/src/mock.rs index aa70d12d7d84c..51fca50f535a9 100644 --- a/substrate/frame/examples/authorization-tx-extension/src/mock.rs +++ b/substrate/frame/examples/authorization-tx-extension/src/mock.rs @@ -78,26 +78,12 @@ mod example_runtime { type Lookup = IdentityLookup; } - #[cfg(feature = "runtime-benchmarks")] - pub struct BenchmarkHelper; - #[cfg(feature = "runtime-benchmarks")] - impl pallet_verify_signature::BenchmarkHelper for BenchmarkHelper { - fn create_signature(_entropy: &[u8], msg: &[u8]) -> (MultiSignature, AccountId) { - use sp_io::crypto::{sr25519_generate, sr25519_sign}; - use sp_runtime::traits::IdentifyAccount; - let public = sr25519_generate(0.into(), None); - let who_account: AccountId = MultiSigner::Sr25519(public).into_account().into(); - let signature = MultiSignature::Sr25519(sr25519_sign(0.into(), &public, msg).unwrap()); - (signature, who_account) - } - } - impl pallet_verify_signature::Config for Runtime { type Signature = MultiSignature; type AccountIdentifier = MultiSigner; type WeightInfo = (); #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = BenchmarkHelper; + type BenchmarkHelper = (); } /// Type that enables any pallet to ask for a coowner origin. diff --git a/substrate/frame/meta-tx/Cargo.toml b/substrate/frame/meta-tx/Cargo.toml index 128ab7c1aabae..ac39a63bfc500 100644 --- a/substrate/frame/meta-tx/Cargo.toml +++ b/substrate/frame/meta-tx/Cargo.toml @@ -28,8 +28,9 @@ sp-io = { workspace = true} [dev-dependencies] pallet-balances = { workspace = true, default-features = true } pallet-transaction-payment = { workspace = true, default-features = true } +pallet-verify-signature = { workspace = true, default-features = true } sp-keyring = { workspace = true, default-features = true } -sp-keystore ={ workspace = true, default-features = true } +sp-keystore = { workspace = true, default-features = true } [features] default = ["std"] @@ -50,6 +51,7 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "pallet-verify-signature/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", diff --git a/substrate/frame/meta-tx/src/benchmarking.rs b/substrate/frame/meta-tx/src/benchmarking.rs index f7afeb9163c6e..b592f778401e6 100644 --- a/substrate/frame/meta-tx/src/benchmarking.rs +++ b/substrate/frame/meta-tx/src/benchmarking.rs @@ -21,53 +21,13 @@ use super::*; use frame_benchmarking::v2::*; use frame_support::traits::UnfilteredDispatchable; use sp_runtime::impl_tx_ext_default; -use types::BenchmarkHelper; pub mod types { use super::*; - use sp_io::crypto::{sr25519_generate, sr25519_sign}; - use sp_runtime::{AccountId32, MultiSignature, MultiSigner}; - - /// Trait for the config type that facilitates the benchmarking of the pallet. - pub trait BenchmarkHelper { - /// Create a weightless call for the benchmark. - /// - /// This is used to obtain the weight for the `dispatch` call excluding the weight of the - /// meta transaction's call. - /// - /// E.g.: `frame_system::Call::remark` call with empty `remark`. - fn create_weightless_call() -> Call; - /// Create a signature for a meta transaction. - fn create_signature(call: Call, ext: Extension) -> (AccountId, Signature); - } + use frame_support::traits::OriginTrait; + use sp_runtime::traits::DispatchInfoOf; - type CallOf = ::RuntimeCall; - type ExtensionOf = ::Extension; - - pub struct BenchmarkHelperFor(core::marker::PhantomData); - impl BenchmarkHelper, ExtensionOf> - for BenchmarkHelperFor - where - CallOf: From>, - { - fn create_weightless_call() -> CallOf { - frame_system::Call::::remark { remark: vec![] }.into() - } - fn create_signature(call: CallOf, ext: ExtensionOf) -> (AccountId32, MultiSignature) { - let public = sr25519_generate(0.into(), None); - ( - MultiSigner::Sr25519(public).into_account().into(), - MultiSignature::Sr25519( - sr25519_sign( - 0.into(), - &public, - &(call, ext.clone(), ext.implicit().unwrap()).encode(), - ) - .unwrap(), - ), - ) - } - } + type CallOf = ::RuntimeCall; /// A weightless extension to facilitate the bare dispatch benchmark. #[derive(TypeInfo, Eq, PartialEq, Clone, Encode, Decode)] @@ -83,17 +43,32 @@ pub mod types { WeightlessExtension(Default::default()) } } - impl TransactionExtension<::RuntimeCall> - for WeightlessExtension - { + impl TransactionExtension> for WeightlessExtension { const IDENTIFIER: &'static str = "WeightlessExtension"; type Implicit = (); type Pre = (); type Val = (); - fn weight(&self, _call: &::RuntimeCall) -> Weight { + fn weight(&self, _call: &CallOf) -> Weight { Weight::from_all(0) } - impl_tx_ext_default!(::RuntimeCall; validate prepare); + fn validate( + &self, + mut origin: as Dispatchable>::RuntimeOrigin, + _: &CallOf, + _: &DispatchInfoOf>, + _: usize, + _: (), + _: &impl Encode, + _: TransactionSource, + ) -> Result< + (ValidTransaction, Self::Val, as Dispatchable>::RuntimeOrigin), + TransactionValidityError, + > { + origin.set_caller_from_signed(whitelisted_caller()); + Ok((ValidTransaction::default(), (), origin)) + } + + impl_tx_ext_default!(CallOf; prepare); } } @@ -105,14 +80,13 @@ fn assert_last_event(generic_event: ::RuntimeEvent) { where T: Config, ::Extension: Default, - ::RuntimeCall: From>, )] mod benchmarks { use super::*; #[benchmark] fn bare_dispatch() { - let meta_call = T::BenchmarkHelper::create_weightless_call(); + let meta_call = frame_system::Call::::remark { remark: vec![] }.into(); let meta_ext = T::Extension::default(); let meta_ext_weight = meta_ext.weight(&meta_call); @@ -124,11 +98,7 @@ mod benchmarks { with the `runtime-benchmarks` feature enabled.", ); - let (signer, meta_sig) = - T::BenchmarkHelper::create_signature(meta_call.clone(), meta_ext.clone()); - - let meta_tx = - MetaTxFor::::new(signer.clone(), meta_sig, meta_call.clone(), meta_ext.clone()); + let meta_tx = MetaTxFor::::new(meta_call.clone(), 0u8, meta_ext.clone()); let caller = whitelisted_caller(); let origin: ::RuntimeOrigin = diff --git a/substrate/frame/meta-tx/src/lib.rs b/substrate/frame/meta-tx/src/lib.rs index 81c5cca1c85ec..89922f897fdd8 100644 --- a/substrate/frame/meta-tx/src/lib.rs +++ b/substrate/frame/meta-tx/src/lib.rs @@ -39,24 +39,27 @@ //! //! ## Low-Level / Implementation Details //! -//! The structure of a meta transaction is identical to a regular transaction. It includes the -//! signer's address, signature, target call, and a configurable set of extensions. The signed -//! payload consists of the call, extensions, and any implicit data required by the extensions. -//! This payload can be represented using the [`sp_runtime::generic::SignedPayload`] type. The -//! extensions follow the same [`TransactionExtension`] contract, and common types such as -//! [`frame_system::CheckGenesis`], [`frame_system::CheckMortality`], [`frame_system::CheckNonce`], -//! etc., are applicable in the context of meta transactions. +//! The structure of a meta transaction is identical to the +//! [`General`](sp_runtime::generic::Preamble::General) transaction. +//! It contains the target call along with a configurable set of extensions and its associated +//! version. Typically, these extensions include type like +//! [pallet_verify_signature::VerifySignature], which provides the signer address +//! and the signature of the payload, encompassing the call and the meta-transaction’s +//! configurations, such as its mortality. The extensions follow the same [`TransactionExtension`] +//! contract, and common types such as [`frame_system::CheckGenesis`], +//! [`frame_system::CheckMortality`], [`frame_system::CheckNonce`], etc., are applicable in the +//! context of meta transactions. Check the [mock] setup for the example. #![cfg_attr(not(feature = "std"), no_std)] mod benchmarking; #[cfg(test)] mod mock; -#[cfg(test)] +#[cfg(all(test, not(feature = "runtime-benchmarks")))] mod tests; pub mod weights; #[cfg(feature = "runtime-benchmarks")] -pub use benchmarking::types::{BenchmarkHelper, BenchmarkHelperFor, WeightlessExtension}; +pub use benchmarking::types::WeightlessExtension; pub use pallet::*; pub use weights::WeightInfo; @@ -66,9 +69,11 @@ use frame_support::{ pallet_prelude::*, }; use frame_system::{pallet_prelude::*, RawOrigin as SystemOrigin}; -use sp_runtime::traits::{ - AsTransactionAuthorizedOrigin, DispatchTransaction, Dispatchable, IdentifyAccount, - TransactionExtension, Verify, +use sp_runtime::{ + generic::ExtensionVersion, + traits::{ + AsTransactionAuthorizedOrigin, DispatchTransaction, Dispatchable, TransactionExtension, + }, }; use sp_std::prelude::*; @@ -76,92 +81,60 @@ use sp_std::prelude::*; /// /// The data that is provided and signed by the signer and shared with the relayer. #[derive(Encode, Decode, PartialEq, Eq, TypeInfo, Clone, RuntimeDebug)] -pub struct MetaTx { - /// The signer's address. - address: Address, - /// The signature of the meta transaction. - signature: Signature, +pub struct MetaTx { /// The target call to be executed on behalf of the signer. call: Call, + /// The extension version. + extension_version: ExtensionVersion, /// The extension/s for the meta transaction. extension: Extension, } -impl MetaTx { +impl MetaTx { /// Create a new meta transaction. - pub fn new(address: Address, signature: Signature, call: Call, extension: Extension) -> Self { - Self { address, signature, call, extension } + pub fn new(call: Call, extension_version: ExtensionVersion, extension: Extension) -> Self { + Self { call, extension_version, extension } } } /// The [`MetaTx`] for the given config. -pub type MetaTxFor = MetaTx< - <::PublicKey as IdentifyAccount>::AccountId, - ::Signature, - ::RuntimeCall, - ::Extension, ->; - -/// The payload that has been signed for the [`MetaTx`]. -pub type SignedPayloadFor = - sp_runtime::generic::SignedPayload<::RuntimeCall, ::Extension>; +pub type MetaTxFor = MetaTx<::RuntimeCall, ::Extension>; #[frame_support::pallet] pub mod pallet { use super::*; #[pallet::config] - pub trait Config: frame_system::Config { + pub trait Config: + frame_system::Config< + RuntimeCall: Dispatchable< + Info = DispatchInfo, + PostInfo = PostDispatchInfo, + RuntimeOrigin = ::RuntimeOrigin, + >, + RuntimeOrigin: AsTransactionAuthorizedOrigin + From>, + > + { /// Weight information for calls in this pallet. type WeightInfo: WeightInfo; - /// The overarching origin type. - // We need extra `AsTransactionAuthorizedOrigin` bound to use `DispatchTransaction` impl. - type RuntimeOrigin: AsTransactionAuthorizedOrigin - + From> - + IsType<::RuntimeOrigin>; /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// The overarching call type. - type RuntimeCall: Parameter - + GetDispatchInfo - + Dispatchable< - Info = DispatchInfo, - PostInfo = PostDispatchInfo, - RuntimeOrigin = ::RuntimeOrigin, - > + IsType<::RuntimeCall>; - /// Signature type for meta transactions. - /// - /// e.g., [`sp_runtime::MultiSignature`] - type Signature: Parameter + Verify; - /// Public key type used for signature verification. - /// - /// The `Signer` of the [`Config::Signature`]. - type PublicKey: IdentifyAccount; /// Transaction extension/s for meta transactions. /// /// The extensions that must be present in every meta transaction. This generally includes - /// extensions like [frame_system::CheckSpecVersion], [frame_system::CheckTxVersion], + /// extensions like [pallet_verify_signature::VerifySignature], + /// [frame_system::CheckSpecVersion], [frame_system::CheckTxVersion], /// [frame_system::CheckGenesis], [frame_system::CheckMortality], - /// [frame_system::CheckNonce], etc. + /// [frame_system::CheckNonce], etc. Check the [mock] setup for the example. /// /// The types implementing the [`TransactionExtension`] trait can be composed into a tuple /// type that will implement the same trait by piping invocations through each type. /// - /// In the `runtime-benchmarks` environment the type must implement `Default` trait and the - /// extension weight must be zero. Use `pallet_meta_tx::WeightlessExtension` type when the - /// `runtime-benchmarks` feature enabled. - type Extension: TransactionExtension<::RuntimeCall>; - /// The benchmark helper provides the necessary functions to create a call and a signature. - /// - /// For runtime using [`sp_runtime::MultiSignature`] cryptography use - /// [`benchmarking::types::BenchmarkHelperFor`] implementation. - #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper: benchmarking::types::BenchmarkHelper< - Self::AccountId, - Self::Signature, - ::RuntimeCall, - Self::Extension, - >; + /// In the `runtime-benchmarks` environment the type must implement [`Default`] trait. + /// The extension must provide an origin and the extension's weight must be zero. Use + /// `pallet_meta_tx::WeightlessExtension` type when the `runtime-benchmarks` feature + /// enabled. + type Extension: TransactionExtension<::RuntimeCall>; } #[pallet::error] @@ -174,6 +147,8 @@ pub mod pallet { Stale, /// The meta transactions's birth block is ancient. AncientBirthBlock, + /// The transaction extension did not authorize any origin. + UnknownOrigin, /// The meta transaction is invalid. Invalid, } @@ -211,29 +186,25 @@ pub mod pallet { _origin: OriginFor, meta_tx: Box>, ) -> DispatchResultWithPostInfo { + let origin = SystemOrigin::None; let meta_tx_size = meta_tx.encoded_size(); - - let signed_payload = SignedPayloadFor::::new(meta_tx.call, meta_tx.extension) - .map_err(|_| Error::::Invalid)?; - - if !signed_payload - .using_encoded(|payload| meta_tx.signature.verify(payload, &meta_tx.address)) - { - return Err(Error::::BadProof.into()); - } - - let origin = SystemOrigin::Signed(meta_tx.address); - let (call, extension, _) = signed_payload.deconstruct(); // `info` with worst-case call weight and extension weight. let info = { - let mut info = call.get_dispatch_info(); - info.extension_weight = extension.weight(&call); + let mut info = meta_tx.call.get_dispatch_info(); + info.extension_weight = meta_tx.extension.weight(&meta_tx.call); info }; // dispatch the meta transaction. - let meta_dispatch_res = extension - .dispatch_transaction(origin.into(), call, &info, meta_tx_size) + let meta_dispatch_res = meta_tx + .extension + .dispatch_transaction( + origin.into(), + meta_tx.call, + &info, + meta_tx_size, + meta_tx.extension_version, + ) .map_err(Error::::from)?; Self::deposit_event(Event::Dispatched { result: meta_dispatch_res }); @@ -259,6 +230,7 @@ pub mod pallet { InvalidTransaction::Future => Error::::Future, InvalidTransaction::Stale => Error::::Stale, InvalidTransaction::AncientBirthBlock => Error::::AncientBirthBlock, + InvalidTransaction::UnknownOrigin => Error::::UnknownOrigin, _ => Error::::Invalid, }, } diff --git a/substrate/frame/meta-tx/src/mock.rs b/substrate/frame/meta-tx/src/mock.rs index 5db726681e832..b3d66c7eeb104 100644 --- a/substrate/frame/meta-tx/src/mock.rs +++ b/substrate/frame/meta-tx/src/mock.rs @@ -26,49 +26,77 @@ use frame_support::{ }; use sp_core::ConstU8; use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; -use sp_runtime::{traits::IdentityLookup, MultiSignature}; +use sp_runtime::{ + traits::{IdentifyAccount, IdentityLookup, Verify}, + MultiSignature, +}; pub type Balance = u64; pub type Signature = MultiSignature; pub type AccountId = <::Signer as IdentifyAccount>::AccountId; -pub type TxExtension = ( - frame_system::CheckNonZeroSender, - frame_system::CheckSpecVersion, - frame_system::CheckTxVersion, - frame_system::CheckGenesis, - frame_system::CheckMortality, - frame_system::CheckNonce, - frame_system::CheckWeight, - pallet_transaction_payment::ChargeTransactionPayment, -); +#[cfg(feature = "runtime-benchmarks")] +pub type MetaTxExtension = crate::benchmarking::types::WeightlessExtension; -pub type UncheckedExtrinsic = - sp_runtime::generic::UncheckedExtrinsic; +#[cfg(not(feature = "runtime-benchmarks"))] +pub use tx_ext::*; #[cfg(not(feature = "runtime-benchmarks"))] -pub type MetaTxExtension = ( - frame_system::CheckNonZeroSender, - frame_system::CheckSpecVersion, - frame_system::CheckTxVersion, - frame_system::CheckGenesis, - frame_system::CheckMortality, - frame_system::CheckNonce, -); -#[cfg(feature = "runtime-benchmarks")] -pub type MetaTxExtension = crate::benchmarking::types::WeightlessExtension; +mod tx_ext { + use super::*; + + pub type UncheckedExtrinsic = + sp_runtime::generic::UncheckedExtrinsic; + + /// Transaction extension. + pub type TxExtension = (pallet_verify_signature::VerifySignature, TxBareExtension); + + /// Transaction extension without signature information. + /// + /// Helper type used to decode the part of the extension which should be signed. + pub type TxBareExtension = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckMortality, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, + ); + + pub const META_EXTENSION_VERSION: ExtensionVersion = 0; + + /// Meta transaction extension. + pub type MetaTxExtension = + (pallet_verify_signature::VerifySignature, MetaTxBareExtension); + + /// Meta transaction extension without signature information. + /// + /// Helper type used to decode the part of the extension which should be signed. + pub type MetaTxBareExtension = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckMortality, + frame_system::CheckNonce, + ); +} impl Config for Runtime { type WeightInfo = (); - type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; - type RuntimeCall = RuntimeCall; - type Signature = Signature; - type PublicKey = ::Signer; type Extension = MetaTxExtension; +} + +impl pallet_verify_signature::Config for Runtime { + type Signature = MultiSignature; + type AccountIdentifier = ::Signer; + type WeightInfo = (); #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = crate::benchmarking::types::BenchmarkHelperFor; + type BenchmarkHelper = (); } #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] @@ -103,6 +131,7 @@ construct_runtime!( Balances: pallet_balances, MetaTx: pallet_meta_tx, TxPayment: pallet_transaction_payment, + VerifySignature: pallet_verify_signature, } ); diff --git a/substrate/frame/meta-tx/src/tests.rs b/substrate/frame/meta-tx/src/tests.rs index 1870852af12d4..5d5e167f0f1e2 100644 --- a/substrate/frame/meta-tx/src/tests.rs +++ b/substrate/frame/meta-tx/src/tests.rs @@ -18,6 +18,7 @@ use crate::*; use frame_support::traits::tokens::fungible::Inspect; use mock::*; +use sp_io::hashing::blake2_256; use sp_keyring::AccountKeyring; use sp_runtime::{ generic::Era, @@ -25,7 +26,9 @@ use sp_runtime::{ DispatchErrorWithPostInfo, MultiSignature, }; -fn create_tx_extension(account: AccountId) -> TxExtension { +type VerifySignatureExt = pallet_verify_signature::VerifySignature; + +fn create_tx_bare_ext(account: AccountId) -> TxBareExtension { ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), @@ -40,8 +43,7 @@ fn create_tx_extension(account: AccountId) -> TxExtension { ) } -#[cfg(not(feature = "runtime-benchmarks"))] -fn create_meta_tx_extension(account: AccountId) -> MetaTxExtension { +pub fn create_meta_tx_bare_ext(account: AccountId) -> MetaTxBareExtension { ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), @@ -54,18 +56,14 @@ fn create_meta_tx_extension(account: AccountId) -> MetaTxExtension { ) } -#[cfg(feature = "runtime-benchmarks")] -fn create_meta_tx_extension(_account: AccountId) -> MetaTxExtension { - crate::benchmarking::types::WeightlessExtension::::default() -} - fn create_signature>( call: Call, ext: Ext, signer: AccountKeyring, ) -> MultiSignature { MultiSignature::Sr25519( - (call, ext.clone(), ext.implicit().unwrap()).using_encoded(|e| signer.sign(&e)), + (META_EXTENSION_VERSION, call, ext.clone(), ext.implicit().unwrap()) + .using_encoded(|e| signer.sign(&blake2_256(e))), ) } @@ -106,13 +104,18 @@ fn sign_and_execute_meta_tx() { let remark_call = RuntimeCall::System(frame_system::Call::remark_with_event { remark: vec![1] }); - let meta_tx_ext = create_meta_tx_extension(alice_account.clone()); - let meta_tx_sig = create_signature(remark_call.clone(), meta_tx_ext.clone(), alice_keyring); + let meta_tx_bare_ext = create_meta_tx_bare_ext(alice_account.clone()); + let meta_tx_sig = + create_signature(remark_call.clone(), meta_tx_bare_ext.clone(), alice_keyring); + let meta_tx_ext = ( + VerifySignatureExt::new_with_signature(meta_tx_sig, alice_account.clone()), + // append signed part. + meta_tx_bare_ext, + ); let meta_tx = MetaTxFor::::new( - alice_account.clone(), - meta_tx_sig, remark_call.clone(), + META_EXTENSION_VERSION, meta_tx_ext.clone(), ); @@ -123,16 +126,16 @@ fn sign_and_execute_meta_tx() { let meta_tx = MetaTxFor::::decode(&mut &meta_tx_encoded[..]).unwrap(); let call = RuntimeCall::MetaTx(Call::dispatch { meta_tx: Box::new(meta_tx.clone()) }); - let tx_ext = create_tx_extension(bob_account.clone()); - let tx_sig = create_signature(call.clone(), tx_ext.clone(), bob_keyring); - - let uxt = UncheckedExtrinsic::new_signed( - call.clone(), - bob_account.clone(), - tx_sig, - tx_ext.clone(), + let tx_bare_ext = create_tx_bare_ext(bob_account.clone()); + let tx_sig = create_signature(call.clone(), tx_bare_ext.clone(), bob_keyring); + let tx_ext = ( + VerifySignatureExt::new_with_signature(tx_sig, bob_account.clone()), + // append signed part + tx_bare_ext, ); + let uxt = UncheckedExtrinsic::new_transaction(call.clone(), tx_ext.clone()); + // Check Extrinsic validity and apply it. let result = apply_extrinsic(uxt); @@ -189,15 +192,22 @@ fn invalid_signature() { let remark_call = RuntimeCall::System(frame_system::Call::remark_with_event { remark: vec![1] }); - let meta_tx_ext = create_meta_tx_extension(alice_account.clone()); + let meta_tx_bare_ext = create_meta_tx_bare_ext(alice_account.clone()); // signature is invalid since it's signed by charlie instead of alice. - let invalid_meta_tx_sig = - create_signature(remark_call.clone(), meta_tx_ext.clone(), AccountKeyring::Charlie); + let invalid_meta_tx_sig = create_signature( + remark_call.clone(), + meta_tx_bare_ext.clone(), + AccountKeyring::Charlie, + ); + let meta_tx_ext = ( + VerifySignatureExt::new_with_signature(invalid_meta_tx_sig, alice_account.clone()), + // append signed part. + meta_tx_bare_ext, + ); let meta_tx = MetaTxFor::::new( - alice_account.clone(), - invalid_meta_tx_sig, remark_call.clone(), + META_EXTENSION_VERSION, meta_tx_ext.clone(), ); @@ -208,10 +218,15 @@ fn invalid_signature() { let meta_tx = MetaTxFor::::decode(&mut &meta_tx_encoded[..]).unwrap(); let call = RuntimeCall::MetaTx(Call::dispatch { meta_tx: Box::new(meta_tx.clone()) }); - let tx_ext = create_tx_extension(bob_account.clone()); - let tx_sig = create_signature(call.clone(), tx_ext.clone(), bob_keyring); + let tx_bare_ext = create_tx_bare_ext(bob_account.clone()); + let tx_sig = create_signature(call.clone(), tx_bare_ext.clone(), bob_keyring); + let tx_ext = ( + VerifySignatureExt::new_with_signature(tx_sig, bob_account.clone()), + // append signed part + tx_bare_ext, + ); - let uxt = UncheckedExtrinsic::new_signed(call, bob_account.clone(), tx_sig, tx_ext); + let uxt = UncheckedExtrinsic::new_transaction(call, tx_ext); // Check Extrinsic validity and apply it. let result = apply_extrinsic(uxt); @@ -247,16 +262,17 @@ fn meta_tx_extension_work() { let remark_call = RuntimeCall::System(frame_system::Call::remark_with_event { remark: vec![1] }); - let meta_tx_ext = create_meta_tx_extension(alice_account.clone()); - let meta_tx_sig = create_signature(remark_call.clone(), meta_tx_ext.clone(), alice_keyring); - - let meta_tx = MetaTxFor::::new( - alice_account.clone(), - meta_tx_sig, - remark_call.clone(), - meta_tx_ext.clone(), + let meta_tx_bare_ext = create_meta_tx_bare_ext(alice_account.clone()); + let meta_tx_sig = + create_signature(remark_call.clone(), meta_tx_bare_ext.clone(), alice_keyring); + let meta_tx_ext = ( + VerifySignatureExt::new_with_signature(meta_tx_sig, alice_account.clone()), + // append signed part. + meta_tx_bare_ext, ); + let meta_tx = MetaTxFor::::new(remark_call, META_EXTENSION_VERSION, meta_tx_ext); + // Encode and share with the world. let meta_tx_encoded = meta_tx.encode(); @@ -264,10 +280,15 @@ fn meta_tx_extension_work() { let meta_tx = MetaTxFor::::decode(&mut &meta_tx_encoded[..]).unwrap(); let call = RuntimeCall::MetaTx(Call::dispatch { meta_tx: Box::new(meta_tx.clone()) }); - let tx_ext = create_tx_extension(bob_account.clone()); - let tx_sig = create_signature(call.clone(), tx_ext.clone(), bob_keyring); + let tx_bare_ext = create_tx_bare_ext(bob_account.clone()); + let tx_sig = create_signature(call.clone(), tx_bare_ext.clone(), bob_keyring); + let tx_ext = ( + VerifySignatureExt::new_with_signature(tx_sig, bob_account.clone()), + // append signed part + tx_bare_ext, + ); - let uxt = UncheckedExtrinsic::new_signed(call.clone(), bob_account.clone(), tx_sig, tx_ext); + let uxt = UncheckedExtrinsic::new_transaction(call, tx_ext); // increment alice's nonce to invalidate the meta tx and verify that the // meta tx extension works. @@ -308,14 +329,18 @@ fn meta_tx_call_fails() { value: alice_balance * 2, }); - let meta_tx_ext = create_meta_tx_extension(alice_account.clone()); + let meta_tx_bare_ext = create_meta_tx_bare_ext(alice_account.clone()); let meta_tx_sig = - create_signature(transfer_call.clone(), meta_tx_ext.clone(), alice_keyring); + create_signature(transfer_call.clone(), meta_tx_bare_ext.clone(), alice_keyring); + let meta_tx_ext = ( + VerifySignatureExt::new_with_signature(meta_tx_sig, alice_account.clone()), + // append signed part. + meta_tx_bare_ext, + ); let meta_tx = MetaTxFor::::new( - alice_account.clone(), - meta_tx_sig, transfer_call.clone(), + META_EXTENSION_VERSION, meta_tx_ext.clone(), ); @@ -326,16 +351,16 @@ fn meta_tx_call_fails() { let meta_tx = MetaTxFor::::decode(&mut &meta_tx_encoded[..]).unwrap(); let call = RuntimeCall::MetaTx(Call::dispatch { meta_tx: Box::new(meta_tx.clone()) }); - let tx_ext = create_tx_extension(bob_account.clone()); - let tx_sig = create_signature(call.clone(), tx_ext.clone(), bob_keyring); - - let uxt = UncheckedExtrinsic::new_signed( - call.clone(), - bob_account.clone(), - tx_sig, - tx_ext.clone(), + let tx_bare_ext = create_tx_bare_ext(bob_account.clone()); + let tx_sig = create_signature(call.clone(), tx_bare_ext.clone(), bob_keyring); + let tx_ext = ( + VerifySignatureExt::new_with_signature(tx_sig, bob_account.clone()), + // append signed part + tx_bare_ext, ); + let uxt = UncheckedExtrinsic::new_transaction(call.clone(), tx_ext.clone()); + // Check Extrinsic validity and apply it. let result = apply_extrinsic(uxt); diff --git a/substrate/frame/verify-signature/src/benchmarking.rs b/substrate/frame/verify-signature/src/benchmarking.rs index 99e893e6f6aba..42c08c8888d47 100644 --- a/substrate/frame/verify-signature/src/benchmarking.rs +++ b/substrate/frame/verify-signature/src/benchmarking.rs @@ -32,16 +32,29 @@ use frame_support::{ pallet_prelude::TransactionSource, }; use frame_system::{Call as SystemCall, RawOrigin}; -use sp_io::hashing::blake2_256; +use sp_io::{ + crypto::{sr25519_generate, sr25519_sign}, + hashing::blake2_256, +}; use sp_runtime::{ generic::ExtensionVersion, - traits::{AsTransactionAuthorizedOrigin, DispatchTransaction, Dispatchable}, + traits::{AsTransactionAuthorizedOrigin, DispatchTransaction, Dispatchable, IdentifyAccount}, + AccountId32, MultiSignature, MultiSigner, }; pub trait BenchmarkHelper { fn create_signature(entropy: &[u8], msg: &[u8]) -> (Signature, Signer); } +impl BenchmarkHelper for () { + fn create_signature(_entropy: &[u8], msg: &[u8]) -> (MultiSignature, AccountId32) { + let public = sr25519_generate(0.into(), None); + let who_account: AccountId32 = MultiSigner::Sr25519(public).into_account().into(); + let signature = MultiSignature::Sr25519(sr25519_sign(0.into(), &public, msg).unwrap()); + (signature, who_account) + } +} + #[benchmarks(where T: Config + Send + Sync, T::RuntimeCall: Dispatchable + GetDispatchInfo, From 21744f698c9e649b87b8e92f0e082d811ffccd5a Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 25 Nov 2024 15:36:10 +0100 Subject: [PATCH 16/28] undo fmt --- polkadot/runtime/westend/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 8e3819872c188..fcc94f0774035 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1140,7 +1140,8 @@ impl InstanceFilter for ProxyType { matches!( c, RuntimeCall::Staking(..) | - RuntimeCall::Session(..) | RuntimeCall::Utility(..) | + RuntimeCall::Session(..) | + RuntimeCall::Utility(..) | RuntimeCall::FastUnstake(..) | RuntimeCall::VoterList(..) | RuntimeCall::NominationPools(..) From 27acefbe746c70720c6c6fe4e4fb719a0a2e5cac Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 25 Nov 2024 15:39:55 +0100 Subject: [PATCH 17/28] update prdoc --- prdoc/pr_6428.prdoc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/prdoc/pr_6428.prdoc b/prdoc/pr_6428.prdoc index 4ef1e2b86a729..43391f8082600 100644 --- a/prdoc/pr_6428.prdoc +++ b/prdoc/pr_6428.prdoc @@ -14,7 +14,8 @@ doc: transaction with the `meta-tx::dispatch` call, passing the signed meta transaction as an argument. - To see an example, refer to the `sign_and_execute_meta_tx` test case within the pallet. + To see an example, refer to the mock setup and the `sign_and_execute_meta_tx` test case within + the pallet. crates: - name: pallet-meta-tx @@ -25,3 +26,7 @@ crates: bump: major - name: polkadot-sdk bump: major +- name: pallet-verify-signature + bump: patch +- name: pallet-example-authorization-tx-extension + bump: major From d25b7a33fc038904b7785eab96e0fc863929f392 Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 25 Nov 2024 15:41:24 +0100 Subject: [PATCH 18/28] toml fmt fix --- substrate/frame/meta-tx/Cargo.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/substrate/frame/meta-tx/Cargo.toml b/substrate/frame/meta-tx/Cargo.toml index ac39a63bfc500..4fc0fb9f4bca9 100644 --- a/substrate/frame/meta-tx/Cargo.toml +++ b/substrate/frame/meta-tx/Cargo.toml @@ -23,7 +23,7 @@ frame-system = { workspace = true } sp-core = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } -sp-io = { workspace = true} +sp-io = { workspace = true } [dev-dependencies] pallet-balances = { workspace = true, default-features = true } @@ -33,7 +33,7 @@ sp-keyring = { workspace = true, default-features = true } sp-keystore = { workspace = true, default-features = true } [features] -default = ["std"] +default = ["std"] std = [ "codec/std", "frame-benchmarking?/std", @@ -42,21 +42,21 @@ std = [ "scale-info/std", "serde", "sp-core/std", + "sp-io/std", "sp-runtime/std", "sp-std/std", - "sp-io/std", ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", "pallet-verify-signature/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", - "sp-runtime/try-runtime", "pallet-balances/try-runtime", "pallet-transaction-payment/try-runtime", + "sp-runtime/try-runtime", ] From 4c53277b3b4f68edf397de401bb35b05b7d98d86 Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 25 Nov 2024 15:59:52 +0100 Subject: [PATCH 19/28] fixe --- substrate/frame/meta-tx/Cargo.toml | 6 ++++-- umbrella/src/lib.rs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/substrate/frame/meta-tx/Cargo.toml b/substrate/frame/meta-tx/Cargo.toml index 4fc0fb9f4bca9..a988d32eb70fc 100644 --- a/substrate/frame/meta-tx/Cargo.toml +++ b/substrate/frame/meta-tx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-meta-tx" -description = "Dispatch Meta Transaction" +description = "FRAME pallet enabling meta transactions." license = "Apache-2.0" version = "0.1.0" edition.workspace = true @@ -40,7 +40,6 @@ std = [ "frame-support/std", "frame-system/std", "scale-info/std", - "serde", "sp-core/std", "sp-io/std", "sp-runtime/std", @@ -50,6 +49,8 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-verify-signature/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] @@ -58,5 +59,6 @@ try-runtime = [ "frame-system/try-runtime", "pallet-balances/try-runtime", "pallet-transaction-payment/try-runtime", + "pallet-verify-signature/try-runtime", "sp-runtime/try-runtime", ] diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index 0f29f69e914ba..8e8717b2fedd7 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -487,7 +487,7 @@ pub use pallet_membership; #[cfg(feature = "pallet-message-queue")] pub use pallet_message_queue; -/// FRAME pallet for meta transactions. +/// FRAME pallet enabling meta transactions. #[cfg(feature = "pallet-meta-tx")] pub use pallet_meta_tx; From 1f839f19041a56385daa0861d99e072f0f0d9250 Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 25 Nov 2024 16:07:06 +0100 Subject: [PATCH 20/28] fix --- substrate/frame/meta-tx/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/substrate/frame/meta-tx/Cargo.toml b/substrate/frame/meta-tx/Cargo.toml index a988d32eb70fc..a039643d35352 100644 --- a/substrate/frame/meta-tx/Cargo.toml +++ b/substrate/frame/meta-tx/Cargo.toml @@ -40,6 +40,7 @@ std = [ "frame-support/std", "frame-system/std", "scale-info/std", + "serde?/std", "sp-core/std", "sp-io/std", "sp-runtime/std", From 532edd24bd39e4296f7312ed77e6bca136a91599 Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 25 Nov 2024 16:42:05 +0100 Subject: [PATCH 21/28] remove unstable --- polkadot/runtime/westend/src/lib.rs | 2 ++ substrate/bin/node/runtime/src/lib.rs | 2 ++ substrate/frame/meta-tx/src/lib.rs | 13 +++++++++++++ substrate/frame/meta-tx/src/mock.rs | 2 ++ 4 files changed, 19 insertions(+) diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index fcc94f0774035..1cc7fd97fdad7 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1595,7 +1595,9 @@ pub type MetaTxExtension = ( impl pallet_meta_tx::Config for Runtime { type WeightInfo = weights::pallet_meta_tx::WeightInfo; + type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; #[cfg(not(feature = "runtime-benchmarks"))] type Extension = MetaTxExtension; #[cfg(feature = "runtime-benchmarks")] diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 9023a1f9f8d39..4388189135061 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -2365,7 +2365,9 @@ pub type MetaTxExtension = ( impl pallet_meta_tx::Config for Runtime { type WeightInfo = (); + type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; #[cfg(not(feature = "runtime-benchmarks"))] type Extension = MetaTxExtension; #[cfg(feature = "runtime-benchmarks")] diff --git a/substrate/frame/meta-tx/src/lib.rs b/substrate/frame/meta-tx/src/lib.rs index 89922f897fdd8..15050be0de668 100644 --- a/substrate/frame/meta-tx/src/lib.rs +++ b/substrate/frame/meta-tx/src/lib.rs @@ -117,8 +117,21 @@ pub mod pallet { { /// Weight information for calls in this pallet. type WeightInfo: WeightInfo; + /// The overarching origin type. + // We need extra `AsTransactionAuthorizedOrigin` bound to use `DispatchTransaction` impl. + type RuntimeOrigin: AsTransactionAuthorizedOrigin + + From> + + IsType<::RuntimeOrigin>; /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// The overarching call type. + type RuntimeCall: Parameter + + GetDispatchInfo + + Dispatchable< + Info = DispatchInfo, + PostInfo = PostDispatchInfo, + RuntimeOrigin = ::RuntimeOrigin, + > + IsType<::RuntimeCall>; /// Transaction extension/s for meta transactions. /// /// The extensions that must be present in every meta transaction. This generally includes diff --git a/substrate/frame/meta-tx/src/mock.rs b/substrate/frame/meta-tx/src/mock.rs index b3d66c7eeb104..13b7fc4091945 100644 --- a/substrate/frame/meta-tx/src/mock.rs +++ b/substrate/frame/meta-tx/src/mock.rs @@ -87,7 +87,9 @@ mod tx_ext { impl Config for Runtime { type WeightInfo = (); + type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; type Extension = MetaTxExtension; } From d4e79119438b4d7443e705aae586c4936d1d355d Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 25 Nov 2024 17:41:41 +0100 Subject: [PATCH 22/28] remove unstable --- substrate/frame/meta-tx/src/lib.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/substrate/frame/meta-tx/src/lib.rs b/substrate/frame/meta-tx/src/lib.rs index 15050be0de668..a22470476d1aa 100644 --- a/substrate/frame/meta-tx/src/lib.rs +++ b/substrate/frame/meta-tx/src/lib.rs @@ -105,16 +105,7 @@ pub mod pallet { use super::*; #[pallet::config] - pub trait Config: - frame_system::Config< - RuntimeCall: Dispatchable< - Info = DispatchInfo, - PostInfo = PostDispatchInfo, - RuntimeOrigin = ::RuntimeOrigin, - >, - RuntimeOrigin: AsTransactionAuthorizedOrigin + From>, - > - { + pub trait Config: frame_system::Config { /// Weight information for calls in this pallet. type WeightInfo: WeightInfo; /// The overarching origin type. From f91835fb39cb417e41bee223b700256914cdd66f Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 25 Nov 2024 18:22:49 +0100 Subject: [PATCH 23/28] fix --- substrate/frame/meta-tx/src/benchmarking.rs | 9 +++++---- substrate/frame/meta-tx/src/lib.rs | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/substrate/frame/meta-tx/src/benchmarking.rs b/substrate/frame/meta-tx/src/benchmarking.rs index b592f778401e6..1250dce5efbd1 100644 --- a/substrate/frame/meta-tx/src/benchmarking.rs +++ b/substrate/frame/meta-tx/src/benchmarking.rs @@ -77,10 +77,11 @@ fn assert_last_event(generic_event: ::RuntimeEvent) { } #[benchmarks( - where - T: Config, - ::Extension: Default, - )] + where + T: Config, + ::Extension: Default, + ::RuntimeCall: From>, + )] mod benchmarks { use super::*; diff --git a/substrate/frame/meta-tx/src/lib.rs b/substrate/frame/meta-tx/src/lib.rs index a22470476d1aa..5ca9b9ec05564 100644 --- a/substrate/frame/meta-tx/src/lib.rs +++ b/substrate/frame/meta-tx/src/lib.rs @@ -98,7 +98,7 @@ impl MetaTx { } /// The [`MetaTx`] for the given config. -pub type MetaTxFor = MetaTx<::RuntimeCall, ::Extension>; +pub type MetaTxFor = MetaTx<::RuntimeCall, ::Extension>; #[frame_support::pallet] pub mod pallet { @@ -138,7 +138,7 @@ pub mod pallet { /// The extension must provide an origin and the extension's weight must be zero. Use /// `pallet_meta_tx::WeightlessExtension` type when the `runtime-benchmarks` feature /// enabled. - type Extension: TransactionExtension<::RuntimeCall>; + type Extension: TransactionExtension<::RuntimeCall>; } #[pallet::error] From 17db62772474112669a69779107ab32e8fb6b23c Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 25 Nov 2024 19:49:31 +0100 Subject: [PATCH 24/28] fix docs --- substrate/frame/meta-tx/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/substrate/frame/meta-tx/src/lib.rs b/substrate/frame/meta-tx/src/lib.rs index 5ca9b9ec05564..c8009c7b40e48 100644 --- a/substrate/frame/meta-tx/src/lib.rs +++ b/substrate/frame/meta-tx/src/lib.rs @@ -43,12 +43,12 @@ //! [`General`](sp_runtime::generic::Preamble::General) transaction. //! It contains the target call along with a configurable set of extensions and its associated //! version. Typically, these extensions include type like -//! [pallet_verify_signature::VerifySignature], which provides the signer address +//! `pallet_verify_signature::VerifySignature`, which provides the signer address //! and the signature of the payload, encompassing the call and the meta-transaction’s //! configurations, such as its mortality. The extensions follow the same [`TransactionExtension`] //! contract, and common types such as [`frame_system::CheckGenesis`], //! [`frame_system::CheckMortality`], [`frame_system::CheckNonce`], etc., are applicable in the -//! context of meta transactions. Check the [mock] setup for the example. +//! context of meta transactions. Check the `mock` setup for the example. #![cfg_attr(not(feature = "std"), no_std)] @@ -126,10 +126,10 @@ pub mod pallet { /// Transaction extension/s for meta transactions. /// /// The extensions that must be present in every meta transaction. This generally includes - /// extensions like [pallet_verify_signature::VerifySignature], + /// extensions like `pallet_verify_signature::VerifySignature`, /// [frame_system::CheckSpecVersion], [frame_system::CheckTxVersion], /// [frame_system::CheckGenesis], [frame_system::CheckMortality], - /// [frame_system::CheckNonce], etc. Check the [mock] setup for the example. + /// [frame_system::CheckNonce], etc. Check the `mock` setup for the example. /// /// The types implementing the [`TransactionExtension`] trait can be composed into a tuple /// type that will implement the same trait by piping invocations through each type. From 1cc1ec9d2b287552a282ce348c90455658f3253d Mon Sep 17 00:00:00 2001 From: command-bot <> Date: Mon, 25 Nov 2024 20:55:50 +0000 Subject: [PATCH 25/28] ".git/.scripts/commands/bench/bench.sh" --subcommand=pallet --runtime=dev --target_dir=substrate --features=runtime-benchmarks --pallet=pallet_meta_tx --- substrate/frame/meta-tx/src/weights.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/substrate/frame/meta-tx/src/weights.rs b/substrate/frame/meta-tx/src/weights.rs index 5204e7a7d101c..e51be54fca990 100644 --- a/substrate/frame/meta-tx/src/weights.rs +++ b/substrate/frame/meta-tx/src/weights.rs @@ -18,22 +18,24 @@ //! Autogenerated weights for `pallet_meta_tx` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-11-08, STEPS: `50`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-11-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `cob`, CPU: `` +//! HOSTNAME: `runner-wiukf8gn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: -// ./target/debug/substrate-node +// target/production/substrate-node // benchmark // pallet -// --chain=dev // --steps=50 -// --repeat=2 -// --pallet=pallet-meta-tx +// --repeat=20 // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_meta_tx +// --chain=dev +// --header=./substrate/HEADER-APACHE2 // --output=./substrate/frame/meta-tx/src/weights.rs // --template=./substrate/.maintain/frame-weight-template.hbs @@ -61,8 +63,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3997` - // Minimum execution time: 218_000_000 picoseconds. - Weight::from_parts(234_000_000, 3997) + // Minimum execution time: 13_032_000 picoseconds. + Weight::from_parts(13_665_000, 3997) .saturating_add(T::DbWeight::get().reads(2_u64)) } } @@ -77,8 +79,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3997` - // Minimum execution time: 218_000_000 picoseconds. - Weight::from_parts(234_000_000, 3997) + // Minimum execution time: 13_032_000 picoseconds. + Weight::from_parts(13_665_000, 3997) .saturating_add(RocksDbWeight::get().reads(2_u64)) } } From 3bc51a5773eb639cf6da6019ea015f8ddaadcce9 Mon Sep 17 00:00:00 2001 From: gui Date: Tue, 10 Dec 2024 09:05:44 +0900 Subject: [PATCH 26/28] test --- substrate/frame/meta-tx/src/lib.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/substrate/frame/meta-tx/src/lib.rs b/substrate/frame/meta-tx/src/lib.rs index c8009c7b40e48..3b858c0c5a61d 100644 --- a/substrate/frame/meta-tx/src/lib.rs +++ b/substrate/frame/meta-tx/src/lib.rs @@ -105,7 +105,18 @@ pub mod pallet { use super::*; #[pallet::config] - pub trait Config: frame_system::Config { + pub trait Config: + frame_system::Config< + RuntimeCall: Parameter + + GetDispatchInfo + + Dispatchable< + Info = DispatchInfo, + PostInfo = PostDispatchInfo, + RuntimeOrigin = ::RuntimeOrigin, + >, + RuntimeOrigin: AsTransactionAuthorizedOrigin + From>, + > + { /// Weight information for calls in this pallet. type WeightInfo: WeightInfo; /// The overarching origin type. From cce8293d976d7563064802b1e5ee92714e8a0269 Mon Sep 17 00:00:00 2001 From: gui Date: Tue, 10 Dec 2024 09:30:31 +0900 Subject: [PATCH 27/28] fix --- substrate/frame/meta-tx/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/substrate/frame/meta-tx/src/lib.rs b/substrate/frame/meta-tx/src/lib.rs index 3b858c0c5a61d..a08607592ad5e 100644 --- a/substrate/frame/meta-tx/src/lib.rs +++ b/substrate/frame/meta-tx/src/lib.rs @@ -112,7 +112,6 @@ pub mod pallet { + Dispatchable< Info = DispatchInfo, PostInfo = PostDispatchInfo, - RuntimeOrigin = ::RuntimeOrigin, >, RuntimeOrigin: AsTransactionAuthorizedOrigin + From>, > From f2d8ecab19098f5bbec03f477c44340fc95186ba Mon Sep 17 00:00:00 2001 From: gui Date: Tue, 10 Dec 2024 09:49:51 +0900 Subject: [PATCH 28/28] fix fmt --- substrate/frame/meta-tx/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/substrate/frame/meta-tx/src/lib.rs b/substrate/frame/meta-tx/src/lib.rs index a08607592ad5e..4d545a1c7403f 100644 --- a/substrate/frame/meta-tx/src/lib.rs +++ b/substrate/frame/meta-tx/src/lib.rs @@ -109,10 +109,7 @@ pub mod pallet { frame_system::Config< RuntimeCall: Parameter + GetDispatchInfo - + Dispatchable< - Info = DispatchInfo, - PostInfo = PostDispatchInfo, - >, + + Dispatchable, RuntimeOrigin: AsTransactionAuthorizedOrigin + From>, > {