pallet_evm_tracker/
check_nonce.rs1use crate::Config;
4use core::cmp::max;
5use core::fmt;
6use core::result::Result;
7use domain_runtime_primitives::ERR_EVM_NONCE_OVERFLOW;
8use frame_support::RuntimeDebugNoBound;
9use frame_support::dispatch::DispatchInfo;
10use frame_support::pallet_prelude::{
11 CheckedAdd, InvalidTransaction, TransactionLongevity, TransactionValidityError, TypeInfo,
12 ValidTransaction, Weight,
13};
14use frame_support::sp_runtime::traits::{DispatchInfoOf, One, TransactionExtension};
15use parity_scale_codec::{Decode, Encode};
16use scale_info::prelude::vec;
17use sp_runtime::traits::{
18 AsSystemOriginSigner, Dispatchable, PostDispatchInfoOf, ValidateResult, Zero,
19};
20use sp_runtime::transaction_validity::TransactionSource;
21use sp_runtime::{DispatchResult, Saturating};
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()
80 }
81
82 fn validate(
83 &self,
84 origin: <T as frame_system::Config>::RuntimeOrigin,
85 call: &T::RuntimeCall,
86 _info: &DispatchInfoOf<T::RuntimeCall>,
87 _len: usize,
88 _self_implicit: Self::Implicit,
89 _inherited_implication: &impl Encode,
90 _source: TransactionSource,
91 ) -> ValidateResult<Self::Val, T::RuntimeCall> {
92 let Some(who) = origin.as_system_origin_signer() else {
93 return Ok((Default::default(), Val::Refund(self.weight(call)), origin));
94 };
95
96 let account = frame_system::Account::<T>::get(who);
97 if account.providers.is_zero() && account.sufficients.is_zero() {
98 return Err(InvalidTransaction::Payment.into());
100 }
101 if self.0 < account.nonce {
102 return Err(InvalidTransaction::Stale.into());
103 }
104
105 let provides = vec![Encode::encode(&(who, self.0))];
106 let requires = if account.nonce < self.0 {
107 vec![Encode::encode(&(who, self.0.saturating_sub(One::one())))]
108 } else {
109 vec![]
110 };
111
112 let validity = ValidTransaction {
113 priority: 0,
114 requires,
115 provides,
116 longevity: TransactionLongevity::MAX,
117 propagate: true,
118 };
119
120 Ok((
121 validity,
122 Val::CheckNonce((who.clone(), account.nonce)),
123 origin,
124 ))
125 }
126
127 fn prepare(
128 self,
129 val: Self::Val,
130 _origin: &T::RuntimeOrigin,
131 _call: &T::RuntimeCall,
132 _info: &DispatchInfoOf<T::RuntimeCall>,
133 _len: usize,
134 ) -> Result<Self::Pre, TransactionValidityError> {
135 let (who, mut nonce) = match val {
136 Val::CheckNonce((who, nonce)) => (who, nonce),
137 Val::Refund(weight) => return Ok(Pre::Refund(weight)),
138 };
139
140 let account_nonce = if let Some(tracked_nonce) = crate::AccountNonce::<T>::get(who.clone())
145 {
146 max(tracked_nonce.as_u32().into(), nonce)
147 } else {
148 nonce
149 };
150
151 if self.0 != account_nonce {
152 return Err(if self.0 < nonce {
153 InvalidTransaction::Stale
154 } else {
155 InvalidTransaction::Future
156 }
157 .into());
158 }
159 nonce = nonce
160 .checked_add(&T::Nonce::one())
161 .ok_or(InvalidTransaction::Custom(ERR_EVM_NONCE_OVERFLOW))?;
162 frame_system::Account::<T>::mutate(who, |account| account.nonce = nonce);
163 Ok(Pre::NonceChecked)
164 }
165
166 fn post_dispatch_details(
167 pre: Self::Pre,
168 _info: &DispatchInfo,
169 _post_info: &PostDispatchInfoOf<T::RuntimeCall>,
170 _len: usize,
171 _result: &DispatchResult,
172 ) -> Result<Weight, TransactionValidityError> {
173 match pre {
174 Pre::NonceChecked => Ok(Weight::zero()),
175 Pre::Refund(weight) => Ok(weight),
176 }
177 }
178}