pallet_evm_tracker/
check_nonce.rs1use crate::Config;
2#[cfg(not(feature = "std"))]
3use alloc::vec;
4use core::cmp::max;
5use core::fmt;
6use core::result::Result;
7use frame_support::dispatch::DispatchInfo;
8use frame_support::pallet_prelude::{
9 InvalidTransaction, TransactionLongevity, TransactionValidityError, TypeInfo, ValidTransaction,
10 Weight,
11};
12use frame_support::sp_runtime::traits::{DispatchInfoOf, One, TransactionExtension};
13use frame_support::RuntimeDebugNoBound;
14use parity_scale_codec::{Decode, Encode};
15use sp_runtime::traits::{
16 AsSystemOriginSigner, Dispatchable, PostDispatchInfoOf, ValidateResult, Zero,
17};
18use sp_runtime::transaction_validity::TransactionSource;
19use sp_runtime::DispatchResult;
20#[cfg(feature = "std")]
21use std::vec;
22
23#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
24#[scale_info(skip_type_params(T))]
25pub struct CheckNonce<T: Config>(#[codec(compact)] pub T::Nonce);
26
27impl<T: Config> CheckNonce<T> {
28 pub fn from(nonce: T::Nonce) -> Self {
30 Self(nonce)
31 }
32}
33
34impl<T: Config> fmt::Debug for CheckNonce<T> {
35 #[cfg(feature = "std")]
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 write!(f, "CheckNonce({})", self.0)
38 }
39
40 #[cfg(not(feature = "std"))]
41 fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
42 Ok(())
43 }
44}
45
46#[derive(RuntimeDebugNoBound)]
48pub enum Val<T: frame_system::Config> {
49 CheckNonce((T::AccountId, T::Nonce)),
51 Refund(Weight),
53}
54
55#[derive(RuntimeDebugNoBound)]
58pub enum Pre {
59 NonceChecked,
61 Refund(Weight),
63}
64
65impl<T: Config> TransactionExtension<T::RuntimeCall> for CheckNonce<T>
66where
67 T::RuntimeCall: Dispatchable<Info = DispatchInfo>,
68 <T::RuntimeCall as Dispatchable>::RuntimeOrigin: AsSystemOriginSigner<T::AccountId> + Clone,
69{
70 const IDENTIFIER: &'static str = "CheckNonce";
71 type Implicit = ();
72 type Val = Val<T>;
73 type Pre = Pre;
74
75 fn weight(&self, _: &T::RuntimeCall) -> sp_weights::Weight {
76 <T::ExtensionsWeightInfo as frame_system::ExtensionsWeightInfo>::check_nonce()
77 }
78
79 fn validate(
80 &self,
81 origin: <T as frame_system::Config>::RuntimeOrigin,
82 call: &T::RuntimeCall,
83 _info: &DispatchInfoOf<T::RuntimeCall>,
84 _len: usize,
85 _self_implicit: Self::Implicit,
86 _inherited_implication: &impl Encode,
87 _source: TransactionSource,
88 ) -> ValidateResult<Self::Val, T::RuntimeCall> {
89 let Some(who) = origin.as_system_origin_signer() else {
90 return Ok((Default::default(), Val::Refund(self.weight(call)), origin));
91 };
92
93 let account = frame_system::Account::<T>::get(who);
94 if account.providers.is_zero() && account.sufficients.is_zero() {
95 return Err(InvalidTransaction::Payment.into());
97 }
98 if self.0 < account.nonce {
99 return Err(InvalidTransaction::Stale.into());
100 }
101
102 let provides = vec![Encode::encode(&(who, self.0))];
103 let requires = if account.nonce < self.0 {
104 vec![Encode::encode(&(who, self.0 - One::one()))]
105 } else {
106 vec![]
107 };
108
109 let validity = ValidTransaction {
110 priority: 0,
111 requires,
112 provides,
113 longevity: TransactionLongevity::MAX,
114 propagate: true,
115 };
116
117 Ok((
118 validity,
119 Val::CheckNonce((who.clone(), account.nonce)),
120 origin,
121 ))
122 }
123
124 fn prepare(
125 self,
126 val: Self::Val,
127 _origin: &T::RuntimeOrigin,
128 _call: &T::RuntimeCall,
129 _info: &DispatchInfoOf<T::RuntimeCall>,
130 _len: usize,
131 ) -> Result<Self::Pre, TransactionValidityError> {
132 let (who, mut nonce) = match val {
133 Val::CheckNonce((who, nonce)) => (who, nonce),
134 Val::Refund(weight) => return Ok(Pre::Refund(weight)),
135 };
136
137 let account_nonce = if let Some(tracked_nonce) = crate::AccountNonce::<T>::get(who.clone())
142 {
143 max(tracked_nonce.as_u32().into(), nonce)
144 } else {
145 nonce
146 };
147
148 if self.0 != account_nonce {
149 return Err(if self.0 < nonce {
150 InvalidTransaction::Stale
151 } else {
152 InvalidTransaction::Future
153 }
154 .into());
155 }
156 nonce += T::Nonce::one();
157 frame_system::Account::<T>::mutate(who, |account| account.nonce = nonce);
158 Ok(Pre::NonceChecked)
159 }
160
161 fn post_dispatch_details(
162 pre: Self::Pre,
163 _info: &DispatchInfo,
164 _post_info: &PostDispatchInfoOf<T::RuntimeCall>,
165 _len: usize,
166 _result: &DispatchResult,
167 ) -> Result<Weight, TransactionValidityError> {
168 match pre {
169 Pre::NonceChecked => Ok(Weight::zero()),
170 Pre::Refund(weight) => Ok(weight),
171 }
172 }
173}