1use crate::Config;
19use alloc::vec;
20use codec::{Decode, Encode};
21use frame_support::dispatch::DispatchInfo;
22use scale_info::TypeInfo;
23use sp_runtime::{
24 traits::{DispatchInfoOf, Dispatchable, One, SignedExtension, Zero},
25 transaction_validity::{
26 InvalidTransaction, TransactionLongevity, TransactionValidity, TransactionValidityError,
27 ValidTransaction,
28 },
29};
30
31#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
39#[scale_info(skip_type_params(T))]
40pub struct CheckNonce<T: Config>(#[codec(compact)] pub T::Nonce);
41
42impl<T: Config> CheckNonce<T> {
43 pub fn from(nonce: T::Nonce) -> Self {
45 Self(nonce)
46 }
47}
48
49impl<T: Config> core::fmt::Debug for CheckNonce<T> {
50 #[cfg(feature = "std")]
51 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
52 write!(f, "CheckNonce({})", self.0)
53 }
54
55 #[cfg(not(feature = "std"))]
56 fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
57 Ok(())
58 }
59}
60
61impl<T: Config> SignedExtension for CheckNonce<T>
62where
63 T::RuntimeCall: Dispatchable<Info = DispatchInfo>,
64{
65 type AccountId = T::AccountId;
66 type Call = T::RuntimeCall;
67 type AdditionalSigned = ();
68 type Pre = ();
69 const IDENTIFIER: &'static str = "CheckNonce";
70
71 fn additional_signed(&self) -> core::result::Result<(), TransactionValidityError> {
72 Ok(())
73 }
74
75 fn pre_dispatch(
76 self,
77 who: &Self::AccountId,
78 _call: &Self::Call,
79 _info: &DispatchInfoOf<Self::Call>,
80 _len: usize,
81 ) -> Result<(), TransactionValidityError> {
82 let mut account = crate::Account::<T>::get(who);
83 if account.providers.is_zero() && account.sufficients.is_zero() {
84 return Err(InvalidTransaction::Payment.into())
86 }
87 if self.0 != account.nonce {
88 return Err(if self.0 < account.nonce {
89 InvalidTransaction::Stale
90 } else {
91 InvalidTransaction::Future
92 }
93 .into())
94 }
95 account.nonce += T::Nonce::one();
96 crate::Account::<T>::insert(who, account);
97 Ok(())
98 }
99
100 fn validate(
101 &self,
102 who: &Self::AccountId,
103 _call: &Self::Call,
104 _info: &DispatchInfoOf<Self::Call>,
105 _len: usize,
106 ) -> TransactionValidity {
107 let account = crate::Account::<T>::get(who);
108 if account.providers.is_zero() && account.sufficients.is_zero() {
109 return InvalidTransaction::Payment.into()
111 }
112 if self.0 < account.nonce {
113 return InvalidTransaction::Stale.into()
114 }
115
116 let provides = vec![Encode::encode(&(who, self.0))];
117 let requires = if account.nonce < self.0 {
118 vec![Encode::encode(&(who, self.0 - One::one()))]
119 } else {
120 vec![]
121 };
122
123 Ok(ValidTransaction {
124 priority: 0,
125 requires,
126 provides,
127 longevity: TransactionLongevity::max_value(),
128 propagate: true,
129 })
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136 use crate::mock::{new_test_ext, Test, CALL};
137 use frame_support::{assert_noop, assert_ok};
138
139 #[test]
140 fn signed_ext_check_nonce_works() {
141 new_test_ext().execute_with(|| {
142 crate::Account::<Test>::insert(
143 1,
144 crate::AccountInfo {
145 nonce: 1u64.into(),
146 consumers: 0,
147 providers: 1,
148 sufficients: 0,
149 data: 0,
150 },
151 );
152 let info = DispatchInfo::default();
153 let len = 0_usize;
154 assert_noop!(
156 CheckNonce::<Test>(0u64.into()).validate(&1, CALL, &info, len),
157 InvalidTransaction::Stale
158 );
159 assert_noop!(
160 CheckNonce::<Test>(0u64.into()).pre_dispatch(&1, CALL, &info, len),
161 InvalidTransaction::Stale
162 );
163 assert_ok!(CheckNonce::<Test>(1u64.into()).validate(&1, CALL, &info, len));
165 assert_ok!(CheckNonce::<Test>(1u64.into()).pre_dispatch(&1, CALL, &info, len));
166 assert_ok!(CheckNonce::<Test>(5u64.into()).validate(&1, CALL, &info, len));
168 assert_noop!(
169 CheckNonce::<Test>(5u64.into()).pre_dispatch(&1, CALL, &info, len),
170 InvalidTransaction::Future
171 );
172 })
173 }
174
175 #[test]
176 fn signed_ext_check_nonce_requires_provider() {
177 new_test_ext().execute_with(|| {
178 crate::Account::<Test>::insert(
179 2,
180 crate::AccountInfo {
181 nonce: 1u64.into(),
182 consumers: 0,
183 providers: 1,
184 sufficients: 0,
185 data: 0,
186 },
187 );
188 crate::Account::<Test>::insert(
189 3,
190 crate::AccountInfo {
191 nonce: 1u64.into(),
192 consumers: 0,
193 providers: 0,
194 sufficients: 1,
195 data: 0,
196 },
197 );
198 let info = DispatchInfo::default();
199 let len = 0_usize;
200 assert_noop!(
202 CheckNonce::<Test>(1u64.into()).validate(&1, CALL, &info, len),
203 InvalidTransaction::Payment
204 );
205 assert_noop!(
206 CheckNonce::<Test>(1u64.into()).pre_dispatch(&1, CALL, &info, len),
207 InvalidTransaction::Payment
208 );
209 assert_ok!(CheckNonce::<Test>(1u64.into()).validate(&2, CALL, &info, len));
211 assert_ok!(CheckNonce::<Test>(1u64.into()).pre_dispatch(&2, CALL, &info, len));
212 assert_ok!(CheckNonce::<Test>(1u64.into()).validate(&3, CALL, &info, len));
214 assert_ok!(CheckNonce::<Test>(1u64.into()).pre_dispatch(&3, CALL, &info, len));
215 })
216 }
217}