From 4d6e973bc27a85bf3a5e40a84afcdb1d81aab69b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Tue, 9 Apr 2024 13:46:16 +0200 Subject: [PATCH] `CheckNonce`: Provide a way to pass an optional default nonce --- .../system/src/extensions/check_nonce.rs | 80 ++++++++++++------- substrate/test-utils/runtime/src/extrinsic.rs | 2 +- 2 files changed, 52 insertions(+), 30 deletions(-) diff --git a/substrate/frame/system/src/extensions/check_nonce.rs b/substrate/frame/system/src/extensions/check_nonce.rs index 7504a814aef13..edb524e929700 100644 --- a/substrate/frame/system/src/extensions/check_nonce.rs +++ b/substrate/frame/system/src/extensions/check_nonce.rs @@ -15,10 +15,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::Config; +use crate::{AccountInfo, Config}; use codec::{Decode, Encode}; use frame_support::dispatch::DispatchInfo; use scale_info::TypeInfo; +use sp_core::{Get, GetDefault}; use sp_runtime::{ traits::{DispatchInfoOf, Dispatchable, One, SignedExtension, Zero}, transaction_validity::{ @@ -35,21 +36,39 @@ use sp_std::vec; /// This extension affects `requires` and `provides` tags of validity, but DOES NOT /// set the `priority` field. Make sure that AT LEAST one of the signed extension sets /// some kind of priority upon validating transactions. -#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] -#[scale_info(skip_type_params(T))] -pub struct CheckNonce(#[codec(compact)] pub T::Nonce); +#[derive(Encode, Decode, TypeInfo)] +#[scale_info(skip_type_params(T, DefaultNonce))] +pub struct CheckNonce { + #[codec(compact)] + pub nonce: T::Nonce, + _phantom: core::marker::PhantomData, +} + +impl Clone for CheckNonce { + fn clone(&self) -> Self { + Self { nonce: self.nonce, _phantom: Default::default() } + } +} + +impl Eq for CheckNonce {} + +impl PartialEq for CheckNonce { + fn eq(&self, other: &Self) -> bool { + self.nonce == other.nonce + } +} -impl CheckNonce { +impl CheckNonce { /// utility constructor. Used only in client/factory code. pub fn from(nonce: T::Nonce) -> Self { - Self(nonce) + Self { nonce, _phantom: Default::default() } } } -impl sp_std::fmt::Debug for CheckNonce { +impl sp_std::fmt::Debug for CheckNonce { #[cfg(feature = "std")] fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - write!(f, "CheckNonce({})", self.0) + write!(f, "CheckNonce({})", self.nonce) } #[cfg(not(feature = "std"))] @@ -58,7 +77,8 @@ impl sp_std::fmt::Debug for CheckNonce { } } -impl SignedExtension for CheckNonce +impl + Send + Sync + 'static> SignedExtension + for CheckNonce where T::RuntimeCall: Dispatchable, { @@ -79,13 +99,14 @@ where _info: &DispatchInfoOf, _len: usize, ) -> Result<(), TransactionValidityError> { - let mut account = crate::Account::::get(who); + let mut account = crate::Account::::try_get(who) + .unwrap_or_else(|_| AccountInfo { nonce: DefaultNonce::get(), ..Default::default() }); if account.providers.is_zero() && account.sufficients.is_zero() { // Nonce storage not paid for return Err(InvalidTransaction::Payment.into()) } - if self.0 != account.nonce { - return Err(if self.0 < account.nonce { + if self.nonce != account.nonce { + return Err(if self.nonce < account.nonce { InvalidTransaction::Stale } else { InvalidTransaction::Future @@ -104,18 +125,19 @@ where _info: &DispatchInfoOf, _len: usize, ) -> TransactionValidity { - let account = crate::Account::::get(who); + let account = crate::Account::::try_get(who) + .unwrap_or_else(|_| AccountInfo { nonce: DefaultNonce::get(), ..Default::default() }); if account.providers.is_zero() && account.sufficients.is_zero() { // Nonce storage not paid for return InvalidTransaction::Payment.into() } - if self.0 < account.nonce { + if self.nonce < account.nonce { return InvalidTransaction::Stale.into() } - let provides = vec![Encode::encode(&(who, self.0))]; - let requires = if account.nonce < self.0 { - vec![Encode::encode(&(who, self.0 - One::one()))] + let provides = vec![Encode::encode(&(who, self.nonce))]; + let requires = if account.nonce < self.nonce { + vec![Encode::encode(&(who, self.nonce - One::one()))] } else { vec![] }; @@ -153,20 +175,20 @@ mod tests { let len = 0_usize; // stale assert_noop!( - CheckNonce::(0).validate(&1, CALL, &info, len), + CheckNonce::::from(0).validate(&1, CALL, &info, len), InvalidTransaction::Stale ); assert_noop!( - CheckNonce::(0).pre_dispatch(&1, CALL, &info, len), + CheckNonce::::from(0).pre_dispatch(&1, CALL, &info, len), InvalidTransaction::Stale ); // correct - assert_ok!(CheckNonce::(1).validate(&1, CALL, &info, len)); - assert_ok!(CheckNonce::(1).pre_dispatch(&1, CALL, &info, len)); + assert_ok!(CheckNonce::::from(1).validate(&1, CALL, &info, len)); + assert_ok!(CheckNonce::::from(1).pre_dispatch(&1, CALL, &info, len)); // future - assert_ok!(CheckNonce::(5).validate(&1, CALL, &info, len)); + assert_ok!(CheckNonce::::from(5).validate(&1, CALL, &info, len)); assert_noop!( - CheckNonce::(5).pre_dispatch(&1, CALL, &info, len), + CheckNonce::::from(5).pre_dispatch(&1, CALL, &info, len), InvalidTransaction::Future ); }) @@ -199,19 +221,19 @@ mod tests { let len = 0_usize; // Both providers and sufficients zero assert_noop!( - CheckNonce::(1).validate(&1, CALL, &info, len), + CheckNonce::::from(1).validate(&1, CALL, &info, len), InvalidTransaction::Payment ); assert_noop!( - CheckNonce::(1).pre_dispatch(&1, CALL, &info, len), + CheckNonce::::from(1).pre_dispatch(&1, CALL, &info, len), InvalidTransaction::Payment ); // Non-zero providers - assert_ok!(CheckNonce::(1).validate(&2, CALL, &info, len)); - assert_ok!(CheckNonce::(1).pre_dispatch(&2, CALL, &info, len)); + assert_ok!(CheckNonce::::from(1).validate(&2, CALL, &info, len)); + assert_ok!(CheckNonce::::from(1).pre_dispatch(&2, CALL, &info, len)); // Non-zero sufficients - assert_ok!(CheckNonce::(1).validate(&3, CALL, &info, len)); - assert_ok!(CheckNonce::(1).pre_dispatch(&3, CALL, &info, len)); + assert_ok!(CheckNonce::::from(1).validate(&3, CALL, &info, len)); + assert_ok!(CheckNonce::::from(1).pre_dispatch(&3, CALL, &info, len)); }) } } diff --git a/substrate/test-utils/runtime/src/extrinsic.rs b/substrate/test-utils/runtime/src/extrinsic.rs index e355e5d099ad5..67d6639a002c7 100644 --- a/substrate/test-utils/runtime/src/extrinsic.rs +++ b/substrate/test-utils/runtime/src/extrinsic.rs @@ -65,7 +65,7 @@ impl TryFrom<&Extrinsic> for TransferData { match uxt { Extrinsic { function: RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value }), - signature: Some((from, _, (CheckNonce(nonce), ..))), + signature: Some((from, _, (CheckNonce { nonce, .. }, ..))), } => Ok(TransferData { from: *from, to: *dest, amount: *value, nonce: *nonce }), Extrinsic { function: RuntimeCall::SubstrateTest(PalletCall::bench_call { transfer }),