common_runtime/extensions/
check_nonce.rs1use frame_system::Config;
20use parity_scale_codec::{Decode, DecodeWithMemTracking, Encode};
21
22use frame_support::{
23 dispatch::{DispatchInfo, Pays},
24 sp_runtime, RuntimeDebugNoBound,
25};
26use scale_info::TypeInfo;
27use sp_runtime::{
28 traits::{
29 AsSystemOriginSigner, DispatchInfoOf, Dispatchable, One, PostDispatchInfoOf,
30 TransactionExtension, ValidateResult,
31 },
32 transaction_validity::{
33 InvalidTransaction, TransactionLongevity, TransactionSource, TransactionValidityError,
34 ValidTransaction,
35 },
36 DispatchResult, Weight,
37};
38extern crate alloc;
39use alloc::vec;
40
41#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
49#[scale_info(skip_type_params(T))]
50pub struct CheckNonce<T: Config>(#[codec(compact)] pub T::Nonce);
51
52impl<T: Config> CheckNonce<T> {
53 pub fn from(nonce: T::Nonce) -> Self {
55 Self(nonce)
56 }
57}
58
59impl<T: Config> core::fmt::Debug for CheckNonce<T> {
60 #[cfg(feature = "std")]
61 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
62 write!(f, "CheckNonce({})", self.0)
63 }
64
65 #[cfg(not(feature = "std"))]
66 fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
67 Ok(())
68 }
69}
70
71#[derive(RuntimeDebugNoBound)]
74pub enum Val<T: Config> {
75 CheckNonce((T::AccountId, T::Nonce)),
77 Refund(Weight),
79}
80
81#[derive(RuntimeDebugNoBound)]
84pub enum Pre {
85 NonceChecked,
87 Refund(Weight),
89}
90
91impl<T: Config> TransactionExtension<T::RuntimeCall> for CheckNonce<T>
92where
93 T::RuntimeCall: Dispatchable<Info = DispatchInfo>,
94 <T::RuntimeCall as Dispatchable>::RuntimeOrigin: AsSystemOriginSigner<T::AccountId> + Clone,
95{
96 const IDENTIFIER: &'static str = "CheckNonce";
97 type Implicit = ();
98 type Val = Val<T>;
99 type Pre = Pre;
100
101 fn weight(&self, _call: &T::RuntimeCall) -> Weight {
102 <T::ExtensionsWeightInfo as frame_system::ExtensionsWeightInfo>::check_nonce()
103 }
104
105 fn validate(
106 &self,
107 origin: <T as Config>::RuntimeOrigin,
108 call: &T::RuntimeCall,
109 _info: &DispatchInfoOf<T::RuntimeCall>,
110 _len: usize,
111 _self_implicit: Self::Implicit,
112 _inherited_implication: &impl Encode,
113 _source: TransactionSource,
114 ) -> ValidateResult<Self::Val, T::RuntimeCall> {
115 let Some(who) = origin.as_system_origin_signer() else {
117 return Ok((ValidTransaction::default(), Val::Refund(self.weight(call)), origin));
118 };
119
120 let (valid_transaction, account_nonce) = validate_nonce::<T>(who, self.0)?;
121
122 Ok((valid_transaction, Val::CheckNonce((who.clone(), account_nonce)), origin))
123 }
124
125 fn prepare(
126 self,
127 val: Self::Val,
128 _origin: &<T::RuntimeCall as Dispatchable>::RuntimeOrigin,
129 _call: &T::RuntimeCall,
130 info: &DispatchInfoOf<T::RuntimeCall>,
131 _len: usize,
132 ) -> Result<Self::Pre, TransactionValidityError> {
133 let (who, nonce) = match val {
134 Val::CheckNonce((who, nonce)) => (who, nonce),
135 Val::Refund(weight) => return Ok(Pre::Refund(weight)),
136 };
137
138 prepare_nonce::<T>(&who, nonce, info.pays_fee)?;
140
141 Ok(Pre::NonceChecked)
142 }
143
144 fn post_dispatch_details(
145 pre: Self::Pre,
146 _info: &DispatchInfo,
147 _post_info: &PostDispatchInfoOf<T::RuntimeCall>,
148 _len: usize,
149 _result: &DispatchResult,
150 ) -> Result<Weight, TransactionValidityError> {
151 match pre {
152 Pre::NonceChecked => Ok(Weight::zero()),
153 Pre::Refund(weight) => Ok(weight),
154 }
155 }
156}
157
158pub fn prepare_nonce<T: Config>(
160 who: &T::AccountId,
161 nonce: T::Nonce,
162 pays_fee: Pays,
163) -> Result<(), TransactionValidityError> {
164 let mut account: frame_system::AccountInfo<<T as Config>::Nonce, <T as Config>::AccountData> =
165 frame_system::Account::<T>::get(who);
166 if nonce != account.nonce {
169 return Err(if nonce < account.nonce {
170 InvalidTransaction::Stale
171 } else {
172 InvalidTransaction::Future
173 }
174 .into());
175 }
176
177 let existing_account =
182 account.providers > 0 || account.consumers > 0 || account.sufficients > 0;
183
184 account.nonce += T::Nonce::one();
186
187 if pays_fee == Pays::Yes || existing_account {
190 frame_system::Account::<T>::insert(who, account);
191 }
192 Ok(())
193}
194
195pub fn validate_nonce<T: Config>(
197 who: &T::AccountId,
198 nonce: T::Nonce,
199) -> Result<(ValidTransaction, T::Nonce), TransactionValidityError> {
200 let account = frame_system::Account::<T>::get(who);
201
202 if nonce < account.nonce {
203 return Err(InvalidTransaction::Stale.into());
204 }
205 let provides = vec![Encode::encode(&(who.clone(), nonce))];
206 let requires = if account.nonce < nonce {
207 vec![Encode::encode(&(who.clone(), nonce - One::one()))]
208 } else {
209 vec![]
210 };
211 Ok((
212 ValidTransaction {
213 priority: 0,
214 requires,
215 provides,
216 longevity: TransactionLongevity::MAX,
217 propagate: true,
218 },
219 account.nonce,
220 ))
221}