frame_system/extensions/
check_nonce.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18use 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/// Nonce check and increment to give replay protection for transactions.
32///
33/// # Transaction Validity
34///
35/// This extension affects `requires` and `provides` tags of validity, but DOES NOT
36/// set the `priority` field. Make sure that AT LEAST one of the signed extension sets
37/// some kind of priority upon validating transactions.
38#[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	/// utility constructor. Used only in client/factory code.
44	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			// Nonce storage not paid for
85			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			// Nonce storage not paid for
110			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			// stale
155			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			// correct
164			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			// future
167			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			// Both providers and sufficients zero
201			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			// Non-zero providers
210			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			// Non-zero sufficients
213			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}