diff --git a/Cargo.lock b/Cargo.lock index f12dff812452..380befae5e64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14035,6 +14035,28 @@ dependencies = [ "sp-weights 31.0.0", ] +[[package]] +name = "pallet-meta-tx" +version = "0.1.0" +dependencies = [ + "docify", + "frame-benchmarking 28.0.0", + "frame-support 28.0.0", + "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", + "sp-core 28.0.0", + "sp-io 30.0.0", + "sp-keyring 31.0.0", + "sp-keystore 0.34.0", + "sp-runtime 31.0.1", + "sp-std 14.0.0", +] + [[package]] name = "pallet-migrations" version = "1.0.0" @@ -18770,6 +18792,7 @@ dependencies = [ "pallet-lottery 28.0.0", "pallet-membership 28.0.0", "pallet-message-queue 31.0.0", + "pallet-meta-tx", "pallet-migrations 1.0.0", "pallet-mixnet 0.4.0", "pallet-mmr 27.0.0", @@ -31294,6 +31317,7 @@ dependencies = [ "pallet-indices 28.0.0", "pallet-membership 28.0.0", "pallet-message-queue 31.0.0", + "pallet-meta-tx", "pallet-migrations 1.0.0", "pallet-mmr 27.0.0", "pallet-multisig 28.0.0", @@ -31321,6 +31345,7 @@ dependencies = [ "pallet-transaction-payment-rpc-runtime-api 28.0.0", "pallet-treasury 27.0.0", "pallet-utility 28.0.0", + "pallet-verify-signature", "pallet-vesting 28.0.0", "pallet-whitelist 27.0.0", "pallet-xcm 7.0.0", diff --git a/Cargo.toml b/Cargo.toml index 36c048d77c8e..aa5d3b4b26f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -376,6 +376,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", @@ -954,6 +955,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/polkadot/runtime/westend/Cargo.toml b/polkadot/runtime/westend/Cargo.toml index 3317484419a9..43794cbee854 100644 --- a/polkadot/runtime/westend/Cargo.toml +++ b/polkadot/runtime/westend/Cargo.toml @@ -70,6 +70,7 @@ pallet-identity = { workspace = true } pallet-indices = { workspace = true } pallet-membership = { workspace = true } pallet-message-queue = { workspace = true } +pallet-meta-tx = { workspace = true } pallet-migrations = { workspace = true } pallet-mmr = { workspace = true } pallet-multisig = { workspace = true } @@ -94,6 +95,7 @@ pallet-transaction-payment = { workspace = true } pallet-transaction-payment-rpc-runtime-api = { workspace = true } pallet-treasury = { workspace = true } pallet-utility = { workspace = true } +pallet-verify-signature = { workspace = true } pallet-vesting = { workspace = true } pallet-whitelist = { workspace = true } pallet-xcm = { workspace = true } @@ -168,6 +170,7 @@ std = [ "pallet-indices/std", "pallet-membership/std", "pallet-message-queue/std", + "pallet-meta-tx/std", "pallet-migrations/std", "pallet-mmr/std", "pallet-multisig/std", @@ -195,6 +198,7 @@ std = [ "pallet-transaction-payment/std", "pallet-treasury/std", "pallet-utility/std", + "pallet-verify-signature/std", "pallet-vesting/std", "pallet-whitelist/std", "pallet-xcm-benchmarks?/std", @@ -257,6 +261,7 @@ runtime-benchmarks = [ "pallet-indices/runtime-benchmarks", "pallet-membership/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", + "pallet-meta-tx/runtime-benchmarks", "pallet-migrations/runtime-benchmarks", "pallet-mmr/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", @@ -279,6 +284,7 @@ runtime-benchmarks = [ "pallet-transaction-payment/runtime-benchmarks", "pallet-treasury/runtime-benchmarks", "pallet-utility/runtime-benchmarks", + "pallet-verify-signature/runtime-benchmarks", "pallet-vesting/runtime-benchmarks", "pallet-whitelist/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", @@ -319,6 +325,7 @@ try-runtime = [ "pallet-indices/try-runtime", "pallet-membership/try-runtime", "pallet-message-queue/try-runtime", + "pallet-meta-tx/try-runtime", "pallet-migrations/try-runtime", "pallet-mmr/try-runtime", "pallet-multisig/try-runtime", @@ -340,6 +347,7 @@ try-runtime = [ "pallet-transaction-payment/try-runtime", "pallet-treasury/try-runtime", "pallet-utility/try-runtime", + "pallet-verify-signature/try-runtime", "pallet-vesting/try-runtime", "pallet-whitelist/try-runtime", "pallet-xcm/try-runtime", diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 4d5b56bcd911..066e17195c9f 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -104,7 +104,7 @@ use sp_runtime::{ OpaqueKeys, SaturatedConversion, Verify, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, FixedU128, KeyTypeId, Percent, Permill, + ApplyExtrinsicResult, FixedU128, KeyTypeId, MultiSignature, MultiSigner, Percent, Permill, }; use sp_staking::SessionIndex; #[cfg(any(feature = "std", test))] @@ -1601,6 +1601,35 @@ impl OnSwap for SwapLeases { } } +pub type MetaTxExtension = ( + pallet_verify_signature::VerifySignature, + pallet_meta_tx::MetaTxMarker, + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckMortality, + frame_system::CheckNonce, + frame_metadata_hash_extension::CheckMetadataHash, +); + +impl pallet_meta_tx::Config for Runtime { + type WeightInfo = weights::pallet_meta_tx::WeightInfo; + type RuntimeEvent = RuntimeEvent; + #[cfg(not(feature = "runtime-benchmarks"))] + type Extension = MetaTxExtension; + #[cfg(feature = "runtime-benchmarks")] + type Extension = pallet_meta_tx::WeightlessExtension; +} + +impl pallet_verify_signature::Config for Runtime { + type Signature = MultiSignature; + type AccountIdentifier = MultiSigner; + type WeightInfo = weights::pallet_verify_signature::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); +} + #[frame_support::runtime(legacy_ordering)] mod runtime { #[runtime::runtime] @@ -1794,6 +1823,12 @@ mod runtime { #[runtime::pallet_index(102)] pub type RootTesting = pallet_root_testing; + #[runtime::pallet_index(103)] + pub type MetaTx = pallet_meta_tx::Pallet; + + #[runtime::pallet_index(104)] + pub type VerifySignature = pallet_verify_signature::Pallet; + // BEEFY Bridges support. #[runtime::pallet_index(200)] pub type Beefy = pallet_beefy; @@ -1939,6 +1974,8 @@ mod benches { [pallet_vesting, Vesting] [pallet_whitelist, Whitelist] [pallet_asset_rate, AssetRate] + [pallet_meta_tx, MetaTx] + [pallet_verify_signature, VerifySignature] // 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 efd18b38545a..2cebb9bc0d0a 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_migrations; pub mod pallet_mmr; pub mod pallet_multisig; @@ -44,6 +45,7 @@ pub mod pallet_timestamp; pub mod pallet_transaction_payment; pub mod pallet_treasury; pub mod pallet_utility; +pub mod pallet_verify_signature; pub mod pallet_vesting; pub mod pallet_whitelist; pub mod pallet_xcm; 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 000000000000..cf182ced3ce3 --- /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/polkadot/runtime/westend/src/weights/pallet_verify_signature.rs b/polkadot/runtime/westend/src/weights/pallet_verify_signature.rs new file mode 100644 index 000000000000..8796f0a613da --- /dev/null +++ b/polkadot/runtime/westend/src/weights/pallet_verify_signature.rs @@ -0,0 +1,59 @@ +// 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_verify_signature` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2025-01-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-ys-ssygq-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// target/production/polkadot +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_verify_signature +// --chain=westend-dev +// --header=./polkadot/file_header.txt +// --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_verify_signature`. +pub struct WeightInfo(PhantomData); +impl pallet_verify_signature::WeightInfo for WeightInfo { + fn verify_signature() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 42_915_000 picoseconds. + Weight::from_parts(43_522_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } +} diff --git a/prdoc/pr_6428.prdoc b/prdoc/pr_6428.prdoc new file mode 100644 index 000000000000..43391f808260 --- /dev/null +++ b/prdoc/pr_6428.prdoc @@ -0,0 +1,32 @@ +# 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 mock setup and 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 +- name: pallet-verify-signature + bump: patch +- name: pallet-example-authorization-tx-extension + bump: major diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 202aee37074d..f37574e5a083 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -2142,28 +2142,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 { @@ -2445,6 +2429,27 @@ impl pallet_parameters::Config for Runtime { type WeightInfo = (); } +pub type MetaTxExtension = ( + pallet_verify_signature::VerifySignature, + pallet_meta_tx::MetaTxMarker, + 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 WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + #[cfg(not(feature = "runtime-benchmarks"))] + type Extension = MetaTxExtension; + #[cfg(feature = "runtime-benchmarks")] + type Extension = pallet_meta_tx::WeightlessExtension; +} + #[frame_support::runtime] mod runtime { use super::*; @@ -2719,6 +2724,9 @@ mod runtime { #[runtime::pallet_index(84)] pub type AssetsFreezer = pallet_assets_freezer::Pallet; + + #[runtime::pallet_index(85)] + pub type MetaTx = pallet_meta_tx::Pallet; } impl TryFrom for pallet_revive::Call { @@ -2982,6 +2990,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/examples/authorization-tx-extension/src/mock.rs b/substrate/frame/examples/authorization-tx-extension/src/mock.rs index aa70d12d7d84..51fca50f535a 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 new file mode 100644 index 000000000000..78c18384d2fd --- /dev/null +++ b/substrate/frame/meta-tx/Cargo.toml @@ -0,0 +1,65 @@ +[package] +name = "pallet-meta-tx" +description = "FRAME pallet enabling meta transactions." +license = "Apache-2.0" +version = "0.1.0" +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 } + +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { 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 } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "serde?/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", +] +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", +] +try-runtime = [ + "frame-support/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/substrate/frame/meta-tx/src/benchmarking.rs b/substrate/frame/meta-tx/src/benchmarking.rs new file mode 100644 index 000000000000..8e1ac058a85a --- /dev/null +++ b/substrate/frame/meta-tx/src/benchmarking.rs @@ -0,0 +1,130 @@ +// 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; + +pub mod types { + use super::*; + use frame_support::traits::OriginTrait; + use sp_runtime::traits::DispatchInfoOf; + + type CallOf = ::RuntimeCall; + + /// 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 { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "WeightlessExtension") + } + } + impl Default for WeightlessExtension { + fn default() -> Self { + WeightlessExtension(Default::default()) + } + } + impl TransactionExtension> for WeightlessExtension { + const IDENTIFIER: &'static str = "WeightlessExtension"; + type Implicit = (); + type Pre = (); + type Val = (); + fn weight(&self, _call: &CallOf) -> Weight { + Weight::from_all(0) + } + 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); + } +} + +fn assert_last_event(generic_event: ::RuntimeEvent) { + frame_system::Pallet::::assert_last_event(generic_event.into()); +} + +#[benchmarks( + where + T: Config, + ::Extension: Default, + )] +mod benchmarks { + use super::*; + + #[benchmark] + fn bare_dispatch() { + 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); + + #[cfg(not(test))] + assert!( + 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.", + ); + + let meta_tx = MetaTxFor::::new(meta_call.clone(), 0u8, meta_ext.clone()); + + 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 { + actual_weight: Some(info.call_weight + meta_ext_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/extension.rs b/substrate/frame/meta-tx/src/extension.rs new file mode 100644 index 000000000000..86635e9d39ff --- /dev/null +++ b/substrate/frame/meta-tx/src/extension.rs @@ -0,0 +1,49 @@ +// 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 super::*; +use sp_runtime::impl_tx_ext_default; + +/// This type serves as a marker extension to differentiate meta-transactions from regular +/// transactions. It implements the `TransactionExtension` trait and carries constant implicit data +/// ("_meta_tx"). +#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo, DebugNoBound)] +#[scale_info(skip_type_params(T))] +pub struct MetaTxMarker { + _phantom: core::marker::PhantomData, +} + +impl MetaTxMarker { + /// Creates new `TransactionExtension` with implicit meta tx marked. + pub fn new() -> Self { + Self { _phantom: Default::default() } + } +} + +impl TransactionExtension for MetaTxMarker { + const IDENTIFIER: &'static str = "MetaTxMarker"; + type Implicit = [u8; 8]; + type Val = (); + type Pre = (); + fn implicit(&self) -> Result { + Ok(*b"_meta_tx") + } + fn weight(&self, _: &T::RuntimeCall) -> Weight { + Weight::zero() + } + impl_tx_ext_default!(T::RuntimeCall; validate prepare); +} diff --git a/substrate/frame/meta-tx/src/lib.rs b/substrate/frame/meta-tx/src/lib.rs new file mode 100644 index 000000000000..ca4ccf336261 --- /dev/null +++ b/substrate/frame/meta-tx/src/lib.rs @@ -0,0 +1,242 @@ +// 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 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(all(test, not(feature = "runtime-benchmarks")))] +mod tests; +pub mod weights; +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::types::WeightlessExtension; +pub use pallet::*; +pub use weights::WeightInfo; +mod extension; +pub use extension::MetaTxMarker; + +use core::ops::Add; +use frame_support::{ + dispatch::{DispatchInfo, GetDispatchInfo, PostDispatchInfo}, + pallet_prelude::*, +}; +use frame_system::{pallet_prelude::*, RawOrigin as SystemOrigin}; +use sp_runtime::{ + generic::ExtensionVersion, + traits::{ + AsTransactionAuthorizedOrigin, DispatchTransaction, Dispatchable, TransactionExtension, + }, +}; +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 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 { + /// Create a new meta transaction. + 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<::RuntimeCall, ::Extension>; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::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 event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// 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`, + /// [frame_system::CheckSpecVersion], [frame_system::CheckTxVersion], + /// [frame_system::CheckGenesis], [frame_system::CheckMortality], + /// [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. + /// 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] + 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 transaction extension did not authorize any origin. + UnknownOrigin, + /// The meta transaction is invalid. + Invalid, + } + + #[pallet::event] + #[pallet::generate_deposit(pub(crate) fn deposit_event)] + pub enum Event { + /// 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 { + /// 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); + let bare_call_weight = T::WeightInfo::bare_dispatch(); + ( + dispatch_info.call_weight.add(extension_weight).add(bare_call_weight), + dispatch_info.class, + ) + })] + pub fn dispatch( + _origin: OriginFor, + meta_tx: Box>, + ) -> DispatchResultWithPostInfo { + let origin = SystemOrigin::None; + let meta_tx_size = meta_tx.encoded_size(); + // `info` with worst-case call weight and extension weight. + let info = { + 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 = 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 }); + + // 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()); + + Ok((Some(T::WeightInfo::bare_dispatch().saturating_add(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, + InvalidTransaction::UnknownOrigin => Error::::UnknownOrigin, + _ => 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 000000000000..c361fcceca18 --- /dev/null +++ b/substrate/frame/meta-tx/src/mock.rs @@ -0,0 +1,147 @@ +// 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_keystore::{testing::MemoryKeystore, KeystoreExt}; +use sp_runtime::{ + traits::{IdentifyAccount, IdentityLookup, Verify}, + MultiSignature, +}; + +pub type Balance = u64; + +pub type Signature = MultiSignature; +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; + +#[cfg(feature = "runtime-benchmarks")] +pub type MetaTxExtension = crate::benchmarking::types::WeightlessExtension; + +#[cfg(not(feature = "runtime-benchmarks"))] +pub use tx_ext::*; + +#[cfg(not(feature = "runtime-benchmarks"))] +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 = ( + MetaTxMarker, + 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 RuntimeEvent = RuntimeEvent; + type Extension = MetaTxExtension; +} + +impl pallet_verify_signature::Config for Runtime { + type Signature = MultiSignature; + type AccountIdentifier = ::Signer; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); +} + +#[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, + VerifySignature: pallet_verify_signature, + } +); + +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.register_extension(KeystoreExt::new(MemoryKeystore::new())); + ext +} diff --git a/substrate/frame/meta-tx/src/tests.rs b/substrate/frame/meta-tx/src/tests.rs new file mode 100644 index 000000000000..bde1de2f6091 --- /dev/null +++ b/substrate/frame/meta-tx/src/tests.rs @@ -0,0 +1,398 @@ +// 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_io::hashing::blake2_256; +use sp_keyring::Sr25519Keyring; +use sp_runtime::{ + generic::Era, + traits::{Applyable, Checkable, Hash, IdentityLookup}, + DispatchErrorWithPostInfo, MultiSignature, +}; + +type VerifySignatureExt = pallet_verify_signature::VerifySignature; + +fn create_tx_bare_ext(account: AccountId) -> TxBareExtension { + ( + 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), + ) +} + +pub fn create_meta_tx_bare_ext(account: AccountId) -> MetaTxBareExtension { + ( + MetaTxMarker::new(), + 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, + ), + ) +} + +fn create_signature>( + call: Call, + ext: Ext, + signer: Sr25519Keyring, +) -> MultiSignature { + MultiSignature::Sr25519( + (META_EXTENSION_VERSION, call, ext.clone(), ext.implicit().unwrap()) + .using_encoded(|e| signer.sign(&blake2_256(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() { + new_test_ext().execute_with(|| { + // meta tx signer + let alice_keyring = Sr25519Keyring::Alice; + // meta tx relayer + let bob_keyring = Sr25519Keyring::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_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.clone(), + META_EXTENSION_VERSION, + 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_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); + + // Asserting the results and make sure the weight is correct. + + let tx_weight = tx_ext.weight(&call) + ::WeightInfo::bare_dispatch(); + 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 = Sr25519Keyring::Alice; + // meta tx relayer + let bob_keyring = Sr25519Keyring::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_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_bare_ext.clone(), + Sr25519Keyring::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( + remark_call.clone(), + META_EXTENSION_VERSION, + 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_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, tx_ext); + + // Check Extrinsic validity and apply it. + let result = apply_extrinsic(uxt); + + // Asserting the results. + + assert_eq!(result.unwrap_err().error, Error::::BadProof.into()); + + // 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)); + }); +} + +#[cfg(not(feature = "runtime-benchmarks"))] +#[test] +fn meta_tx_extension_work() { + new_test_ext().execute_with(|| { + // meta tx signer + let alice_keyring = Sr25519Keyring::Alice; + // meta tx relayer + let bob_keyring = Sr25519Keyring::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_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(); + + // 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_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, 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()); + + // 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_call_fails() { + new_test_ext().execute_with(|| { + // meta tx signer + let alice_keyring = Sr25519Keyring::Alice; + // meta tx relayer + let bob_keyring = Sr25519Keyring::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, + }); + + let meta_tx_bare_ext = create_meta_tx_bare_ext(alice_account.clone()); + let meta_tx_sig = + 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( + transfer_call.clone(), + META_EXTENSION_VERSION, + 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_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); + + // Asserting the results and make sure the weight is correct. + + let tx_weight = tx_ext.weight(&call) + ::WeightInfo::bare_dispatch(); + 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. + assert_eq!(alice_balance, Balances::free_balance(alice_account)); + assert_eq!(bob_balance - tx_fee, Balances::free_balance(bob_account)); + }); +} diff --git a/substrate/frame/meta-tx/src/weights.rs b/substrate/frame/meta-tx/src/weights.rs new file mode 100644 index 000000000000..fdc13d115941 --- /dev/null +++ b/substrate/frame/meta-tx/src/weights.rs @@ -0,0 +1,86 @@ +// 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: 2025-01-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-ys-ssygq-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` + +// Executed Command: +// target/production/substrate-node +// benchmark +// pallet +// --steps=50 +// --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 + +#![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: 13_110_000 picoseconds. + Weight::from_parts(13_605_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: 13_110_000 picoseconds. + Weight::from_parts(13_605_000, 3997) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } +} diff --git a/substrate/frame/verify-signature/src/benchmarking.rs b/substrate/frame/verify-signature/src/benchmarking.rs index 99e893e6f6ab..42c08c8888d4 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, diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index fc0b2d5a140e..44c6f237e7cb 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -100,6 +100,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", @@ -293,6 +294,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-mmr?/runtime-benchmarks", "pallet-multisig?/runtime-benchmarks", @@ -429,6 +431,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", @@ -499,6 +502,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", @@ -546,7 +550,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-weight-reclaim", "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-rewards", "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-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-weight-reclaim", "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-rewards", "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-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", @@ -1093,6 +1097,11 @@ default-features = false optional = true path = "../substrate/frame/message-queue" +[dependencies.pallet-meta-tx] +default-features = false +optional = true +path = "../substrate/frame/meta-tx" + [dependencies.pallet-migrations] default-features = false optional = true diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index a132f16a2c33..de615e040eeb 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -495,6 +495,10 @@ pub use pallet_membership; #[cfg(feature = "pallet-message-queue")] pub use pallet_message_queue; +/// FRAME pallet enabling 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;