diff --git a/node/src/benchmarking.rs b/node/src/benchmarking.rs index 218670609..ba176e15f 100644 --- a/node/src/benchmarking.rs +++ b/node/src/benchmarking.rs @@ -5,6 +5,7 @@ use crate::service::FullClient; use node_subtensor_runtime as runtime; +use node_subtensor_runtime::check_nonce; use node_subtensor_runtime::pallet_subtensor; use runtime::{AccountId, Balance, BalancesCall, SystemCall}; use sc_cli::Result; @@ -130,7 +131,7 @@ pub fn create_benchmark_extrinsic( period, best_block.saturated_into(), )), - frame_system::CheckNonce::::from(nonce), + check_nonce::CheckNonce::::from(nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(0), pallet_subtensor::SubtensorSignedExtension::::new(), diff --git a/runtime/src/check_nonce.rs b/runtime/src/check_nonce.rs new file mode 100644 index 000000000..e6e992ccf --- /dev/null +++ b/runtime/src/check_nonce.rs @@ -0,0 +1,127 @@ +use codec::{Decode, Encode}; +use frame_support::dispatch::{DispatchInfo, Pays}; +use frame_system::Config; +use scale_info::TypeInfo; +use sp_runtime::{ + traits::{DispatchInfoOf, Dispatchable, One, SignedExtension, Zero}, + transaction_validity::{ + InvalidTransaction, TransactionLongevity, TransactionValidity, TransactionValidityError, + ValidTransaction, + }, +}; +use sp_std::vec; + +/// Nonce check and increment to give replay protection for transactions. +/// +/// # Transaction Validity +/// +/// 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); + +impl CheckNonce { + /// utility constructor. Used only in client/factory code. + pub fn from(nonce: T::Nonce) -> Self { + Self(nonce) + } +} + +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) + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + Ok(()) + } +} + +impl SignedExtension for CheckNonce +where + T::RuntimeCall: Dispatchable, +{ + type AccountId = T::AccountId; + type Call = T::RuntimeCall; + type AdditionalSigned = (); + type Pre = (); + const IDENTIFIER: &'static str = "CheckNonce"; + + fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { + Ok(()) + } + + fn pre_dispatch( + self, + who: &Self::AccountId, + _call: &Self::Call, + info: &DispatchInfoOf, + _len: usize, + ) -> Result<(), TransactionValidityError> { + let mut account = frame_system::Account::::get(who); + match info.pays_fee { + Pays::Yes => { + if account.providers.is_zero() && account.sufficients.is_zero() { + // Nonce storage not paid for + return Err(InvalidTransaction::Payment.into()); + } + } + // not check providers and sufficients for Pays::No extrinsic + Pays::No => {} + } + + if self.0 != account.nonce { + return Err(if self.0 < account.nonce { + InvalidTransaction::Stale + } else { + InvalidTransaction::Future + } + .into()); + } + account.nonce += T::Nonce::one(); + frame_system::Account::::insert(who, account); + Ok(()) + } + + fn validate( + &self, + who: &Self::AccountId, + _call: &Self::Call, + info: &DispatchInfoOf, + _len: usize, + ) -> TransactionValidity { + let account = frame_system::Account::::get(who); + match info.pays_fee { + Pays::Yes => { + if account.providers.is_zero() && account.sufficients.is_zero() { + // Nonce storage not paid for + return Err(InvalidTransaction::Payment.into()); + } + } + // not check providers and sufficients for Pays::No extrinsic + Pays::No => {} + } + if self.0 < 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()))] + } else { + vec![] + }; + + Ok(ValidTransaction { + priority: 0, + requires, + provides, + longevity: TransactionLongevity::max_value(), + propagate: true, + }) + } +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 70bfddbbb..46d16a010 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -6,6 +6,7 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +pub mod check_nonce; mod migrations; use codec::{Decode, Encode, MaxEncodedLen}; @@ -1177,7 +1178,7 @@ pub type SignedExtra = ( frame_system::CheckTxVersion, frame_system::CheckGenesis, frame_system::CheckEra, - frame_system::CheckNonce, + check_nonce::CheckNonce, frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, pallet_subtensor::SubtensorSignedExtension,