pezpallet_meta_tx/
lib.rs

1// This file is part of Bizinikiwi.
2
3// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! # Meta Tx (Meta Transaction) Pezpallet
19//!
20//! This pezpallet enables the dispatch of transactions that are authorized by one party (the
21//! signer) and executed by an untrusted third party (the relayer), who covers the transaction fees.
22//!
23//! ## Pezpallet API
24//!
25//! See the [`pezpallet`] module for more information about the interfaces this pezpallet exposes,
26//! including its configuration trait, dispatchables, storage items, events and errors.
27//!
28//! ## Overview
29//!
30//! The pezpallet provides a client-level API, typically not meant for direct use by end users.
31//! A meta transaction, constructed with the help of a wallet, contains a target call, necessary
32//! extensions, and the signer's signature. This transaction is then broadcast, and any interested
33//! relayer can pick it up and execute it. The relayer submits a regular transaction via the
34//! [`dispatch`](`Pezpallet::dispatch`) function, passing the meta transaction as an argument to
35//! execute the target call on behalf of the signer while covering the fees.
36//!
37//! ### Example
38#![doc = docify::embed!("src/tests.rs", sign_and_execute_meta_tx)]
39//!
40//! ## Low-Level / Implementation Details
41//!
42//! The structure of a meta transaction is identical to the
43//! [`General`](pezsp_runtime::generic::Preamble::General) transaction.
44//! It contains the target call along with a configurable set of extensions and its associated
45//! version. Typically, these extensions include type like
46//! `pezpallet_verify_signature::VerifySignature`, which provides the signer address
47//! and the signature of the payload, encompassing the call and the meta-transaction’s
48//! configurations, such as its mortality.  The extensions follow the same [`TransactionExtension`]
49//! contract, and common types such as [`pezframe_system::CheckGenesis`],
50//! [`pezframe_system::CheckMortality`], [`pezframe_system::CheckNonce`], etc., are applicable in
51//! the context of meta transactions. Check the `mock` setup for the example.
52
53#![cfg_attr(not(feature = "std"), no_std)]
54
55mod benchmarking;
56#[cfg(test)]
57mod mock;
58#[cfg(all(test, not(feature = "runtime-benchmarks")))]
59mod tests;
60pub mod weights;
61#[cfg(feature = "runtime-benchmarks")]
62pub use benchmarking::types::WeightlessExtension;
63pub use pezpallet::*;
64pub use weights::WeightInfo;
65mod extension;
66pub use extension::MetaTxMarker;
67
68use core::ops::Add;
69use pezframe_support::{
70	dispatch::{DispatchInfo, GetDispatchInfo, PostDispatchInfo},
71	pezpallet_prelude::*,
72};
73use pezframe_system::{pezpallet_prelude::*, RawOrigin as SystemOrigin};
74use pezsp_runtime::{
75	generic::ExtensionVersion,
76	traits::{
77		AsTransactionAuthorizedOrigin, DispatchTransaction, Dispatchable, TransactionExtension,
78	},
79};
80use pezsp_std::prelude::*;
81
82/// Meta Transaction type.
83///
84/// The data that is provided and signed by the signer and shared with the relayer.
85#[derive(Encode, Decode, PartialEq, Eq, TypeInfo, Clone, RuntimeDebug, DecodeWithMemTracking)]
86pub struct MetaTx<Call, Extension> {
87	/// The target call to be executed on behalf of the signer.
88	call: Call,
89	/// The extension version.
90	extension_version: ExtensionVersion,
91	/// The extension/s for the meta transaction.
92	extension: Extension,
93}
94
95impl<Call, Extension> MetaTx<Call, Extension> {
96	/// Create a new meta transaction.
97	pub fn new(call: Call, extension_version: ExtensionVersion, extension: Extension) -> Self {
98		Self { call, extension_version, extension }
99	}
100}
101
102/// The [`MetaTx`] for the given config.
103pub type MetaTxFor<T> =
104	MetaTx<<T as pezframe_system::Config>::RuntimeCall, <T as Config>::Extension>;
105
106#[pezframe_support::pezpallet]
107pub mod pezpallet {
108	use super::*;
109
110	#[pezpallet::config]
111	pub trait Config:
112		pezframe_system::Config<
113		RuntimeCall: Dispatchable<
114			Info = DispatchInfo,
115			PostInfo = PostDispatchInfo,
116			RuntimeOrigin = <Self as pezframe_system::Config>::RuntimeOrigin,
117		>,
118		RuntimeOrigin: AsTransactionAuthorizedOrigin + From<SystemOrigin<Self::AccountId>>,
119	>
120	{
121		/// Weight information for calls in this pezpallet.
122		type WeightInfo: WeightInfo;
123		/// The overarching event type.
124		#[allow(deprecated)]
125		type RuntimeEvent: From<Event<Self>>
126			+ IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
127		/// Transaction extension/s for meta transactions.
128		///
129		/// The extensions that must be present in every meta transaction. This generally includes
130		/// extensions like `pezpallet_verify_signature::VerifySignature`,
131		/// [pezframe_system::CheckSpecVersion], [pezframe_system::CheckTxVersion],
132		/// [pezframe_system::CheckGenesis], [pezframe_system::CheckMortality],
133		/// [pezframe_system::CheckNonce], etc. Check the `mock` setup for the example.
134		///
135		/// The types implementing the [`TransactionExtension`] trait can be composed into a tuple
136		/// type that will implement the same trait by piping invocations through each type.
137		///
138		/// In the `runtime-benchmarks` environment the type must implement [`Default`] trait.
139		/// The extension must provide an origin and the extension's weight must be zero. Use
140		/// `pezpallet_meta_tx::WeightlessExtension` type when the `runtime-benchmarks` feature
141		/// enabled.
142		type Extension: TransactionExtension<<Self as pezframe_system::Config>::RuntimeCall>;
143	}
144
145	#[pezpallet::error]
146	pub enum Error<T> {
147		/// Invalid proof (e.g. signature).
148		BadProof,
149		/// The meta transaction is not yet valid (e.g. nonce too high).
150		Future,
151		/// The meta transaction is outdated (e.g. nonce too low).
152		Stale,
153		/// The meta transactions's birth block is ancient.
154		AncientBirthBlock,
155		/// The transaction extension did not authorize any origin.
156		UnknownOrigin,
157		/// The meta transaction is invalid.
158		Invalid,
159	}
160
161	#[pezpallet::event]
162	#[pezpallet::generate_deposit(pub(crate) fn deposit_event)]
163	pub enum Event<T: Config> {
164		/// A meta transaction has been dispatched.
165		///
166		/// Contains the dispatch result of the meta transaction along with post-dispatch
167		/// information.
168		Dispatched { result: DispatchResultWithPostInfo },
169	}
170
171	#[pezpallet::pezpallet]
172	pub struct Pezpallet<T>(_);
173
174	#[pezpallet::call]
175	impl<T: Config> Pezpallet<T> {
176		/// Dispatch a given meta transaction.
177		///
178		/// - `_origin`: Can be any kind of origin.
179		/// - `meta_tx`: Meta Transaction with a target call to be dispatched.
180		#[pezpallet::call_index(0)]
181		#[pezpallet::weight({
182			let dispatch_info = meta_tx.call.get_dispatch_info();
183			let extension_weight = meta_tx.extension.weight(&meta_tx.call);
184			let bare_call_weight = T::WeightInfo::bare_dispatch();
185			(
186				dispatch_info.call_weight.add(extension_weight).add(bare_call_weight),
187				dispatch_info.class,
188			)
189		})]
190		pub fn dispatch(
191			_origin: OriginFor<T>,
192			meta_tx: Box<MetaTxFor<T>>,
193		) -> DispatchResultWithPostInfo {
194			let origin = SystemOrigin::None;
195			let meta_tx_size = meta_tx.encoded_size();
196			// `info` with worst-case call weight and extension weight.
197			let info = {
198				let mut info = meta_tx.call.get_dispatch_info();
199				info.extension_weight = meta_tx.extension.weight(&meta_tx.call);
200				info
201			};
202
203			// dispatch the meta transaction.
204			let meta_dispatch_res = meta_tx
205				.extension
206				.dispatch_transaction(
207					origin.into(),
208					meta_tx.call,
209					&info,
210					meta_tx_size,
211					meta_tx.extension_version,
212				)
213				.map_err(Error::<T>::from)?;
214
215			Self::deposit_event(Event::Dispatched { result: meta_dispatch_res });
216
217			// meta weight after possible refunds.
218			let meta_weight = meta_dispatch_res
219				.map_or_else(|err| err.post_info.actual_weight, |info| info.actual_weight)
220				.unwrap_or(info.total_weight());
221
222			Ok((Some(T::WeightInfo::bare_dispatch().saturating_add(meta_weight)), true.into())
223				.into())
224		}
225	}
226
227	/// Implements [`From<TransactionValidityError>`] for [`Error`] by mapping the relevant error
228	/// variants.
229	impl<T> From<TransactionValidityError> for Error<T> {
230		fn from(err: TransactionValidityError) -> Self {
231			use TransactionValidityError::*;
232			match err {
233				Unknown(_) => Error::<T>::Invalid,
234				Invalid(err) => match err {
235					InvalidTransaction::BadProof => Error::<T>::BadProof,
236					InvalidTransaction::Future => Error::<T>::Future,
237					InvalidTransaction::Stale => Error::<T>::Stale,
238					InvalidTransaction::AncientBirthBlock => Error::<T>::AncientBirthBlock,
239					InvalidTransaction::UnknownOrigin => Error::<T>::UnknownOrigin,
240					_ => Error::<T>::Invalid,
241				},
242			}
243		}
244	}
245}