From 3b8b4ba3086f5a788c5b0befba0c022b2c0672d9 Mon Sep 17 00:00:00 2001 From: Guillaume Thiolliere Date: Wed, 13 Nov 2024 18:20:25 +0900 Subject: [PATCH] BACKPORT-CONFLICT --- .../extensions/check_obsolete_extension.rs | 54 ++ .../extensions/refund_relayer_extension.rs | 65 ++ polkadot/runtime/common/src/claims.rs | 34 +- prdoc/pr_6323.prdoc | 32 + .../src/extensions.rs | 133 ++++ substrate/frame/examples/basic/src/lib.rs | 8 + substrate/frame/examples/basic/src/tests.rs | 15 + substrate/frame/sudo/src/extension.rs | 17 +- .../system/src/extensions/check_mortality.rs | 43 ++ .../src/extensions/check_non_zero_sender.rs | 51 ++ .../system/src/extensions/check_nonce.rs | 204 ++++++ .../system/src/extensions/check_weight.rs | 10 + .../asset-conversion-tx-payment/src/lib.rs | 18 + .../asset-tx-payment/src/lib.rs | 14 + .../skip-feeless-payment/src/lib.rs | 21 + .../skip-feeless-payment/src/mock.rs | 7 + .../skip-feeless-payment/src/tests.rs | 24 + .../frame/transaction-payment/src/lib.rs | 15 + .../frame/transaction-payment/src/tests.rs | 75 ++ .../verify-signature/src/benchmarking.rs | 78 +++ .../frame/verify-signature/src/extension.rs | 158 +++++ substrate/frame/verify-signature/src/tests.rs | 132 ++++ .../runtime/src/generic/checked_extrinsic.rs | 17 + .../as_transaction_extension.rs | 131 ++++ .../dispatch_transaction.rs | 160 +++++ .../src/traits/transaction_extension/mod.rs | 641 ++++++++++++++++++ substrate/test-utils/runtime/src/lib.rs | 25 + 27 files changed, 2180 insertions(+), 2 deletions(-) create mode 100644 prdoc/pr_6323.prdoc create mode 100644 substrate/frame/examples/authorization-tx-extension/src/extensions.rs create mode 100644 substrate/frame/verify-signature/src/benchmarking.rs create mode 100644 substrate/frame/verify-signature/src/extension.rs create mode 100644 substrate/frame/verify-signature/src/tests.rs create mode 100644 substrate/primitives/runtime/src/traits/transaction_extension/as_transaction_extension.rs create mode 100644 substrate/primitives/runtime/src/traits/transaction_extension/dispatch_transaction.rs create mode 100644 substrate/primitives/runtime/src/traits/transaction_extension/mod.rs diff --git a/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs b/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs index df75092af6e8..a51508b44fd4 100644 --- a/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs +++ b/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs @@ -282,9 +282,31 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { )* ), ); +<<<<<<< HEAD:bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs fn additional_signed(&self) -> sp_std::result::Result< (), sp_runtime::transaction_validity::TransactionValidityError, +======= + fn weight(&self, _: &$call) -> frame_support::pallet_prelude::Weight { + frame_support::pallet_prelude::Weight::zero() + } + + fn validate( + &self, + origin: <$call as sp_runtime::traits::Dispatchable>::RuntimeOrigin, + call: &$call, + _info: &sp_runtime::traits::DispatchInfoOf<$call>, + _len: usize, + _self_implicit: Self::Implicit, + _inherited_implication: &impl codec::Encode, + _source: sp_runtime::transaction_validity::TransactionSource, + ) -> Result< + ( + sp_runtime::transaction_validity::ValidTransaction, + Self::Val, + <$call as sp_runtime::traits::Dispatchable>::RuntimeOrigin, + ), sp_runtime::transaction_validity::TransactionValidityError +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)):bridges/bin/runtime-common/src/extensions.rs > { Ok(()) } @@ -371,6 +393,7 @@ mod tests { }, RefundableParachain, }, +<<<<<<< HEAD:bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs mock::*, }; use bp_polkadot_core::parachains::ParaId; @@ -379,6 +402,11 @@ mod tests { use sp_runtime::{ traits::{ConstU64, SignedExtension}, transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, +======= + transaction_validity::{ + InvalidTransaction, TransactionSource::External, TransactionValidity, ValidTransaction, + }, +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)):bridges/bin/runtime-common/src/extensions.rs DispatchError, }; @@ -466,7 +494,17 @@ mod tests { run_test(|| { assert_err!( +<<<<<<< HEAD:bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs BridgeRejectObsoleteHeadersAndMessages.validate(&42, &MockCall { data: 1 }, &(), 0), +======= + BridgeRejectObsoleteHeadersAndMessages.validate_only( + 42u64.into(), + &MockCall { data: 1 }, + &(), + 0, + External, + ), +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)):bridges/bin/runtime-common/src/extensions.rs InvalidTransaction::Custom(1) ); assert_err!( @@ -480,7 +518,17 @@ mod tests { ); assert_err!( +<<<<<<< HEAD:bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs BridgeRejectObsoleteHeadersAndMessages.validate(&42, &MockCall { data: 2 }, &(), 0), +======= + BridgeRejectObsoleteHeadersAndMessages.validate_only( + 42u64.into(), + &MockCall { data: 2 }, + &(), + 0, + External, + ), +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)):bridges/bin/runtime-common/src/extensions.rs InvalidTransaction::Custom(2) ); assert_err!( @@ -495,8 +543,14 @@ mod tests { assert_eq!( BridgeRejectObsoleteHeadersAndMessages +<<<<<<< HEAD:bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs .validate(&42, &MockCall { data: 3 }, &(), 0) .unwrap(), +======= + .validate_only(42u64.into(), &MockCall { data: 3 }, &(), 0, External) + .unwrap() + .0, +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)):bridges/bin/runtime-common/src/extensions.rs ValidTransaction { priority: 3, ..Default::default() }, ); assert_eq!( diff --git a/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs b/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs index 6ba3506377d0..ba01191966d5 100644 --- a/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs +++ b/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs @@ -27,8 +27,13 @@ use bp_relayers::{ExplicitOrAccountParams, RewardsAccountOwner, RewardsAccountPa use bp_runtime::{Chain, Parachain, RangeInclusiveExt, StaticStrProvider}; use codec::{Codec, Decode, Encode}; use frame_support::{ +<<<<<<< HEAD:bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs dispatch::{CallableCallFor, DispatchInfo, PostDispatchInfo}, traits::IsSubType, +======= + dispatch::{DispatchInfo, PostDispatchInfo}, + pallet_prelude::TransactionSource, +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)):bridges/modules/relayers/src/extension/mod.rs weights::Weight, CloneNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, }; @@ -450,6 +455,7 @@ where call: &Self::Call, _info: &DispatchInfoOf, _len: usize, +<<<<<<< HEAD:bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs ) -> TransactionValidity { // this is the only relevant line of code for the `pre_dispatch` // @@ -457,6 +463,19 @@ where // reasons, so if you're adding some code that may fail here, please check if it needs // to be added to the `pre_dispatch` as well let parsed_call = T::parse_and_check_for_obsolete_call(call)?; +======= + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + _source: TransactionSource, + ) -> ValidateResult { + // Prepare relevant data for `prepare` + let parsed_call = match C::parse_and_check_for_obsolete_call(call)? { + Some(parsed_call) => parsed_call, + None => return Ok((Default::default(), None, origin)), + }; + // Those calls are only for signed transactions. + let relayer = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?; +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)):bridges/modules/relayers/src/extension/mod.rs // the following code just plays with transaction priority and never returns an error @@ -962,8 +981,15 @@ pub(crate) mod tests { Call as ParachainsCall, Pallet as ParachainsPallet, RelayBlockHash, }; use sp_runtime::{ +<<<<<<< HEAD:bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs traits::{ConstU64, Header as HeaderT}, transaction_validity::{InvalidTransaction, ValidTransaction}, +======= + traits::{ConstU64, DispatchTransaction, Header as HeaderT}, + transaction_validity::{ + InvalidTransaction, TransactionSource::External, TransactionValidity, ValidTransaction, + }, +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)):bridges/modules/relayers/src/extension/mod.rs DispatchError, }; @@ -1545,6 +1571,7 @@ pub(crate) mod tests { } fn run_validate(call: RuntimeCall) -> TransactionValidity { +<<<<<<< HEAD:bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs let extension: TestExtension = RefundSignedExtensionAdapter(RefundBridgedParachainMessages(PhantomData)); extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) @@ -1560,6 +1587,44 @@ pub(crate) mod tests { let extension: TestMessagesExtension = RefundSignedExtensionAdapter(RefundBridgedMessages(PhantomData)); extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) +======= + let extension: TestExtension = BridgeRelayersTransactionExtension(PhantomData); + extension + .validate_only( + Some(relayer_account_at_this_chain()).into(), + &call, + &DispatchInfo::default(), + 0, + External, + ) + .map(|t| t.0) + } + + fn run_grandpa_validate(call: RuntimeCall) -> TransactionValidity { + let extension: TestGrandpaExtension = BridgeRelayersTransactionExtension(PhantomData); + extension + .validate_only( + Some(relayer_account_at_this_chain()).into(), + &call, + &DispatchInfo::default(), + 0, + External, + ) + .map(|t| t.0) + } + + fn run_messages_validate(call: RuntimeCall) -> TransactionValidity { + let extension: TestMessagesExtension = BridgeRelayersTransactionExtension(PhantomData); + extension + .validate_only( + Some(relayer_account_at_this_chain()).into(), + &call, + &DispatchInfo::default(), + 0, + External, + ) + .map(|t| t.0) +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)):bridges/modules/relayers/src/extension/mod.rs } fn ignore_priority(tx: TransactionValidity) -> TransactionValidity { diff --git a/polkadot/runtime/common/src/claims.rs b/polkadot/runtime/common/src/claims.rs index 162bf01c3843..a6fe3c1e1c5f 100644 --- a/polkadot/runtime/common/src/claims.rs +++ b/polkadot/runtime/common/src/claims.rs @@ -35,7 +35,8 @@ use sp_io::{crypto::secp256k1_ecdsa_recover, hashing::keccak_256}; use sp_runtime::{ traits::{CheckedSub, DispatchInfoOf, SignedExtension, Zero}, transaction_validity::{ - InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction, + InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, + ValidTransaction, }, RuntimeDebug, }; @@ -650,6 +651,7 @@ where call: &Self::Call, _info: &DispatchInfoOf, _len: usize, +<<<<<<< HEAD ) -> TransactionValidity { if let Some(local_call) = call.is_sub_type() { if let Call::attest { statement: attested_statement } = local_call { @@ -659,6 +661,22 @@ where let e = InvalidTransaction::Custom(ValidityError::InvalidStatement.into()); ensure!(&attested_statement[..] == s.to_text(), e); } +======= + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + _source: TransactionSource, + ) -> Result< + (ValidTransaction, Self::Val, ::RuntimeOrigin), + TransactionValidityError, + > { + if let Some(Call::attest { statement: attested_statement }) = call.is_sub_type() { + let who = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?; + let signer = Preclaims::::get(who) + .ok_or(InvalidTransaction::Custom(ValidityError::SignerHasNoClaim.into()))?; + if let Some(s) = Signing::::get(signer) { + let e = InvalidTransaction::Custom(ValidityError::InvalidStatement.into()); + ensure!(&attested_statement[..] == s.to_text(), e); +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) } } Ok(ValidTransaction::default()) @@ -699,6 +717,7 @@ mod tests { use super::*; use hex_literal::hex; use secp_utils::*; + use sp_runtime::transaction_validity::TransactionSource::External; use codec::Encode; // The testing primitives are very useful for avoiding having to work with signatures @@ -1055,8 +1074,13 @@ mod tests { }); let di = c.get_dispatch_info(); assert_eq!(di.pays_fee, Pays::No); +<<<<<<< HEAD let r = p.validate(&42, &c, &di, 20); assert_eq!(r, TransactionValidity::Ok(ValidTransaction::default())); +======= + let r = p.validate_only(Some(42).into(), &c, &di, 20, External); + assert_eq!(r.unwrap().0, ValidTransaction::default()); +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) }); } @@ -1068,13 +1092,21 @@ mod tests { statement: StatementKind::Regular.to_text().to_vec(), }); let di = c.get_dispatch_info(); +<<<<<<< HEAD let r = p.validate(&42, &c, &di, 20); +======= + let r = p.validate_only(Some(42).into(), &c, &di, 20, External); +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) assert!(r.is_err()); let c = RuntimeCall::Claims(ClaimsCall::attest { statement: StatementKind::Saft.to_text().to_vec(), }); let di = c.get_dispatch_info(); +<<<<<<< HEAD let r = p.validate(&69, &c, &di, 20); +======= + let r = p.validate_only(Some(69).into(), &c, &di, 20, External); +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) assert!(r.is_err()); }); } diff --git a/prdoc/pr_6323.prdoc b/prdoc/pr_6323.prdoc new file mode 100644 index 000000000000..ec632a14f946 --- /dev/null +++ b/prdoc/pr_6323.prdoc @@ -0,0 +1,32 @@ +title: add `TransactionSource` to `TransactionExtension::validate` +doc: +- audience: Runtime Dev + description: | + Add a the source of the extrinsic as an argument in `TransactionExtension::validate`. + The transaction source can be useful for transactions that should only be valid if it comes from the node. For example from offchain worker. + To update the current code. The transaction source can simply be ignored: `_source: TransactionSource` + + +crates: +- name: sp-runtime + bump: major +- name: bridge-runtime-common + bump: patch +- name: frame-system + bump: patch +- name: pallet-transaction-payment + bump: patch +- name: polkadot-runtime-common + bump: patch +- name: pallet-sudo + bump: patch +- name: pallet-verify-signature + bump: patch +- name: pallet-asset-tx-payment + bump: patch +- name: pallet-bridge-relayers + bump: patch +- name: pallet-asset-conversion-tx-payment + bump: patch +- name: pallet-skip-feeless-payment + bump: patch diff --git a/substrate/frame/examples/authorization-tx-extension/src/extensions.rs b/substrate/frame/examples/authorization-tx-extension/src/extensions.rs new file mode 100644 index 000000000000..dcbe171c183a --- /dev/null +++ b/substrate/frame/examples/authorization-tx-extension/src/extensions.rs @@ -0,0 +1,133 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use core::{fmt, marker::PhantomData}; + +use codec::{Decode, Encode}; +use frame_support::{pallet_prelude::TransactionSource, traits::OriginTrait, Parameter}; +use scale_info::TypeInfo; +use sp_runtime::{ + impl_tx_ext_default, + traits::{ + DispatchInfoOf, DispatchOriginOf, IdentifyAccount, TransactionExtension, ValidateResult, + Verify, + }, + transaction_validity::{InvalidTransaction, ValidTransaction}, +}; + +use crate::pallet_coownership::{Config, Origin}; + +/// Helper struct to organize the data needed for signature verification of both parties involved. +#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo)] +pub struct AuthCredentials { + first: (Signer, Signature), + second: (Signer, Signature), +} + +/// Extension that, if activated by providing a pair of signers and signatures, will authorize a +/// coowner origin of the two signers. Both signers have to construct their signatures on all of the +/// data that follows this extension in the `TransactionExtension` pipeline, their implications and +/// the call. Essentially re-sign the transaction from this point onwards in the pipeline by using +/// the `inherited_implication`, as shown below. +#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct AuthorizeCoownership { + inner: Option>, + _phantom: PhantomData, +} + +impl Default for AuthorizeCoownership { + fn default() -> Self { + Self { inner: None, _phantom: Default::default() } + } +} + +impl AuthorizeCoownership { + /// Creates an active extension that will try to authorize the coownership origin. + pub fn new(first: (Signer, Signature), second: (Signer, Signature)) -> Self { + Self { inner: Some(AuthCredentials { first, second }), _phantom: Default::default() } + } +} + +impl fmt::Debug for AuthorizeCoownership { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "AuthorizeCoownership") + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result { + Ok(()) + } +} + +impl TransactionExtension + for AuthorizeCoownership +where + Signer: IdentifyAccount + Parameter + Send + Sync + 'static, + Signature: Verify + Parameter + Send + Sync + 'static, +{ + const IDENTIFIER: &'static str = "AuthorizeCoownership"; + type Implicit = (); + type Val = (); + type Pre = (); + + fn validate( + &self, + mut origin: DispatchOriginOf, + _call: &T::RuntimeCall, + _info: &DispatchInfoOf, + _len: usize, + _self_implicit: Self::Implicit, + inherited_implication: &impl codec::Encode, + _source: TransactionSource, + ) -> ValidateResult { + // If the extension is inactive, just move on in the pipeline. + let Some(auth) = &self.inner else { + return Ok((ValidTransaction::default(), (), origin)); + }; + let first_account = auth.first.0.clone().into_account(); + let second_account = auth.second.0.clone().into_account(); + + // Construct the payload to sign using the `inherited_implication`. + let msg = inherited_implication.using_encoded(sp_io::hashing::blake2_256); + + // Both parties' signatures must be correct for the origin to be authorized. + // In a prod environment, we're just return a `InvalidTransaction::BadProof` if the + // signature isn't valid, but we return these custom errors to be able to assert them in + // tests. + if !auth.first.1.verify(&msg[..], &first_account) { + Err(InvalidTransaction::Custom(100))? + } + if !auth.second.1.verify(&msg[..], &second_account) { + Err(InvalidTransaction::Custom(200))? + } + // Construct a `pallet_coownership::Origin`. + let local_origin = Origin::Coowners(first_account, second_account); + // Turn it into a local `PalletsOrigin`. + let local_origin = ::PalletsOrigin::from(local_origin); + // Then finally into a pallet `RuntimeOrigin`. + let local_origin = ::RuntimeOrigin::from(local_origin); + // Which the `set_caller_from` function will convert into the overarching `RuntimeOrigin` + // created by `construct_runtime!`. + origin.set_caller_from(local_origin); + // Make sure to return the new origin. + Ok((ValidTransaction::default(), (), origin)) + } + // We're not doing any special logic in `TransactionExtension::prepare`, so just impl a default. + impl_tx_ext_default!(T::RuntimeCall; weight prepare); +} diff --git a/substrate/frame/examples/basic/src/lib.rs b/substrate/frame/examples/basic/src/lib.rs index fea04cb447a0..a23816eb0a94 100644 --- a/substrate/frame/examples/basic/src/lib.rs +++ b/substrate/frame/examples/basic/src/lib.rs @@ -60,6 +60,7 @@ use codec::{Decode, Encode}; use core::marker::PhantomData; use frame_support::{ dispatch::{ClassifyDispatch, DispatchClass, DispatchResult, Pays, PaysFee, WeighData}, + pallet_prelude::TransactionSource, traits::IsSubType, weights::Weight, }; @@ -516,7 +517,14 @@ where call: &Self::Call, _info: &DispatchInfoOf, len: usize, +<<<<<<< HEAD ) -> TransactionValidity { +======= + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + _source: TransactionSource, + ) -> ValidateResult::RuntimeCall> { +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) // if the transaction is too big, just drop it. if len > 200 { return InvalidTransaction::ExhaustsResources.into() diff --git a/substrate/frame/examples/basic/src/tests.rs b/substrate/frame/examples/basic/src/tests.rs index d7095eb3c944..d1fb19e98403 100644 --- a/substrate/frame/examples/basic/src/tests.rs +++ b/substrate/frame/examples/basic/src/tests.rs @@ -27,7 +27,12 @@ use sp_core::H256; // The testing primitives are very useful for avoiding having to work with signatures // or public keys. `u64` is used as the `AccountId` and no `Signature`s are required. use sp_runtime::{ +<<<<<<< HEAD traits::{BlakeTwo256, IdentityLookup}, +======= + traits::{BlakeTwo256, DispatchTransaction, IdentityLookup}, + transaction_validity::TransactionSource::External, +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) BuildStorage, }; // Reexport crate as its pallet name for construct_runtime. @@ -146,13 +151,23 @@ fn signed_ext_watch_dummy_works() { assert_eq!( WatchDummy::(PhantomData) +<<<<<<< HEAD .validate(&1, &call, &info, 150) +======= + .validate_only(Some(1).into(), &call, &info, 150, External) +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) .unwrap() .priority, u64::MAX, ); assert_eq!( +<<<<<<< HEAD WatchDummy::(PhantomData).validate(&1, &call, &info, 250), +======= + WatchDummy::(PhantomData) + .validate_only(Some(1).into(), &call, &info, 250, External) + .unwrap_err(), +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) InvalidTransaction::ExhaustsResources.into(), ); }) diff --git a/substrate/frame/sudo/src/extension.rs b/substrate/frame/sudo/src/extension.rs index fb7eaf789480..e2c5bd7f5a01 100644 --- a/substrate/frame/sudo/src/extension.rs +++ b/substrate/frame/sudo/src/extension.rs @@ -18,7 +18,7 @@ use crate::{Config, Key}; use codec::{Decode, Encode}; use core::{fmt, marker::PhantomData}; -use frame_support::{dispatch::DispatchInfo, ensure}; +use frame_support::{dispatch::DispatchInfo, ensure, pallet_prelude::TransactionSource}; use scale_info::TypeInfo; use sp_runtime::{ traits::{DispatchInfoOf, Dispatchable, SignedExtension}, @@ -85,7 +85,22 @@ where _call: &Self::Call, info: &DispatchInfoOf, _len: usize, +<<<<<<< HEAD ) -> TransactionValidity { +======= + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + _source: TransactionSource, + ) -> Result< + ( + ValidTransaction, + Self::Val, + <::RuntimeCall as Dispatchable>::RuntimeOrigin, + ), + TransactionValidityError, + > { + let who = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?; +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) let sudo_key: T::AccountId = Key::::get().ok_or(UnknownTransaction::CannotLookup)?; ensure!(*who == sudo_key, InvalidTransaction::BadSigner); diff --git a/substrate/frame/system/src/extensions/check_mortality.rs b/substrate/frame/system/src/extensions/check_mortality.rs index 6666c4812fbc..1e84b1e95f05 100644 --- a/substrate/frame/system/src/extensions/check_mortality.rs +++ b/substrate/frame/system/src/extensions/check_mortality.rs @@ -17,6 +17,7 @@ use crate::{pallet_prelude::BlockNumberFor, BlockHash, Config, Pallet}; use codec::{Decode, Encode}; +use frame_support::pallet_prelude::TransactionSource; use scale_info::TypeInfo; use sp_runtime::{ generic::Era, @@ -98,6 +99,32 @@ impl SignedExtension for CheckMortality { ) -> Result { self.validate(who, call, info, len).map(|_| ()) } +<<<<<<< HEAD +======= + + fn validate( + &self, + origin: ::RuntimeOrigin, + _call: &T::RuntimeCall, + _info: &DispatchInfoOf, + _len: usize, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + _source: TransactionSource, + ) -> ValidateResult { + let current_u64 = >::block_number().saturated_into::(); + let valid_till = self.0.death(current_u64); + Ok(( + ValidTransaction { + longevity: valid_till.saturating_sub(current_u64), + ..Default::default() + }, + (), + origin, + )) + } + impl_tx_ext_default!(T::RuntimeCall; prepare); +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) } #[cfg(test)] @@ -109,6 +136,12 @@ mod tests { weights::Weight, }; use sp_core::H256; +<<<<<<< HEAD +======= + use sp_runtime::{ + traits::DispatchTransaction, transaction_validity::TransactionSource::External, + }; +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) #[test] fn signed_ext_check_era_should_work() { @@ -145,7 +178,17 @@ mod tests { System::set_block_number(17); >::insert(16, H256::repeat_byte(1)); +<<<<<<< HEAD assert_eq!(ext.validate(&1, CALL, &normal, len).unwrap().longevity, 15); +======= + assert_eq!( + ext.validate_only(Some(1).into(), CALL, &normal, len, External) + .unwrap() + .0 + .longevity, + 15 + ); +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) }) } } diff --git a/substrate/frame/system/src/extensions/check_non_zero_sender.rs b/substrate/frame/system/src/extensions/check_non_zero_sender.rs index 06dc2bf177ac..8d3be7b55a9a 100644 --- a/substrate/frame/system/src/extensions/check_non_zero_sender.rs +++ b/substrate/frame/system/src/extensions/check_non_zero_sender.rs @@ -18,7 +18,11 @@ use crate::Config; use codec::{Decode, Encode}; use core::marker::PhantomData; +<<<<<<< HEAD use frame_support::{dispatch::DispatchInfo, DefaultNoBound}; +======= +use frame_support::{pallet_prelude::TransactionSource, traits::OriginTrait, DefaultNoBound}; +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) use scale_info::TypeInfo; use sp_runtime::{ traits::{DispatchInfoOf, Dispatchable, SignedExtension}, @@ -81,9 +85,20 @@ where _call: &Self::Call, _info: &DispatchInfoOf, _len: usize, +<<<<<<< HEAD ) -> TransactionValidity { if who.using_encoded(|d| d.iter().all(|x| *x == 0)) { return Err(TransactionValidityError::Invalid(InvalidTransaction::BadSigner)) +======= + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + _source: TransactionSource, + ) -> sp_runtime::traits::ValidateResult { + if let Some(who) = origin.as_signer() { + if who.using_encoded(|d| d.iter().all(|x| *x == 0)) { + return Err(InvalidTransaction::BadSigner.into()) + } +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) } Ok(ValidTransaction::default()) } @@ -93,18 +108,54 @@ where mod tests { use super::*; use crate::mock::{new_test_ext, Test, CALL}; +<<<<<<< HEAD use frame_support::{assert_noop, assert_ok}; +======= + use frame_support::{assert_ok, dispatch::DispatchInfo}; + use sp_runtime::{ + traits::{AsTransactionAuthorizedOrigin, DispatchTransaction}, + transaction_validity::{TransactionSource::External, TransactionValidityError}, + }; +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) #[test] fn zero_account_ban_works() { new_test_ext().execute_with(|| { let info = DispatchInfo::default(); let len = 0_usize; +<<<<<<< HEAD assert_noop!( CheckNonZeroSender::::new().validate(&0, CALL, &info, len), InvalidTransaction::BadSigner ); assert_ok!(CheckNonZeroSender::::new().validate(&1, CALL, &info, len)); +======= + assert_eq!( + CheckNonZeroSender::::new() + .validate_only(Some(0).into(), CALL, &info, len, External) + .unwrap_err(), + TransactionValidityError::from(InvalidTransaction::BadSigner) + ); + assert_ok!(CheckNonZeroSender::::new().validate_only( + Some(1).into(), + CALL, + &info, + len, + External, + )); + }) + } + + #[test] + fn unsigned_origin_works() { + new_test_ext().execute_with(|| { + let info = DispatchInfo::default(); + let len = 0_usize; + let (_, _, origin) = CheckNonZeroSender::::new() + .validate(None.into(), CALL, &info, len, (), CALL, External) + .unwrap(); + assert!(!origin.is_transaction_authorized()); +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) }) } } diff --git a/substrate/frame/system/src/extensions/check_nonce.rs b/substrate/frame/system/src/extensions/check_nonce.rs index 3535870d1b59..83d03e55ed9c 100644 --- a/substrate/frame/system/src/extensions/check_nonce.rs +++ b/substrate/frame/system/src/extensions/check_nonce.rs @@ -18,7 +18,13 @@ use crate::Config; use alloc::vec; use codec::{Decode, Encode}; +<<<<<<< HEAD use frame_support::dispatch::DispatchInfo; +======= +use frame_support::{ + dispatch::DispatchInfo, pallet_prelude::TransactionSource, RuntimeDebugNoBound, +}; +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) use scale_info::TypeInfo; use sp_runtime::{ traits::{DispatchInfoOf, Dispatchable, One, SignedExtension, Zero}, @@ -78,8 +84,19 @@ where _call: &Self::Call, _info: &DispatchInfoOf, _len: usize, +<<<<<<< HEAD ) -> Result<(), TransactionValidityError> { let mut account = crate::Account::::get(who); +======= + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + _source: TransactionSource, + ) -> ValidateResult { + let Some(who) = origin.as_system_origin_signer() else { + return Ok((Default::default(), Val::Refund(self.weight(call)), origin)) + }; + let account = crate::Account::::get(who); +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) if account.providers.is_zero() && account.sufficients.is_zero() { // Nonce storage not paid for return Err(InvalidTransaction::Payment.into()) @@ -133,8 +150,19 @@ where #[cfg(test)] mod tests { use super::*; +<<<<<<< HEAD use crate::mock::{new_test_ext, Test, CALL}; use frame_support::{assert_noop, assert_ok}; +======= + use crate::mock::{new_test_ext, RuntimeCall, Test, CALL}; + use frame_support::{ + assert_ok, assert_storage_noop, dispatch::GetDispatchInfo, traits::OriginTrait, + }; + use sp_runtime::{ + traits::{AsTransactionAuthorizedOrigin, DispatchTransaction}, + transaction_validity::TransactionSource::External, + }; +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) #[test] fn signed_ext_check_nonce_works() { @@ -152,6 +180,7 @@ mod tests { let info = DispatchInfo::default(); let len = 0_usize; // stale +<<<<<<< HEAD assert_noop!( CheckNonce::(0u64.into()).validate(&1, CALL, &info, len), InvalidTransaction::Stale @@ -168,6 +197,49 @@ mod tests { assert_noop!( CheckNonce::(5u64.into()).pre_dispatch(&1, CALL, &info, len), InvalidTransaction::Future +======= + assert_storage_noop!({ + assert_eq!( + CheckNonce::(0u64.into()) + .validate_only(Some(1).into(), CALL, &info, len, External) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Stale) + ); + assert_eq!( + CheckNonce::(0u64.into()) + .validate_and_prepare(Some(1).into(), CALL, &info, len) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Stale) + ); + }); + // correct + assert_ok!(CheckNonce::(1u64.into()).validate_only( + Some(1).into(), + CALL, + &info, + len, + External, + )); + assert_ok!(CheckNonce::(1u64.into()).validate_and_prepare( + Some(1).into(), + CALL, + &info, + len + )); + // future + assert_ok!(CheckNonce::(5u64.into()).validate_only( + Some(1).into(), + CALL, + &info, + len, + External, + )); + assert_eq!( + CheckNonce::(5u64.into()) + .validate_and_prepare(Some(1).into(), CALL, &info, len) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Future) +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) ); }) } @@ -198,6 +270,7 @@ mod tests { let info = DispatchInfo::default(); let len = 0_usize; // Both providers and sufficients zero +<<<<<<< HEAD assert_noop!( CheckNonce::(1u64.into()).validate(&1, CALL, &info, len), InvalidTransaction::Payment @@ -212,6 +285,137 @@ mod tests { // Non-zero sufficients assert_ok!(CheckNonce::(1u64.into()).validate(&3, CALL, &info, len)); assert_ok!(CheckNonce::(1u64.into()).pre_dispatch(&3, CALL, &info, len)); +======= + assert_storage_noop!({ + assert_eq!( + CheckNonce::(1u64.into()) + .validate_only(Some(1).into(), CALL, &info, len, External) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Payment) + ); + assert_eq!( + CheckNonce::(1u64.into()) + .validate_and_prepare(Some(1).into(), CALL, &info, len) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Payment) + ); + }); + // Non-zero providers + assert_ok!(CheckNonce::(1u64.into()).validate_only( + Some(2).into(), + CALL, + &info, + len, + External, + )); + assert_ok!(CheckNonce::(1u64.into()).validate_and_prepare( + Some(2).into(), + CALL, + &info, + len + )); + // Non-zero sufficients + assert_ok!(CheckNonce::(1u64.into()).validate_only( + Some(3).into(), + CALL, + &info, + len, + External, + )); + assert_ok!(CheckNonce::(1u64.into()).validate_and_prepare( + Some(3).into(), + CALL, + &info, + len + )); + }) + } + + #[test] + fn unsigned_check_nonce_works() { + new_test_ext().execute_with(|| { + let info = DispatchInfo::default(); + let len = 0_usize; + let (_, val, origin) = CheckNonce::(1u64.into()) + .validate(None.into(), CALL, &info, len, (), CALL, External) + .unwrap(); + assert!(!origin.is_transaction_authorized()); + assert_ok!(CheckNonce::(1u64.into()).prepare(val, &origin, CALL, &info, len)); + }) + } + + #[test] + fn check_nonce_preserves_account_data() { + new_test_ext().execute_with(|| { + crate::Account::::insert( + 1, + crate::AccountInfo { + nonce: 1u64.into(), + consumers: 0, + providers: 1, + sufficients: 0, + data: 0, + }, + ); + let info = DispatchInfo::default(); + let len = 0_usize; + // run the validation step + let (_, val, origin) = CheckNonce::(1u64.into()) + .validate(Some(1).into(), CALL, &info, len, (), CALL, External) + .unwrap(); + // mutate `AccountData` for the caller + crate::Account::::mutate(1, |info| { + info.data = 42; + }); + // run the preparation step + assert_ok!(CheckNonce::(1u64.into()).prepare(val, &origin, CALL, &info, len)); + // only the nonce should be altered by the preparation step + let expected_info = crate::AccountInfo { + nonce: 2u64.into(), + consumers: 0, + providers: 1, + sufficients: 0, + data: 42, + }; + assert_eq!(crate::Account::::get(1), expected_info); + }) + } + + #[test] + fn check_nonce_skipped_and_refund_for_other_origins() { + new_test_ext().execute_with(|| { + let ext = CheckNonce::(1u64.into()); + + let mut info = CALL.get_dispatch_info(); + info.extension_weight = ext.weight(CALL); + + // Ensure we test the refund. + assert!(info.extension_weight != Weight::zero()); + + let len = CALL.encoded_size(); + + let origin = crate::RawOrigin::Root.into(); + let (pre, origin) = ext.validate_and_prepare(origin, CALL, &info, len).unwrap(); + + assert!(origin.as_system_ref().unwrap().is_root()); + + let pd_res = Ok(()); + let mut post_info = frame_support::dispatch::PostDispatchInfo { + actual_weight: Some(info.total_weight()), + pays_fee: Default::default(), + }; + + as TransactionExtension>::post_dispatch( + pre, + &info, + &mut post_info, + len, + &pd_res, + ) + .unwrap(); + + assert_eq!(post_info.actual_weight, Some(info.call_weight)); +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) }) } } diff --git a/substrate/frame/system/src/extensions/check_weight.rs b/substrate/frame/system/src/extensions/check_weight.rs index 22da2a5b9872..b321997abe7d 100644 --- a/substrate/frame/system/src/extensions/check_weight.rs +++ b/substrate/frame/system/src/extensions/check_weight.rs @@ -19,6 +19,7 @@ use crate::{limits::BlockWeights, Config, Pallet, LOG_TARGET}; use codec::{Decode, Encode}; use frame_support::{ dispatch::{DispatchInfo, PostDispatchInfo}, + pallet_prelude::TransactionSource, traits::Get, }; use scale_info::TypeInfo; @@ -231,8 +232,17 @@ where _call: &Self::Call, info: &DispatchInfoOf, len: usize, +<<<<<<< HEAD ) -> TransactionValidity { Self::do_validate(info, len) +======= + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + _source: TransactionSource, + ) -> ValidateResult { + let (validity, next_len) = Self::do_validate(info, len)?; + Ok((validity, next_len, origin)) +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) } fn pre_dispatch_unsigned( diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs index 538d88bfacfa..b28e3f88e65a 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs @@ -47,10 +47,15 @@ extern crate alloc; use codec::{Decode, Encode}; use frame_support::{ dispatch::{DispatchInfo, DispatchResult, PostDispatchInfo}, +<<<<<<< HEAD traits::{ fungibles::{Balanced, Inspect}, IsType, }, +======= + pallet_prelude::TransactionSource, + traits::IsType, +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) DefaultNoBound, }; use pallet_transaction_payment::OnChargeTransaction; @@ -262,9 +267,22 @@ where call: &Self::Call, info: &DispatchInfoOf, len: usize, +<<<<<<< HEAD ) -> TransactionValidity { use pallet_transaction_payment::ChargeTransactionPayment; let (fee, _) = self.withdraw_fee(who, call, info, len)?; +======= + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + _source: TransactionSource, + ) -> ValidateResult { + let Some(who) = origin.as_system_origin_signer() else { + return Ok((ValidTransaction::default(), Val::NoCharge, origin)) + }; + // Non-mutating call of `compute_fee` to calculate the fee used in the transaction priority. + let fee = pallet_transaction_payment::Pallet::::compute_fee(len as u32, info, self.tip); + self.can_withdraw_fee(&who, call, info, fee)?; +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) let priority = ChargeTransactionPayment::::get_priority(info, len, self.tip, fee); Ok(ValidTransaction { priority, ..Default::default() }) } diff --git a/substrate/frame/transaction-payment/asset-tx-payment/src/lib.rs b/substrate/frame/transaction-payment/asset-tx-payment/src/lib.rs index 97f1116993fc..b398bb4cd595 100644 --- a/substrate/frame/transaction-payment/asset-tx-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/asset-tx-payment/src/lib.rs @@ -38,6 +38,10 @@ use codec::{Decode, Encode}; use frame_support::{ dispatch::{DispatchInfo, DispatchResult, PostDispatchInfo}, +<<<<<<< HEAD +======= + pallet_prelude::{TransactionSource, Weight}, +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) traits::{ tokens::{ fungibles::{Balanced, Credit, Inspect}, @@ -240,7 +244,17 @@ where call: &Self::Call, info: &DispatchInfoOf, len: usize, +<<<<<<< HEAD ) -> TransactionValidity { +======= + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + _source: TransactionSource, + ) -> Result< + (ValidTransaction, Self::Val, ::RuntimeOrigin), + TransactionValidityError, + > { +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) use pallet_transaction_payment::ChargeTransactionPayment; let (fee, _) = self.withdraw_fee(who, call, info, len)?; let priority = ChargeTransactionPayment::::get_priority(info, len, self.tip, fee); diff --git a/substrate/frame/transaction-payment/skip-feeless-payment/src/lib.rs b/substrate/frame/transaction-payment/skip-feeless-payment/src/lib.rs index 3ab38743bafd..1203500567fe 100644 --- a/substrate/frame/transaction-payment/skip-feeless-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/skip-feeless-payment/src/lib.rs @@ -39,6 +39,7 @@ use codec::{Decode, Encode}; use frame_support::{ dispatch::{CheckIfFeeless, DispatchResult}, + pallet_prelude::TransactionSource, traits::{IsType, OriginTrait}, }; use scale_info::{StaticTypeInfo, TypeInfo}; @@ -129,11 +130,31 @@ where call: &Self::Call, info: &DispatchInfoOf, len: usize, +<<<<<<< HEAD ) -> TransactionValidity { if call.is_feeless(&::RuntimeOrigin::signed(who.clone())) { Ok(ValidTransaction::default()) } else { self.0.validate(who, call, info, len) +======= + self_implicit: S::Implicit, + inherited_implication: &impl Encode, + source: TransactionSource, + ) -> ValidateResult { + if call.is_feeless(&origin) { + Ok((Default::default(), Skip(origin.caller().clone()), origin)) + } else { + let (x, y, z) = self.0.validate( + origin, + call, + info, + len, + self_implicit, + inherited_implication, + source, + )?; + Ok((x, Apply(y), z)) +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) } } diff --git a/substrate/frame/transaction-payment/skip-feeless-payment/src/mock.rs b/substrate/frame/transaction-payment/skip-feeless-payment/src/mock.rs index d6d600f24e77..7a96fb296f11 100644 --- a/substrate/frame/transaction-payment/skip-feeless-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/skip-feeless-payment/src/mock.rs @@ -55,7 +55,14 @@ impl SignedExtension for DummyExtension { _call: &Self::Call, _info: &DispatchInfoOf, _len: usize, +<<<<<<< HEAD ) -> TransactionValidity { +======= + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + _source: TransactionSource, + ) -> ValidateResult { +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) ValidateCount::mutate(|c| *c += 1); Ok(Default::default()) } diff --git a/substrate/frame/transaction-payment/skip-feeless-payment/src/tests.rs b/substrate/frame/transaction-payment/skip-feeless-payment/src/tests.rs index adee52d6b3ce..53d0fb89c769 100644 --- a/substrate/frame/transaction-payment/skip-feeless-payment/src/tests.rs +++ b/substrate/frame/transaction-payment/skip-feeless-payment/src/tests.rs @@ -18,6 +18,10 @@ use crate::mock::{ pallet_dummy::Call, DummyExtension, PreDispatchCount, Runtime, RuntimeCall, ValidateCount, }; use frame_support::dispatch::DispatchInfo; +<<<<<<< HEAD +======= +use sp_runtime::{traits::DispatchTransaction, transaction_validity::TransactionSource}; +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) #[test] fn skip_feeless_payment_works() { @@ -40,13 +44,33 @@ fn validate_works() { let call = RuntimeCall::DummyPallet(Call::::aux { data: 1 }); SkipCheckIfFeeless::::from(DummyExtension) +<<<<<<< HEAD .validate(&0, &call, &DispatchInfo::default(), 0) +======= + .validate_only( + Some(0).into(), + &call, + &DispatchInfo::default(), + 0, + TransactionSource::External, + ) +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) .unwrap(); assert_eq!(ValidateCount::get(), 1); let call = RuntimeCall::DummyPallet(Call::::aux { data: 0 }); SkipCheckIfFeeless::::from(DummyExtension) +<<<<<<< HEAD .validate(&0, &call, &DispatchInfo::default(), 0) +======= + .validate_only( + Some(0).into(), + &call, + &DispatchInfo::default(), + 0, + TransactionSource::External, + ) +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) .unwrap(); assert_eq!(ValidateCount::get(), 1); } diff --git a/substrate/frame/transaction-payment/src/lib.rs b/substrate/frame/transaction-payment/src/lib.rs index 69fad6e0e324..0929dea05d3d 100644 --- a/substrate/frame/transaction-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/src/lib.rs @@ -54,6 +54,7 @@ use frame_support::{ dispatch::{ DispatchClass, DispatchInfo, DispatchResult, GetDispatchInfo, Pays, PostDispatchInfo, }, + pallet_prelude::TransactionSource, traits::{Defensive, EstimateCallFee, Get}, weights::{Weight, WeightToFee}, }; @@ -843,8 +844,22 @@ where call: &Self::Call, info: &DispatchInfoOf, len: usize, +<<<<<<< HEAD ) -> TransactionValidity { let (final_fee, _) = self.withdraw_fee(who, call, info, len)?; +======= + _: (), + _implication: &impl Encode, + _source: TransactionSource, + ) -> Result< + (ValidTransaction, Self::Val, ::RuntimeOrigin), + TransactionValidityError, + > { + let Ok(who) = frame_system::ensure_signed(origin.clone()) else { + return Ok((ValidTransaction::default(), Val::NoCharge, origin)); + }; + let final_fee = self.can_withdraw_fee(&who, call, info, len)?; +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) let tip = self.0; Ok(ValidTransaction { priority: Self::get_priority(info, len, tip, final_fee), diff --git a/substrate/frame/transaction-payment/src/tests.rs b/substrate/frame/transaction-payment/src/tests.rs index bc0efd2d64a3..2c19d60bb51f 100644 --- a/substrate/frame/transaction-payment/src/tests.rs +++ b/substrate/frame/transaction-payment/src/tests.rs @@ -21,7 +21,14 @@ use crate as pallet_transaction_payment; use codec::Encode; use sp_runtime::{ +<<<<<<< HEAD testing::TestXt, traits::One, transaction_validity::InvalidTransaction, BuildStorage, +======= + generic::UncheckedExtrinsic, + traits::{DispatchTransaction, One}, + transaction_validity::{InvalidTransaction, TransactionSource::External}, + BuildStorage, +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) }; use frame_support::{ @@ -237,12 +244,16 @@ fn signed_extension_allows_free_transactions() { class: DispatchClass::Operational, pays_fee: Pays::No, }; +<<<<<<< HEAD assert_ok!(ChargeTransactionPayment::::from(0).validate( &1, CALL, &operational_transaction, len )); +======= + assert_ok!(Ext::from(0).validate_only(Some(1).into(), CALL, &op_tx, len, External)); +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) // like a InsecureFreeNormal let free_transaction = DispatchInfo { @@ -250,6 +261,7 @@ fn signed_extension_allows_free_transactions() { class: DispatchClass::Normal, pays_fee: Pays::Yes, }; +<<<<<<< HEAD assert_noop!( ChargeTransactionPayment::::from(0).validate( &1, @@ -257,6 +269,12 @@ fn signed_extension_allows_free_transactions() { &free_transaction, len ), +======= + assert_eq!( + Ext::from(0) + .validate_only(Some(1).into(), CALL, &free_tx, len, External) + .unwrap_err(), +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) TransactionValidityError::Invalid(InvalidTransaction::Payment), ); }); @@ -678,6 +696,7 @@ fn should_alter_operational_priority() { .unwrap() .priority; +<<<<<<< HEAD assert_eq!(priority, 60); let priority = ChargeTransactionPayment::(2 * tip) @@ -685,6 +704,22 @@ fn should_alter_operational_priority() { .unwrap() .priority; +======= + let ext = Ext::from(tip); + let priority = ext + .validate_only(Some(2).into(), CALL, &normal, len, External) + .unwrap() + .0 + .priority; + assert_eq!(priority, 60); + + let ext = Ext::from(2 * tip); + let priority = ext + .validate_only(Some(2).into(), CALL, &normal, len, External) + .unwrap() + .0 + .priority; +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) assert_eq!(priority, 110); }); @@ -694,6 +729,7 @@ fn should_alter_operational_priority() { class: DispatchClass::Operational, pays_fee: Pays::Yes, }; +<<<<<<< HEAD let priority = ChargeTransactionPayment::(tip) .validate(&2, CALL, &op, len) .unwrap() @@ -704,6 +740,17 @@ fn should_alter_operational_priority() { .validate(&2, CALL, &op, len) .unwrap() .priority; +======= + + let ext = Ext::from(tip); + let priority = + ext.validate_only(Some(2).into(), CALL, &op, len, External).unwrap().0.priority; + assert_eq!(priority, 5810); + + let ext = Ext::from(2 * tip); + let priority = + ext.validate_only(Some(2).into(), CALL, &op, len, External).unwrap().0.priority; +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) assert_eq!(priority, 6110); }); } @@ -719,11 +766,20 @@ fn no_tip_has_some_priority() { class: DispatchClass::Normal, pays_fee: Pays::Yes, }; +<<<<<<< HEAD let priority = ChargeTransactionPayment::(tip) .validate(&2, CALL, &normal, len) .unwrap() .priority; +======= + let ext = Ext::from(tip); + let priority = ext + .validate_only(Some(2).into(), CALL, &normal, len, External) + .unwrap() + .0 + .priority; +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) assert_eq!(priority, 10); }); @@ -733,10 +789,16 @@ fn no_tip_has_some_priority() { class: DispatchClass::Operational, pays_fee: Pays::Yes, }; +<<<<<<< HEAD let priority = ChargeTransactionPayment::(tip) .validate(&2, CALL, &op, len) .unwrap() .priority; +======= + let ext = Ext::from(tip); + let priority = + ext.validate_only(Some(2).into(), CALL, &op, len, External).unwrap().0.priority; +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) assert_eq!(priority, 5510); }); } @@ -753,9 +815,17 @@ fn higher_tip_have_higher_priority() { class: DispatchClass::Normal, pays_fee: Pays::Yes, }; +<<<<<<< HEAD priority1 = ChargeTransactionPayment::(tip) .validate(&2, CALL, &normal, len) .unwrap() +======= + let ext = Ext::from(tip); + pri1 = ext + .validate_only(Some(2).into(), CALL, &normal, len, External) + .unwrap() + .0 +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) .priority; }); @@ -765,10 +835,15 @@ fn higher_tip_have_higher_priority() { class: DispatchClass::Operational, pays_fee: Pays::Yes, }; +<<<<<<< HEAD priority2 = ChargeTransactionPayment::(tip) .validate(&2, CALL, &op, len) .unwrap() .priority; +======= + let ext = Ext::from(tip); + pri2 = ext.validate_only(Some(2).into(), CALL, &op, len, External).unwrap().0.priority; +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) }); (priority1, priority2) diff --git a/substrate/frame/verify-signature/src/benchmarking.rs b/substrate/frame/verify-signature/src/benchmarking.rs new file mode 100644 index 000000000000..475cf4cec591 --- /dev/null +++ b/substrate/frame/verify-signature/src/benchmarking.rs @@ -0,0 +1,78 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Benchmarks for Verify Signature Pallet + +#![cfg(feature = "runtime-benchmarks")] + +extern crate alloc; + +use super::*; + +#[allow(unused)] +use crate::{extension::VerifySignature, Config, Pallet as VerifySignaturePallet}; +use alloc::vec; +use frame_benchmarking::{v2::*, BenchmarkError}; +use frame_support::{ + dispatch::{DispatchInfo, GetDispatchInfo}, + pallet_prelude::TransactionSource, +}; +use frame_system::{Call as SystemCall, RawOrigin}; +use sp_io::hashing::blake2_256; +use sp_runtime::traits::{AsTransactionAuthorizedOrigin, Dispatchable, TransactionExtension}; + +pub trait BenchmarkHelper { + fn create_signature(entropy: &[u8], msg: &[u8]) -> (Signature, Signer); +} + +#[benchmarks(where + T: Config + Send + Sync, + T::RuntimeCall: Dispatchable + GetDispatchInfo, + T::RuntimeOrigin: AsTransactionAuthorizedOrigin, +)] +mod benchmarks { + use super::*; + + #[benchmark] + fn verify_signature() -> Result<(), BenchmarkError> { + let entropy = [42u8; 256]; + let call: T::RuntimeCall = SystemCall::remark { remark: vec![] }.into(); + let info = call.get_dispatch_info(); + let msg = call.using_encoded(blake2_256).to_vec(); + let (signature, signer) = T::BenchmarkHelper::create_signature(&entropy, &msg[..]); + let ext = VerifySignature::::new_with_signature(signature, signer); + + #[block] + { + assert!(ext + .validate( + RawOrigin::None.into(), + &call, + &info, + 0, + (), + &call, + TransactionSource::External + ) + .is_ok()); + } + + Ok(()) + } + + impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::Test); +} diff --git a/substrate/frame/verify-signature/src/extension.rs b/substrate/frame/verify-signature/src/extension.rs new file mode 100644 index 000000000000..d48991e7a1da --- /dev/null +++ b/substrate/frame/verify-signature/src/extension.rs @@ -0,0 +1,158 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Transaction extension which validates a signature against a payload constructed from a call and +//! the rest of the transaction extension pipeline. + +use crate::{Config, WeightInfo}; +use codec::{Decode, Encode}; +use frame_support::{pallet_prelude::TransactionSource, traits::OriginTrait}; +use scale_info::TypeInfo; +use sp_io::hashing::blake2_256; +use sp_runtime::{ + impl_tx_ext_default, + traits::{ + transaction_extension::TransactionExtension, AsTransactionAuthorizedOrigin, DispatchInfoOf, + Dispatchable, Verify, + }, + transaction_validity::{InvalidTransaction, TransactionValidityError, ValidTransaction}, +}; +use sp_weights::Weight; + +/// Extension that, if enabled, validates a signature type against the payload constructed from the +/// call and the rest of the transaction extension pipeline. This extension provides the +/// functionality that traditionally signed transactions had with the implicit signature checking +/// implemented in [`Checkable`](sp_runtime::traits::Checkable). It is meant to be placed ahead of +/// any other extensions that do authorization work in the [`TransactionExtension`] pipeline. +#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub enum VerifySignature +where + T: Config + Send + Sync, +{ + /// The extension will verify the signature and, if successful, authorize a traditionally + /// signed transaction. + Signed { + /// The signature provided by the transaction submitter. + signature: T::Signature, + /// The account that signed the payload. + account: T::AccountId, + }, + /// The extension is disabled and will be passthrough. + Disabled, +} + +impl core::fmt::Debug for VerifySignature +where + T: Config + Send + Sync, +{ + #[cfg(feature = "std")] + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "VerifySignature") + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result { + Ok(()) + } +} + +impl VerifySignature +where + T: Config + Send + Sync, +{ + /// Create a new extension instance that will validate the provided signature. + pub fn new_with_signature(signature: T::Signature, account: T::AccountId) -> Self { + Self::Signed { signature, account } + } + + /// Create a new passthrough extension instance. + pub fn new_disabled() -> Self { + Self::Disabled + } +} + +impl TransactionExtension for VerifySignature +where + T: Config + Send + Sync, + ::RuntimeOrigin: AsTransactionAuthorizedOrigin, +{ + const IDENTIFIER: &'static str = "VerifyMultiSignature"; + type Implicit = (); + type Val = (); + type Pre = (); + + fn weight(&self, _call: &T::RuntimeCall) -> Weight { + match &self { + // The benchmarked weight of the payload construction and signature checking. + Self::Signed { .. } => T::WeightInfo::verify_signature(), + // When the extension is passthrough, it consumes no weight. + Self::Disabled => Weight::zero(), + } + } + + fn validate( + &self, + mut origin: ::RuntimeOrigin, + _call: &T::RuntimeCall, + _info: &DispatchInfoOf, + _len: usize, + _: (), + inherited_implication: &impl Encode, + _source: TransactionSource, + ) -> Result< + (ValidTransaction, Self::Val, ::RuntimeOrigin), + TransactionValidityError, + > { + // If the extension is disabled, return early. + let (signature, account) = match &self { + Self::Signed { signature, account } => (signature, account), + Self::Disabled => return Ok((Default::default(), (), origin)), + }; + + // This extension must receive an unauthorized origin as it is meant to headline the + // authorization extension pipeline. Any extensions that precede this one must not authorize + // any origin and serve some other functional purpose. + if origin.is_transaction_authorized() { + return Err(InvalidTransaction::BadSigner.into()); + } + + // Construct the payload that the signature will be validated against. The inherited + // implication contains the encoded bytes of the call and all of the extension data of the + // extensions that follow in the `TransactionExtension` pipeline. + // + // In other words: + // - extensions that precede this extension are ignored in terms of signature validation; + // - extensions that follow this extension are included in the payload to be signed (as if + // they were the entire `SignedExtension` pipeline in the traditional signed transaction + // model). + // + // The encoded bytes of the payload are then hashed using `blake2_256`. + let msg = inherited_implication.using_encoded(blake2_256); + + // The extension was enabled, so the signature must match. + if !signature.verify(&msg[..], account) { + Err(InvalidTransaction::BadProof)? + } + + // Return the signer as the transaction origin. + origin.set_caller_from_signed(account.clone()); + Ok((ValidTransaction::default(), (), origin)) + } + + impl_tx_ext_default!(T::RuntimeCall; prepare); +} diff --git a/substrate/frame/verify-signature/src/tests.rs b/substrate/frame/verify-signature/src/tests.rs new file mode 100644 index 000000000000..505a33a883c2 --- /dev/null +++ b/substrate/frame/verify-signature/src/tests.rs @@ -0,0 +1,132 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests for Utility Pallet + +#![cfg(test)] + +use super::*; + +use extension::VerifySignature; +use frame_support::{ + derive_impl, + dispatch::GetDispatchInfo, + pallet_prelude::{InvalidTransaction, TransactionSource, TransactionValidityError}, + traits::OriginTrait, +}; +use frame_system::Call as SystemCall; +use sp_io::hashing::blake2_256; +use sp_runtime::{ + testing::{TestSignature, UintAuthorityId}, + traits::DispatchTransaction, +}; + +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + VerifySignaturePallet: crate, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Test { + type Block = Block; +} + +#[cfg(feature = "runtime-benchmarks")] +pub struct BenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl crate::BenchmarkHelper for BenchmarkHelper { + fn create_signature(_entropy: &[u8], msg: &[u8]) -> (TestSignature, u64) { + (TestSignature(0, msg.to_vec()), 0) + } +} + +impl crate::Config for Test { + type Signature = TestSignature; + type AccountIdentifier = UintAuthorityId; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = BenchmarkHelper; +} + +#[cfg(feature = "runtime-benchmarks")] +pub fn new_test_ext() -> sp_io::TestExternalities { + use sp_runtime::BuildStorage; + let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +#[test] +fn verification_works() { + let who = 0; + let call: RuntimeCall = SystemCall::remark { remark: vec![] }.into(); + let sig = TestSignature(0, call.using_encoded(blake2_256).to_vec()); + let info = call.get_dispatch_info(); + + let (_, _, origin) = VerifySignature::::new_with_signature(sig, who) + .validate_only(None.into(), &call, &info, 0, TransactionSource::External) + .unwrap(); + assert_eq!(origin.as_signer().unwrap(), &who) +} + +#[test] +fn bad_signature() { + let who = 0; + let call: RuntimeCall = SystemCall::remark { remark: vec![] }.into(); + let sig = TestSignature(0, b"bogus message".to_vec()); + let info = call.get_dispatch_info(); + + assert_eq!( + VerifySignature::::new_with_signature(sig, who) + .validate_only(None.into(), &call, &info, 0, TransactionSource::External) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::BadProof) + ); +} + +#[test] +fn bad_starting_origin() { + let who = 0; + let call: RuntimeCall = SystemCall::remark { remark: vec![] }.into(); + let sig = TestSignature(0, b"bogus message".to_vec()); + let info = call.get_dispatch_info(); + + assert_eq!( + VerifySignature::::new_with_signature(sig, who) + .validate_only(Some(42).into(), &call, &info, 0, TransactionSource::External) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::BadSigner) + ); +} + +#[test] +fn disabled_extension_works() { + let who = 42; + let call: RuntimeCall = SystemCall::remark { remark: vec![] }.into(); + let info = call.get_dispatch_info(); + + let (_, _, origin) = VerifySignature::::new_disabled() + .validate_only(Some(who).into(), &call, &info, 0, TransactionSource::External) + .unwrap(); + assert_eq!(origin.as_signer().unwrap(), &who) +} diff --git a/substrate/primitives/runtime/src/generic/checked_extrinsic.rs b/substrate/primitives/runtime/src/generic/checked_extrinsic.rs index 44325920beee..bdebe78d1985 100644 --- a/substrate/primitives/runtime/src/generic/checked_extrinsic.rs +++ b/substrate/primitives/runtime/src/generic/checked_extrinsic.rs @@ -59,12 +59,29 @@ where info: &DispatchInfoOf, len: usize, ) -> TransactionValidity { +<<<<<<< HEAD if let Some((ref id, ref extra)) = self.signed { Extra::validate(extra, id, &self.function, info, len) } else { let valid = Extra::validate_unsigned(&self.function, info, len)?; let unsigned_validation = U::validate_unsigned(source, &self.function)?; Ok(valid.combine_with(unsigned_validation)) +======= + match self.format { + ExtrinsicFormat::Bare => { + let inherent_validation = I::validate_unsigned(source, &self.function)?; + #[allow(deprecated)] + let legacy_validation = Extension::bare_validate(&self.function, info, len)?; + Ok(legacy_validation.combine_with(inherent_validation)) + }, + ExtrinsicFormat::Signed(ref signer, ref extension) => { + let origin = Some(signer.clone()).into(); + extension.validate_only(origin, &self.function, info, len, source).map(|x| x.0) + }, + ExtrinsicFormat::General(ref extension) => extension + .validate_only(None.into(), &self.function, info, len, source) + .map(|x| x.0), +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) } } diff --git a/substrate/primitives/runtime/src/traits/transaction_extension/as_transaction_extension.rs b/substrate/primitives/runtime/src/traits/transaction_extension/as_transaction_extension.rs new file mode 100644 index 000000000000..282064078fe3 --- /dev/null +++ b/substrate/primitives/runtime/src/traits/transaction_extension/as_transaction_extension.rs @@ -0,0 +1,131 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The [AsTransactionExtension] adapter struct for adapting [SignedExtension]s to +//! [TransactionExtension]s. + +#![allow(deprecated)] + +use scale_info::TypeInfo; +use sp_core::RuntimeDebug; + +use crate::{ + traits::{AsSystemOriginSigner, SignedExtension, ValidateResult}, + transaction_validity::{InvalidTransaction, TransactionSource}, +}; + +use super::*; + +/// Adapter to use a `SignedExtension` in the place of a `TransactionExtension`. +#[derive(TypeInfo, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] +#[deprecated = "Convert your SignedExtension to a TransactionExtension."] +pub struct AsTransactionExtension(pub SE); + +impl Default for AsTransactionExtension { + fn default() -> Self { + Self(SE::default()) + } +} + +impl From for AsTransactionExtension { + fn from(value: SE) -> Self { + Self(value) + } +} + +impl TransactionExtension for AsTransactionExtension +where + ::RuntimeOrigin: AsSystemOriginSigner + Clone, +{ + const IDENTIFIER: &'static str = SE::IDENTIFIER; + type Implicit = SE::AdditionalSigned; + + fn implicit(&self) -> Result { + self.0.additional_signed() + } + fn metadata() -> Vec { + SE::metadata() + } + fn weight(&self, _call: &SE::Call) -> Weight { + Weight::zero() + } + type Val = (); + type Pre = SE::Pre; + + fn validate( + &self, + origin: ::RuntimeOrigin, + call: &SE::Call, + info: &DispatchInfoOf, + len: usize, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + _source: TransactionSource, + ) -> ValidateResult { + let who = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?; + let r = self.0.validate(who, call, info, len)?; + Ok((r, (), origin)) + } + + fn prepare( + self, + _: (), + origin: &::RuntimeOrigin, + call: &SE::Call, + info: &DispatchInfoOf, + len: usize, + ) -> Result { + let who = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?; + self.0.pre_dispatch(who, call, info, len) + } + + fn post_dispatch_details( + pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, + len: usize, + result: &DispatchResult, + ) -> Result { + SE::post_dispatch(Some(pre), info, post_info, len, result)?; + Ok(Weight::zero()) + } + + fn bare_validate( + call: &SE::Call, + info: &DispatchInfoOf, + len: usize, + ) -> TransactionValidity { + SE::validate_unsigned(call, info, len) + } + + fn bare_validate_and_prepare( + call: &SE::Call, + info: &DispatchInfoOf, + len: usize, + ) -> Result<(), TransactionValidityError> { + SE::pre_dispatch_unsigned(call, info, len) + } + + fn bare_post_dispatch( + info: &DispatchInfoOf, + post_info: &mut PostDispatchInfoOf, + len: usize, + result: &DispatchResult, + ) -> Result<(), TransactionValidityError> { + SE::post_dispatch(None, info, post_info, len, result) + } +} diff --git a/substrate/primitives/runtime/src/traits/transaction_extension/dispatch_transaction.rs b/substrate/primitives/runtime/src/traits/transaction_extension/dispatch_transaction.rs new file mode 100644 index 000000000000..19c8a2b2d496 --- /dev/null +++ b/substrate/primitives/runtime/src/traits/transaction_extension/dispatch_transaction.rs @@ -0,0 +1,160 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The [DispatchTransaction] trait. + +use crate::{ + traits::AsTransactionAuthorizedOrigin, + transaction_validity::{InvalidTransaction, TransactionSource}, +}; + +use super::*; + +/// Single-function utility trait with a blanket impl over [`TransactionExtension`] in order to +/// provide transaction dispatching functionality. We avoid implementing this directly on the trait +/// since we never want it to be overriden by the trait implementation. +pub trait DispatchTransaction { + /// The origin type of the transaction. + type Origin; + /// The info type. + type Info; + /// The resultant type. + type Result; + /// The `Val` of the extension. + type Val; + /// The `Pre` of the extension. + type Pre; + /// Just validate a transaction. + /// + /// The is basically the same as [validate](TransactionExtension::validate), except that there + /// is no need to supply the bond data. + fn validate_only( + &self, + origin: Self::Origin, + call: &Call, + info: &Self::Info, + len: usize, + source: TransactionSource, + ) -> Result<(ValidTransaction, Self::Val, Self::Origin), TransactionValidityError>; + /// Validate and prepare a transaction, ready for dispatch. + fn validate_and_prepare( + self, + origin: Self::Origin, + call: &Call, + info: &Self::Info, + len: usize, + ) -> Result<(Self::Pre, Self::Origin), TransactionValidityError>; + /// Dispatch a transaction with the given base origin and call. + fn dispatch_transaction( + self, + origin: Self::Origin, + call: Call, + info: &Self::Info, + len: usize, + ) -> Self::Result; + /// Do everything which would be done in a [dispatch_transaction](Self::dispatch_transaction), + /// but instead of executing the call, execute `substitute` instead. Since this doesn't actually + /// dispatch the call, it doesn't need to consume it and so `call` can be passed as a reference. + fn test_run( + self, + origin: Self::Origin, + call: &Call, + info: &Self::Info, + len: usize, + substitute: impl FnOnce( + Self::Origin, + ) -> crate::DispatchResultWithInfo<::PostInfo>, + ) -> Self::Result; +} + +impl, Call: Dispatchable + Encode> DispatchTransaction for T +where + ::RuntimeOrigin: AsTransactionAuthorizedOrigin, +{ + type Origin = ::RuntimeOrigin; + type Info = DispatchInfoOf; + type Result = crate::ApplyExtrinsicResultWithInfo>; + type Val = T::Val; + type Pre = T::Pre; + + fn validate_only( + &self, + origin: Self::Origin, + call: &Call, + info: &DispatchInfoOf, + len: usize, + source: TransactionSource, + ) -> Result<(ValidTransaction, T::Val, Self::Origin), TransactionValidityError> { + match self.validate(origin, call, info, len, self.implicit()?, call, source) { + // After validation, some origin must have been authorized. + Ok((_, _, origin)) if !origin.is_transaction_authorized() => + Err(InvalidTransaction::UnknownOrigin.into()), + res => res, + } + } + fn validate_and_prepare( + self, + origin: Self::Origin, + call: &Call, + info: &DispatchInfoOf, + len: usize, + ) -> Result<(T::Pre, Self::Origin), TransactionValidityError> { + let (_, val, origin) = + self.validate_only(origin, call, info, len, TransactionSource::InBlock)?; + let pre = self.prepare(val, &origin, &call, info, len)?; + Ok((pre, origin)) + } + fn dispatch_transaction( + self, + origin: ::RuntimeOrigin, + call: Call, + info: &DispatchInfoOf, + len: usize, + ) -> Self::Result { + let (pre, origin) = self.validate_and_prepare(origin, &call, info, len)?; + let mut res = call.dispatch(origin); + let pd_res = res.map(|_| ()).map_err(|e| e.error); + let post_info = match &mut res { + Ok(info) => info, + Err(err) => &mut err.post_info, + }; + post_info.set_extension_weight(info); + T::post_dispatch(pre, info, post_info, len, &pd_res)?; + Ok(res) + } + fn test_run( + self, + origin: Self::Origin, + call: &Call, + info: &Self::Info, + len: usize, + substitute: impl FnOnce( + Self::Origin, + ) -> crate::DispatchResultWithInfo<::PostInfo>, + ) -> Self::Result { + let (pre, origin) = self.validate_and_prepare(origin, &call, info, len)?; + let mut res = substitute(origin); + let pd_res = res.map(|_| ()).map_err(|e| e.error); + let post_info = match &mut res { + Ok(info) => info, + Err(err) => &mut err.post_info, + }; + post_info.set_extension_weight(info); + T::post_dispatch(pre, info, post_info, len, &pd_res)?; + Ok(res) + } +} diff --git a/substrate/primitives/runtime/src/traits/transaction_extension/mod.rs b/substrate/primitives/runtime/src/traits/transaction_extension/mod.rs new file mode 100644 index 000000000000..f8c5dc6a724e --- /dev/null +++ b/substrate/primitives/runtime/src/traits/transaction_extension/mod.rs @@ -0,0 +1,641 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The transaction extension trait. + +use crate::{ + scale_info::{MetaType, StaticTypeInfo}, + transaction_validity::{ + TransactionSource, TransactionValidity, TransactionValidityError, ValidTransaction, + }, + DispatchResult, +}; +use codec::{Codec, Decode, Encode}; +use impl_trait_for_tuples::impl_for_tuples; +#[doc(hidden)] +pub use sp_std::marker::PhantomData; +use sp_std::{self, fmt::Debug, prelude::*}; +use sp_weights::Weight; +use tuplex::{PopFront, PushBack}; + +use super::{ + DispatchInfoOf, DispatchOriginOf, Dispatchable, ExtensionPostDispatchWeightHandler, + PostDispatchInfoOf, RefundWeight, +}; + +mod as_transaction_extension; +mod dispatch_transaction; +#[allow(deprecated)] +pub use as_transaction_extension::AsTransactionExtension; +pub use dispatch_transaction::DispatchTransaction; + +/// Shortcut for the result value of the `validate` function. +pub type ValidateResult = + Result<(ValidTransaction, Val, DispatchOriginOf), TransactionValidityError>; + +/// Means by which a transaction may be extended. This type embodies both the data and the logic +/// that should be additionally associated with the transaction. It should be plain old data. +/// +/// The simplest transaction extension would be the Unit type (and empty pipeline) `()`. This +/// executes no additional logic and implies a dispatch of the transaction's call using the +/// inherited origin (either `None` or `Signed`, depending on whether this is a signed or general +/// transaction). +/// +/// Transaction extensions are capable of altering certain associated semantics: +/// +/// - They may define the origin with which the transaction's call should be dispatched. +/// - They may define various parameters used by the transaction queue to determine under what +/// conditions the transaction should be retained and introduced on-chain. +/// - They may define whether this transaction is acceptable for introduction on-chain at all. +/// +/// Each of these semantics are defined by the `validate` function. +/// +/// **NOTE: Transaction extensions cannot under any circumstances alter the call itself.** +/// +/// Transaction extensions are capable of defining logic which is executed additionally to the +/// dispatch of the call: +/// +/// - They may define logic which must be executed prior to the dispatch of the call. +/// - They may also define logic which must be executed after the dispatch of the call. +/// +/// Each of these semantics are defined by the `prepare` and `post_dispatch_details` functions +/// respectively. +/// +/// Finally, transaction extensions may define additional data to help define the implications of +/// the logic they introduce. This additional data may be explicitly defined by the transaction +/// author (in which case it is included as part of the transaction body), or it may be implicitly +/// defined by the transaction extension based around the on-chain state (which the transaction +/// author is assumed to know). This data may be utilized by the above logic to alter how a node's +/// transaction queue treats this transaction. +/// +/// ## Default implementations +/// +/// Of the 6 functions in this trait along with `TransactionExtension`, 2 of them must return a +/// value of an associated type on success, with only `implicit` having a default implementation. +/// This means that default implementations cannot be provided for `validate` and `prepare`. +/// However, a macro is provided [impl_tx_ext_default](crate::impl_tx_ext_default) which is capable +/// of generating default implementations for both of these functions. If you do not wish to +/// introduce additional logic into the transaction pipeline, then it is recommended that you use +/// this macro to implement these functions. Additionally, [weight](TransactionExtension::weight) +/// can return a default value, which would mean the extension is weightless, but it is not +/// implemented by default. Instead, implementers can explicitly choose to implement this default +/// behavior through the same [impl_tx_ext_default](crate::impl_tx_ext_default) macro. +/// +/// If your extension does any post-flight logic, then the functionality must be implemented in +/// [post_dispatch_details](TransactionExtension::post_dispatch_details). This function can return +/// the actual weight used by the extension during an entire dispatch cycle by wrapping said weight +/// value in a `Some`. This is useful in computing fee refunds, similar to how post dispatch +/// information is used to refund fees for calls. Alternatively, a `None` can be returned, which +/// means that the worst case scenario weight, namely the value returned by +/// [weight](TransactionExtension::weight), is the actual weight. This particular piece of logic +/// is embedded in the default implementation of +/// [post_dispatch](TransactionExtension::post_dispatch) so that the weight is assumed to be worst +/// case scenario, but implementers of this trait can correct it with extra effort. Therefore, all +/// users of an extension should use [post_dispatch](TransactionExtension::post_dispatch), with +/// [post_dispatch_details](TransactionExtension::post_dispatch_details) considered an internal +/// function. +/// +/// ## Pipelines, Inherited Implications, and Authorized Origins +/// +/// Requiring a single transaction extension to define all of the above semantics would be +/// cumbersome and would lead to a lot of boilerplate. Instead, transaction extensions are +/// aggregated into pipelines, which are tuples of transaction extensions. Each extension in the +/// pipeline is executed in order, and the output of each extension is aggregated and/or relayed as +/// the input to the next extension in the pipeline. +/// +/// This ordered composition happens with all data types ([Val](TransactionExtension::Val), +/// [Pre](TransactionExtension::Pre) and [Implicit](TransactionExtension::Implicit)) as well as +/// all functions. There are important consequences stemming from how the composition affects the +/// meaning of the `origin` and `implication` parameters as well as the results. Whereas the +/// [prepare](TransactionExtension::prepare) and +/// [post_dispatch](TransactionExtension::post_dispatch) functions are clear in their meaning, the +/// [validate](TransactionExtension::validate) function is fairly sophisticated and warrants further +/// explanation. +/// +/// Firstly, the `origin` parameter. The `origin` passed into the first item in a pipeline is simply +/// that passed into the tuple itself. It represents an authority who has authorized the implication +/// of the transaction, as of the extension it has been passed into *and any further extensions it +/// may pass though, all the way to, and including, the transaction's dispatch call itself. Each +/// following item in the pipeline is passed the origin which the previous item returned. The origin +/// returned from the final item in the pipeline is the origin which is returned by the tuple +/// itself. +/// +/// This means that if a constituent extension returns a different origin to the one it was called +/// with, then (assuming no other extension changes it further) *this new origin will be used for +/// all extensions following it in the pipeline, and will be returned from the pipeline to be used +/// as the origin for the call's dispatch*. The call itself as well as all these extensions +/// following may each imply consequence for this origin. We call this the *inherited implication*. +/// +/// The *inherited implication* is the cumulated on-chain effects born by whatever origin is +/// returned. It is expressed to the [validate](TransactionExtension::validate) function only as the +/// `implication` argument which implements the [Encode] trait. A transaction extension may define +/// its own implications through its own fields and the +/// [implicit](TransactionExtension::implicit) function. This is only utilized by extensions +/// which precede it in a pipeline or, if the transaction is an old-school signed transaction, the +/// underlying transaction verification logic. +/// +/// **The inherited implication passed as the `implication` parameter to +/// [validate](TransactionExtension::validate) does not include the extension's inner data itself +/// nor does it include the result of the extension's `implicit` function.** If you both provide an +/// implication and rely on the implication, then you need to manually aggregate your extensions +/// implication with the aggregated implication passed in. +/// +/// In the post dispatch pipeline, the actual weight of each extension is accrued in the +/// [PostDispatchInfo](PostDispatchInfoOf) of that transaction sequentially with each +/// [post_dispatch](TransactionExtension::post_dispatch) call. This means that an extension handling +/// transaction payment and refunds should be at the end of the pipeline in order to capture the +/// correct amount of weight used during the call. This is because one cannot know the actual weight +/// of an extension after post dispatch without running the post dispatch ahead of time. +pub trait TransactionExtension: + Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo +{ + /// Unique identifier of this signed extension. + /// + /// This will be exposed in the metadata to identify the signed extension used in an extrinsic. + const IDENTIFIER: &'static str; + + /// Any additional data which was known at the time of transaction construction and can be + /// useful in authenticating the transaction. This is determined dynamically in part from the + /// on-chain environment using the `implicit` function and not directly contained in the + /// transaction itself and therefore is considered "implicit". + type Implicit: Codec + StaticTypeInfo; + + /// Determine any additional data which was known at the time of transaction construction and + /// can be useful in authenticating the transaction. The expected usage of this is to include in + /// any data which is signed and verified as part of transaction validation. Also perform any + /// pre-signature-verification checks and return an error if needed. + fn implicit(&self) -> Result { + use crate::transaction_validity::InvalidTransaction::IndeterminateImplicit; + Ok(Self::Implicit::decode(&mut &[][..]).map_err(|_| IndeterminateImplicit)?) + } + + /// Returns the metadata for this extension. + /// + /// As a [`TransactionExtension`] can be a tuple of [`TransactionExtension`]s we need to return + /// a `Vec` that holds the metadata of each one. Each individual `TransactionExtension` must + /// return *exactly* one [`TransactionExtensionMetadata`]. + /// + /// This method provides a default implementation that returns a vec containing a single + /// [`TransactionExtensionMetadata`]. + fn metadata() -> Vec { + sp_std::vec![TransactionExtensionMetadata { + identifier: Self::IDENTIFIER, + ty: scale_info::meta_type::(), + implicit: scale_info::meta_type::() + }] + } + + /// The type that encodes information that can be passed from `validate` to `prepare`. + type Val; + + /// The type that encodes information that can be passed from `prepare` to `post_dispatch`. + type Pre; + + /// The weight consumed by executing this extension instance fully during transaction dispatch. + fn weight(&self, call: &Call) -> Weight; + + /// Validate a transaction for the transaction queue. + /// + /// This function can be called frequently by the transaction queue to obtain transaction + /// validity against current state. It should perform all checks that determine a valid + /// transaction, that can pay for its execution and quickly eliminate ones that are stale or + /// incorrect. + /// + /// Parameters: + /// - `origin`: The origin of the transaction which this extension inherited; coming from an + /// "old-school" *signed transaction*, this will be a system `RawOrigin::Signed` value. If the + /// transaction is a "new-school" *General Transaction*, then this will be a system + /// `RawOrigin::None` value. If this extension is an item in a composite, then it could be + /// anything which was previously returned as an `origin` value in the result of a `validate` + /// call. + /// - `call`: The `Call` wrapped by this extension. + /// - `info`: Information concerning, and inherent to, the transaction's call. + /// - `len`: The total length of the encoded transaction. + /// - `inherited_implication`: The *implication* which this extension inherits. This is a tuple + /// of the transaction's call and some additional opaque-but-encodable data. Coming directly + /// from a transaction, the latter is [()]. However, if this extension is expressed as part of + /// a composite type, then the latter component is equal to any further implications to which + /// the returned `origin` could potentially apply. See Pipelines, Inherited Implications, and + /// Authorized Origins for more information. + /// + /// Returns a [ValidateResult], which is a [Result] whose success type is a tuple of + /// [ValidTransaction] (defining useful metadata for the transaction queue), the [Self::Val] + /// token of this transaction, which gets passed into [prepare](TransactionExtension::prepare), + /// and the origin of the transaction, which gets passed into + /// [prepare](TransactionExtension::prepare) and is ultimately used for dispatch. + fn validate( + &self, + origin: DispatchOriginOf, + call: &Call, + info: &DispatchInfoOf, + len: usize, + self_implicit: Self::Implicit, + inherited_implication: &impl Encode, + source: TransactionSource, + ) -> ValidateResult; + + /// Do any pre-flight stuff for a transaction after validation. + /// + /// This is for actions which do not happen in the transaction queue but only immediately prior + /// to the point of dispatch on-chain. This should not return an error, since errors should + /// already have been identified during the [validate](TransactionExtension::validate) call. If + /// an error is returned, the transaction will be considered invalid but no state changes will + /// happen and therefore work done in [validate](TransactionExtension::validate) will not be + /// paid for. + /// + /// Unlike `validate`, this function may consume `self`. + /// + /// Parameters: + /// - `val`: `Self::Val` returned by the result of the `validate` call. + /// - `origin`: The origin returned by the result of the `validate` call. + /// - `call`: The `Call` wrapped by this extension. + /// - `info`: Information concerning, and inherent to, the transaction's call. + /// - `len`: The total length of the encoded transaction. + /// + /// Returns a [Self::Pre] value on success, which gets passed into + /// [post_dispatch](TransactionExtension::post_dispatch) and after the call is dispatched. + /// + /// IMPORTANT: **Checks made in validation need not be repeated here.** + fn prepare( + self, + val: Self::Val, + origin: &DispatchOriginOf, + call: &Call, + info: &DispatchInfoOf, + len: usize, + ) -> Result; + + /// Do any post-flight stuff for an extrinsic. + /// + /// `_pre` contains the output of `prepare`. + /// + /// This gets given the `DispatchResult` `_result` from the extrinsic and can, if desired, + /// introduce a `TransactionValidityError`, causing the block to become invalid for including + /// it. + /// + /// On success, the caller must return the amount of unspent weight left over by this extension + /// after dispatch. By default, this function returns no unspent weight, which means the entire + /// weight computed for the worst case scenario is consumed. + /// + /// WARNING: This function does not automatically keep track of accumulated "actual" weight. + /// Unless this weight is handled at the call site, use + /// [post_dispatch](TransactionExtension::post_dispatch) + /// instead. + /// + /// Parameters: + /// - `pre`: `Self::Pre` returned by the result of the `prepare` call prior to dispatch. + /// - `info`: Information concerning, and inherent to, the transaction's call. + /// - `post_info`: Information concerning the dispatch of the transaction's call. + /// - `len`: The total length of the encoded transaction. + /// - `result`: The result of the dispatch. + /// + /// WARNING: It is dangerous to return an error here. To do so will fundamentally invalidate the + /// transaction and any block that it is included in, causing the block author to not be + /// compensated for their work in validating the transaction or producing the block so far. It + /// can only be used safely when you *know* that the transaction is one that would only be + /// introduced by the current block author. + fn post_dispatch_details( + _pre: Self::Pre, + _info: &DispatchInfoOf, + _post_info: &PostDispatchInfoOf, + _len: usize, + _result: &DispatchResult, + ) -> Result { + Ok(Weight::zero()) + } + + /// A wrapper for [`post_dispatch_details`](TransactionExtension::post_dispatch_details) that + /// refunds the unspent weight consumed by this extension into the post dispatch information. + /// + /// If `post_dispatch_details` returns a non-zero unspent weight, which, by definition, must be + /// less than the worst case weight provided by [weight](TransactionExtension::weight), that + /// is the value refunded in `post_info`. + /// + /// If no unspent weight is reported by `post_dispatch_details`, this function assumes the worst + /// case weight and does not refund anything. + /// + /// For more information, look into + /// [post_dispatch_details](TransactionExtension::post_dispatch_details). + fn post_dispatch( + pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &mut PostDispatchInfoOf, + len: usize, + result: &DispatchResult, + ) -> Result<(), TransactionValidityError> { + let unspent_weight = Self::post_dispatch_details(pre, info, &post_info, len, result)?; + post_info.refund(unspent_weight); + + Ok(()) + } + + /// Validation logic for bare extrinsics. + /// + /// NOTE: This function will be migrated to a separate `InherentExtension` interface. + fn bare_validate( + _call: &Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> TransactionValidity { + Ok(ValidTransaction::default()) + } + + /// All pre-flight logic run before dispatching bare extrinsics. + /// + /// NOTE: This function will be migrated to a separate `InherentExtension` interface. + fn bare_validate_and_prepare( + _call: &Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> Result<(), TransactionValidityError> { + Ok(()) + } + + /// Post dispatch logic run after dispatching bare extrinsics. + /// + /// NOTE: This function will be migrated to a separate `InherentExtension` interface. + fn bare_post_dispatch( + _info: &DispatchInfoOf, + _post_info: &mut PostDispatchInfoOf, + _len: usize, + _result: &DispatchResult, + ) -> Result<(), TransactionValidityError> { + Ok(()) + } +} + +/// Helper macro to be used in a `impl TransactionExtension` block to add default implementations of +/// `weight`, `validate`, `prepare` or any combinations of the them. +/// +/// The macro is to be used with 2 parameters, separated by ";": +/// - the `Call` type; +/// - the functions for which a default implementation should be generated, separated by " "; +/// available options are `weight`, `validate` and `prepare`. +/// +/// Example usage: +/// ```nocompile +/// impl TransactionExtension for EmptyExtension { +/// type Val = (); +/// type Pre = (); +/// +/// impl_tx_ext_default!(FirstCall; weight validate prepare); +/// } +/// +/// impl TransactionExtension for SimpleExtension { +/// type Val = u32; +/// type Pre = (); +/// +/// fn weight(&self, _: &SecondCall) -> Weight { +/// Weight::zero() +/// } +/// +/// fn validate( +/// &self, +/// _origin: ::RuntimeOrigin, +/// _call: &SecondCall, +/// _info: &DispatchInfoOf, +/// _len: usize, +/// _self_implicit: Self::Implicit, +/// _inherited_implication: &impl Encode, +/// ) -> ValidateResult { +/// Ok((Default::default(), 42u32, origin)) +/// } +/// +/// impl_tx_ext_default!(SecondCall; prepare); +/// } +/// ``` +#[macro_export] +macro_rules! impl_tx_ext_default { + ($call:ty ; , $( $rest:tt )*) => { + impl_tx_ext_default!{$call ; $( $rest )*} + }; + ($call:ty ; validate $( $rest:tt )*) => { + fn validate( + &self, + origin: $crate::traits::DispatchOriginOf<$call>, + _call: &$call, + _info: &$crate::traits::DispatchInfoOf<$call>, + _len: usize, + _self_implicit: Self::Implicit, + _inherited_implication: &impl $crate::codec::Encode, + _source: $crate::transaction_validity::TransactionSource, + ) -> $crate::traits::ValidateResult { + Ok((Default::default(), Default::default(), origin)) + } + impl_tx_ext_default!{$call ; $( $rest )*} + }; + ($call:ty ; prepare $( $rest:tt )*) => { + fn prepare( + self, + _val: Self::Val, + _origin: &$crate::traits::DispatchOriginOf<$call>, + _call: &$call, + _info: &$crate::traits::DispatchInfoOf<$call>, + _len: usize, + ) -> Result { + Ok(Default::default()) + } + impl_tx_ext_default!{$call ; $( $rest )*} + }; + ($call:ty ; weight $( $rest:tt )*) => { + fn weight(&self, _call: &$call) -> $crate::Weight { + $crate::Weight::zero() + } + impl_tx_ext_default!{$call ; $( $rest )*} + }; + ($call:ty ;) => {}; +} + +/// Information about a [`TransactionExtension`] for the runtime metadata. +pub struct TransactionExtensionMetadata { + /// The unique identifier of the [`TransactionExtension`]. + pub identifier: &'static str, + /// The type of the [`TransactionExtension`]. + pub ty: MetaType, + /// The type of the [`TransactionExtension`] additional signed data for the payload. + pub implicit: MetaType, +} + +#[impl_for_tuples(1, 12)] +impl TransactionExtension for Tuple { + const IDENTIFIER: &'static str = "Use `metadata()`!"; + for_tuples!( type Implicit = ( #( Tuple::Implicit ),* ); ); + fn implicit(&self) -> Result { + Ok(for_tuples!( ( #( Tuple.implicit()? ),* ) )) + } + fn metadata() -> Vec { + let mut ids = Vec::new(); + for_tuples!( #( ids.extend(Tuple::metadata()); )* ); + ids + } + + for_tuples!( type Val = ( #( Tuple::Val ),* ); ); + for_tuples!( type Pre = ( #( Tuple::Pre ),* ); ); + + fn weight(&self, call: &Call) -> Weight { + let mut weight = Weight::zero(); + for_tuples!( #( weight = weight.saturating_add(Tuple.weight(call)); )* ); + weight + } + + fn validate( + &self, + origin: ::RuntimeOrigin, + call: &Call, + info: &DispatchInfoOf, + len: usize, + self_implicit: Self::Implicit, + inherited_implication: &impl Encode, + source: TransactionSource, + ) -> Result< + (ValidTransaction, Self::Val, ::RuntimeOrigin), + TransactionValidityError, + > { + let valid = ValidTransaction::default(); + let val = (); + let following_explicit_implications = for_tuples!( ( #( &self.Tuple ),* ) ); + let following_implicit_implications = self_implicit; + + for_tuples!(#( + // Implication of this pipeline element not relevant for later items, so we pop it. + let (_item, following_explicit_implications) = following_explicit_implications.pop_front(); + let (item_implicit, following_implicit_implications) = following_implicit_implications.pop_front(); + let (item_valid, item_val, origin) = { + let implications = ( + // The first is the implications born of the fact we return the mutated + // origin. + inherited_implication, + // This is the explicitly made implication born of the fact the new origin is + // passed into the next items in this pipeline-tuple. + &following_explicit_implications, + // This is the implicitly made implication born of the fact the new origin is + // passed into the next items in this pipeline-tuple. + &following_implicit_implications, + ); + Tuple.validate(origin, call, info, len, item_implicit, &implications, source)? + }; + let valid = valid.combine_with(item_valid); + let val = val.push_back(item_val); + )* ); + Ok((valid, val, origin)) + } + + fn prepare( + self, + val: Self::Val, + origin: &::RuntimeOrigin, + call: &Call, + info: &DispatchInfoOf, + len: usize, + ) -> Result { + Ok(for_tuples!( ( #( + Tuple::prepare(self.Tuple, val.Tuple, origin, call, info, len)? + ),* ) )) + } + + fn post_dispatch_details( + pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, + len: usize, + result: &DispatchResult, + ) -> Result { + let mut total_unspent_weight = Weight::zero(); + for_tuples!( #({ + let unspent_weight = Tuple::post_dispatch_details(pre.Tuple, info, post_info, len, result)?; + total_unspent_weight = total_unspent_weight.saturating_add(unspent_weight); + })* ); + Ok(total_unspent_weight) + } + + fn post_dispatch( + pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &mut PostDispatchInfoOf, + len: usize, + result: &DispatchResult, + ) -> Result<(), TransactionValidityError> { + for_tuples!( #( Tuple::post_dispatch(pre.Tuple, info, post_info, len, result)?; )* ); + Ok(()) + } + + fn bare_validate(call: &Call, info: &DispatchInfoOf, len: usize) -> TransactionValidity { + let valid = ValidTransaction::default(); + for_tuples!(#( + let item_valid = Tuple::bare_validate(call, info, len)?; + let valid = valid.combine_with(item_valid); + )* ); + Ok(valid) + } + + fn bare_validate_and_prepare( + call: &Call, + info: &DispatchInfoOf, + len: usize, + ) -> Result<(), TransactionValidityError> { + for_tuples!( #( Tuple::bare_validate_and_prepare(call, info, len)?; )* ); + Ok(()) + } + + fn bare_post_dispatch( + info: &DispatchInfoOf, + post_info: &mut PostDispatchInfoOf, + len: usize, + result: &DispatchResult, + ) -> Result<(), TransactionValidityError> { + for_tuples!( #( Tuple::bare_post_dispatch(info, post_info, len, result)?; )* ); + Ok(()) + } +} + +impl TransactionExtension for () { + const IDENTIFIER: &'static str = "UnitTransactionExtension"; + type Implicit = (); + fn implicit(&self) -> sp_std::result::Result { + Ok(()) + } + type Val = (); + type Pre = (); + fn weight(&self, _call: &Call) -> Weight { + Weight::zero() + } + fn validate( + &self, + origin: ::RuntimeOrigin, + _call: &Call, + _info: &DispatchInfoOf, + _len: usize, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + _source: TransactionSource, + ) -> Result< + (ValidTransaction, (), ::RuntimeOrigin), + TransactionValidityError, + > { + Ok((ValidTransaction::default(), (), origin)) + } + fn prepare( + self, + _val: (), + _origin: &::RuntimeOrigin, + _call: &Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> Result<(), TransactionValidityError> { + Ok(()) + } +} diff --git a/substrate/test-utils/runtime/src/lib.rs b/substrate/test-utils/runtime/src/lib.rs index d1a3eaa2daa9..80e9232c7690 100644 --- a/substrate/test-utils/runtime/src/lib.rs +++ b/substrate/test-utils/runtime/src/lib.rs @@ -285,7 +285,17 @@ impl sp_runtime::traits::SignedExtension for CheckSubstrateCall { call: &Self::Call, _info: &DispatchInfoOf, _len: usize, +<<<<<<< HEAD ) -> TransactionValidity { +======= + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + _source: TransactionSource, + ) -> Result< + (ValidTransaction, Self::Val, ::RuntimeOrigin), + TransactionValidityError, + > { +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) log::trace!(target: LOG_TARGET, "validate"); match call { RuntimeCall::SubstrateTest(ref substrate_test_call) => @@ -1050,8 +1060,13 @@ mod tests { use sp_consensus::BlockOrigin; use sp_core::{storage::well_known_keys::HEAP_PAGES, traits::CallContext}; use sp_runtime::{ +<<<<<<< HEAD traits::{Hash as _, SignedExtension}, transaction_validity::{InvalidTransaction, ValidTransaction}, +======= + traits::{DispatchTransaction, Hash as _}, + transaction_validity::{InvalidTransaction, TransactionSource::External, ValidTransaction}, +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) }; use substrate_test_runtime_client::{ prelude::*, runtime::TestAPI, DefaultTestClientBuilderExt, TestClientBuilder, @@ -1208,7 +1223,12 @@ mod tests { &x, &ExtrinsicBuilder::new_call_with_priority(16).build().function, &info, +<<<<<<< HEAD len +======= + len, + External, +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) ) .unwrap() .priority, @@ -1221,7 +1241,12 @@ mod tests { &x, &ExtrinsicBuilder::new_call_do_not_propagate().build().function, &info, +<<<<<<< HEAD len +======= + len, + External, +>>>>>>> 8e3d9296 ([Tx ext stage 2: 1/4] Add `TransactionSource` as argument in `TransactionExtension::validate` (#6323)) ) .unwrap() .propagate,