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}