pallet_evm_tracker/
check_nonce.rs

1use 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    /// utility constructor. Used only in client/factory code.
29    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/// Operation to perform from `validate` to `prepare` in [`frame_system::CheckNonce`] transaction extension.
47#[derive(RuntimeDebugNoBound)]
48pub enum Val<T: frame_system::Config> {
49    /// Account and its nonce to check for.
50    CheckNonce((T::AccountId, T::Nonce)),
51    /// Weight to refund.
52    Refund(Weight),
53}
54
55/// Operation to perform from `prepare` to `post_dispatch_details` in [`frame_system::CheckNonce`] transaction
56/// extension.
57#[derive(RuntimeDebugNoBound)]
58pub enum Pre {
59    /// The transaction extension weight should not be refunded.
60    NonceChecked,
61    /// The transaction extension weight should be refunded.
62    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            // Nonce storage not paid for
96            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        // if a sender sends an evm transaction first and substrate transaction
138        // after with same nonce, then reject the second transaction
139        // if sender reverse the transaction types, substrate first and evm second,
140        // evm transaction will be rejected, since substrate updates nonce in pre_dispatch.
141        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}