diff --git a/Cargo.lock b/Cargo.lock index 31badd989fe1a..a5f741627f533 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1239,7 +1239,7 @@ dependencies = [ "fixed-hash", "impl-rlp", "impl-serde 0.3.0", - "tiny-keccak 2.0.2", + "tiny-keccak 2.0.1", ] [[package]] @@ -1974,7 +1974,7 @@ dependencies = [ "indexmap", "log", "slab", - "tokio 0.2.16", + "tokio 0.2.13", "tokio-util", ] @@ -2186,7 +2186,7 @@ dependencies = [ "net2", "pin-project", "time", - "tokio 0.2.16", + "tokio 0.2.13", "tower-service", "want 0.3.0", ] @@ -2204,7 +2204,7 @@ dependencies = [ "log", "rustls 0.17.0", "rustls-native-certs", - "tokio 0.2.16", + "tokio 0.2.13", "tokio-rustls", "webpki", ] @@ -4873,9 +4873,9 @@ dependencies = [ [[package]] name = "paste" -version = "0.1.10" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4fb1930692d1b6a9cfabdde3d06ea0a7d186518e2f4d67660d8970e2fa647a" +checksum = "092d791bf7847f70bbd49085489fba25fc2c193571752bff9e36e74e72403932" dependencies = [ "paste-impl", "proc-macro-hack", @@ -4883,9 +4883,9 @@ dependencies = [ [[package]] name = "paste-impl" -version = "0.1.10" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62486e111e571b1e93b710b61e8f493c0013be39629b714cb166bdb06aa5a8a" +checksum = "406c23fb4c45cc6f68a9bbabb8ec7bd6f8cfcbd17e9e8f72c2460282f8325729" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -5229,9 +5229,9 @@ dependencies = [ [[package]] name = "quicksink" -version = "0.1.2" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77de3c815e5a160b1539c6592796801df2043ae35e123b46d73380cfa57af858" +checksum = "a8461ef7445f61fd72d8dcd0629ce724b9131b3c2eb36e83a5d3d4161c127530" dependencies = [ "futures-core", "futures-sink", @@ -5895,7 +5895,7 @@ dependencies = [ "substrate-prometheus-endpoint", "tempfile", "time", - "tokio 0.2.16", + "tokio 0.2.13", ] [[package]] @@ -6153,7 +6153,7 @@ dependencies = [ "substrate-test-runtime-client", "substrate-test-runtime-transaction-pool", "tempfile", - "tokio 0.2.16", + "tokio 0.2.13", ] [[package]] @@ -6335,7 +6335,7 @@ dependencies = [ "substrate-prometheus-endpoint", "substrate-test-runtime-client", "tempfile", - "tokio 0.2.16", + "tokio 0.2.13", ] [[package]] @@ -6500,7 +6500,7 @@ dependencies = [ "sp-utils", "substrate-test-runtime-client", "threadpool", - "tokio 0.2.16", + "tokio 0.2.13", ] [[package]] @@ -6918,18 +6918,18 @@ checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" [[package]] name = "serde" -version = "1.0.106" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" +checksum = "e707fbbf255b8fc8c3b99abb91e7257a622caeb20a9818cbadbeeede4e0932ff" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.106" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" +checksum = "ac5d00fc561ba2724df6758a17de23df5914f20e41cb00f94d5b7ae42fffaff8" dependencies = [ "proc-macro2", "quote 1.0.3", @@ -6938,9 +6938,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.51" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9" +checksum = "78a7a12c167809363ec3bd7329fc0a3369056996de43c4b37ef3cd54a6ce4867" dependencies = [ "itoa", "ryu", @@ -7389,7 +7389,7 @@ dependencies = [ "sp-storage", "substrate-bip39", "tiny-bip39", - "tiny-keccak 2.0.2", + "tiny-keccak 2.0.1", "twox-hash", "wasmi", "zeroize", @@ -7961,7 +7961,7 @@ dependencies = [ "sc-rpc-api", "serde", "sp-storage", - "tokio 0.2.16", + "tokio 0.2.13", ] [[package]] @@ -7997,7 +7997,7 @@ dependencies = [ "hyper 0.13.4", "log", "prometheus", - "tokio 0.2.16", + "tokio 0.2.13", ] [[package]] @@ -8399,9 +8399,9 @@ dependencies = [ [[package]] name = "tiny-keccak" -version = "2.0.2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +checksum = "2953ca5148619bc99695c1274cb54c5275bbb913c6adad87e72eaf8db9787f69" dependencies = [ "crunchy", ] @@ -8442,9 +8442,9 @@ dependencies = [ [[package]] name = "tokio" -version = "0.2.16" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee5a0dd887e37d37390c13ff8ac830f992307fe30a1fff0ab8427af67211ba28" +checksum = "0fa5e81d6bc4e67fe889d5783bd2a128ab2e0cfa487e0be16b6a8d177b101616" dependencies = [ "bytes 0.5.4", "fnv", @@ -8576,7 +8576,7 @@ checksum = "4adb8b3e5f86b707f1b54e7c15b6de52617a823608ccda98a15d3a24222f265a" dependencies = [ "futures-core", "rustls 0.17.0", - "tokio 0.2.16", + "tokio 0.2.13", "webpki", ] @@ -8688,7 +8688,7 @@ dependencies = [ "futures-sink", "log", "pin-project-lite", - "tokio 0.2.16", + "tokio 0.2.13", ] [[package]] @@ -9225,18 +9225,18 @@ dependencies = [ [[package]] name = "wast" -version = "13.0.0" +version = "12.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b20abd8b4a26f7e0d4dd5e357e90a3d555ec190e94472c9b2b27c5b9777f9ae" +checksum = "0615ba420811bcda39cf80e8a1bd75997aec09222bda35165920a07ef15cc695" dependencies = [ "leb128", ] [[package]] name = "wat" -version = "1.0.14" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51a615830ee3e7200b505c441fec09aac2f114deae69df52f215cb828ba112c4" +checksum = "095f615fbfcae695e3a4cea7d9f02f70561c81274c0142f45a12bf1e154d08bd" dependencies = [ "wast", ] diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index 94f033fd8f58e..7693f5f0c8065 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -269,7 +269,7 @@ pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; /// Executive: handles dispatch to the various modules. -pub type Executive = frame_executive::Executive, Runtime, AllModules>; +pub type Executive = frame_executive::Executive, AllModules>; impl_runtime_apis! { impl sp_api::Core for Runtime { diff --git a/bin/node/cli/src/factory_impl.rs b/bin/node/cli/src/factory_impl.rs index 1d1eabe29cbfb..178035f5debd7 100644 --- a/bin/node/cli/src/factory_impl.rs +++ b/bin/node/cli/src/factory_impl.rs @@ -56,6 +56,7 @@ impl FactoryState { frame_system::CheckEra::from(Era::mortal(256, phase)), frame_system::CheckNonce::from(index), frame_system::CheckWeight::new(), + frame_system::ValidateUnsigned::new(), pallet_transaction_payment::ChargeTransactionPayment::from(0), Default::default(), ) @@ -122,7 +123,7 @@ impl RuntimeAdapter for FactoryState { (*amount).into() ) ) - }, key, (version, genesis_hash.clone(), prior_block_hash.clone(), (), (), (), ())) + }, key, (version, genesis_hash.clone(), prior_block_hash.clone(), (), (), (), (), ())) } fn inherent_extrinsics(&self) -> InherentData { diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index 257068cf144ac..1ed3e8447adf7 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -608,6 +608,7 @@ mod tests { let check_era = frame_system::CheckEra::from(Era::Immortal); let check_nonce = frame_system::CheckNonce::from(index); let check_weight = frame_system::CheckWeight::new(); + let check_unsigned = frame_system::ValidateUnsigned::new(); let payment = pallet_transaction_payment::ChargeTransactionPayment::from(0); let extra = ( check_version, @@ -615,13 +616,14 @@ mod tests { check_era, check_nonce, check_weight, + check_unsigned, payment, Default::default(), ); let raw_payload = SignedPayload::from_raw( function, extra, - (version, genesis_hash, genesis_hash, (), (), (), ()) + (version, genesis_hash, genesis_hash, (), (), (), (), ()) ); let signature = raw_payload.using_encoded(|payload| { signer.sign(payload) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 54e236db96e96..8e04960442518 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -105,6 +105,7 @@ impl frame_system::offchain::CreateTransaction for frame_system::CheckEra::::from(generic::Era::mortal(period, current_block)), frame_system::CheckNonce::::from(index), frame_system::CheckWeight::::new(), + frame_system::ValidateUnsigned::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), Default::default(), ); @@ -127,8 +128,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to 0. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 240, - impl_version: 1, + spec_version: 241, + impl_version: 0, apis: RUNTIME_API_VERSIONS, }; @@ -708,6 +709,7 @@ pub type SignedExtra = ( frame_system::CheckEra, frame_system::CheckNonce, frame_system::CheckWeight, + frame_system::ValidateUnsigned, pallet_transaction_payment::ChargeTransactionPayment, pallet_contracts::CheckBlockGasLimit, ); @@ -718,7 +720,7 @@ pub type SignedPayload = generic::SignedPayload; /// Extrinsic type that has already been checked. pub type CheckedExtrinsic = generic::CheckedExtrinsic; /// Executive: handles dispatch to the various modules. -pub type Executive = frame_executive::Executive, Runtime, AllModules>; +pub type Executive = frame_executive::Executive, AllModules>; impl_runtime_apis! { impl sp_api::Core for Runtime { diff --git a/bin/node/testing/src/keyring.rs b/bin/node/testing/src/keyring.rs index 6b0d06875d692..93490e48c92d0 100644 --- a/bin/node/testing/src/keyring.rs +++ b/bin/node/testing/src/keyring.rs @@ -73,6 +73,7 @@ pub fn signed_extra(nonce: Index, extra_fee: Balance) -> SignedExtra { frame_system::CheckEra::from(Era::mortal(256, 0)), frame_system::CheckNonce::from(nonce), frame_system::CheckWeight::new(), + frame_system::ValidateUnsigned::new(), pallet_transaction_payment::ChargeTransactionPayment::from(extra_fee), Default::default(), ) diff --git a/bin/utils/subkey/src/main.rs b/bin/utils/subkey/src/main.rs index 08a46f1190ac2..dff63f94eec9a 100644 --- a/bin/utils/subkey/src/main.rs +++ b/bin/utils/subkey/src/main.rs @@ -688,6 +688,7 @@ fn create_extrinsic( frame_system::CheckEra::::from(Era::Immortal), frame_system::CheckNonce::::from(i), frame_system::CheckWeight::::new(), + frame_system::ValidateUnsigned::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(f), Default::default(), ) @@ -703,6 +704,7 @@ fn create_extrinsic( (), (), (), + (), ), ); let signature = raw_payload.using_encoded(|payload| signer.sign(payload)).into_runtime(); diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 10938bb7debc1..98b9083a3d656 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -119,6 +119,7 @@ use sp_runtime::{ }, transaction_validity::{ ValidTransaction, InvalidTransaction, TransactionValidity, TransactionValidityError, + TransactionSource, }, RuntimeDebug, }; @@ -1100,6 +1101,7 @@ impl SignedExtension for CheckBlockGasLimit { fn validate( &self, _: &Self::AccountId, + _: TransactionSource, call: &Self::Call, _: &DispatchInfoOf, _: usize, diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 1a5aa08454d14..09b686dae6f12 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -28,9 +28,10 @@ use assert_matches::assert_matches; use hex_literal::*; use codec::{Decode, Encode, KeyedVec}; use sp_runtime::{ - Perbill, BuildStorage, transaction_validity::{InvalidTransaction, ValidTransaction}, - traits::{BlakeTwo256, Hash, IdentityLookup, SignedExtension}, + Perbill, BuildStorage, testing::{Digest, DigestItem, Header, UintAuthorityId, H256}, + traits::{BlakeTwo256, Hash, IdentityLookup, SignedExtension}, + transaction_validity::{InvalidTransaction, ValidTransaction, TransactionSource}, }; use frame_support::{ assert_ok, assert_err, impl_outer_dispatch, impl_outer_event, impl_outer_origin, parameter_types, @@ -1632,16 +1633,21 @@ fn cannot_self_destruct_in_constructor() { #[test] fn check_block_gas_limit_works() { ExtBuilder::default().block_gas_limit(50).build().execute_with(|| { + let source = TransactionSource::External; let info = DispatchInfo { weight: 100, class: DispatchClass::Normal, pays_fee: true }; let check = CheckBlockGasLimit::(Default::default()); let call: Call = crate::Call::put_code(1000, vec![]).into(); assert_eq!( - check.validate(&0, &call, &info, 0), InvalidTransaction::ExhaustsResources.into(), + check.validate(&0, source, &call, &info, 0), + InvalidTransaction::ExhaustsResources.into(), ); let call: Call = crate::Call::update_schedule(Default::default()).into(); - assert_eq!(check.validate(&0, &call, &info, 0), Ok(Default::default())); + assert_eq!( + check.validate(&0, source, &call, &info, 0), + Ok(Default::default()) + ); }); } diff --git a/frame/example/src/lib.rs b/frame/example/src/lib.rs index 13985671c2e79..54dde4ba97ad8 100644 --- a/frame/example/src/lib.rs +++ b/frame/example/src/lib.rs @@ -269,6 +269,7 @@ use sp_runtime::{ }, transaction_validity::{ ValidTransaction, TransactionValidityError, InvalidTransaction, TransactionValidity, + TransactionSource, }, }; @@ -627,6 +628,7 @@ impl SignedExtension for WatchDummy { fn validate( &self, _who: &Self::AccountId, + _source: TransactionSource, call: &Self::Call, _info: &DispatchInfoOf, len: usize, @@ -826,16 +828,17 @@ mod tests { fn signed_ext_watch_dummy_works() { new_test_ext().execute_with(|| { let call = >::set_dummy(10); + let source = TransactionSource::External; let info = DispatchInfo::default(); assert_eq!( - WatchDummy::(PhantomData).validate(&1, &call, &info, 150) + WatchDummy::(PhantomData).validate(&1, source, &call, &info, 150) .unwrap() .priority, Bounded::max_value(), ); assert_eq!( - WatchDummy::(PhantomData).validate(&1, &call, &info, 250), + WatchDummy::(PhantomData).validate(&1, source, &call, &info, 250), InvalidTransaction::ExhaustsResources.into(), ); }) diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index 20c79fe4a5cac..3322882f24d67 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -59,19 +59,8 @@ //! # pub type Balances = u64; //! # pub type AllModules = u64; //! # pub enum Runtime {}; -//! # use sp_runtime::transaction_validity::{ -//! TransactionValidity, UnknownTransaction, TransactionSource, -//! # }; -//! # use sp_runtime::traits::ValidateUnsigned; -//! # impl ValidateUnsigned for Runtime { -//! # type Call = (); -//! # -//! # fn validate_unsigned(_source: TransactionSource, _call: &Self::Call) -> TransactionValidity { -//! # UnknownTransaction::NoUnsignedValidator.into() -//! # } -//! # } //! /// Executive: handles dispatch to the various modules. -//! pub type Executive = executive::Executive; +//! pub type Executive = executive::Executive; //! ``` #![cfg_attr(not(feature = "std"), no_std)] @@ -84,7 +73,7 @@ use frame_support::{ use sp_runtime::{ generic::Digest, ApplyExtrinsicResult, traits::{ - self, Header, Zero, One, Checkable, Applyable, CheckEqual, ValidateUnsigned, NumberFor, + self, Header, Zero, One, Checkable, Applyable, CheckEqual, NumberFor, Block as BlockT, Dispatchable, Saturating, }, transaction_validity::{TransactionValidity, TransactionSource}, @@ -102,21 +91,20 @@ pub type CheckedOf = >::Checked; pub type CallOf = as Applyable>::Call; pub type OriginOf = as Dispatchable>::Origin; -pub struct Executive( - PhantomData<(System, Block, Context, UnsignedValidator, AllModules)> +pub struct Executive( + PhantomData<(System, Block, Context, AllModules)> ); impl< System: frame_system::Trait, Block: traits::Block, Context: Default, - UnsignedValidator, AllModules: OnRuntimeUpgrade + OnInitialize + OnFinalize + - OffchainWorker, -> ExecuteBlock for Executive + OffchainWorker + +> ExecuteBlock for Executive where Block::Extrinsic: Checkable + Codec, CheckedOf: @@ -124,10 +112,9 @@ where GetDispatchInfo, CallOf: Dispatchable, OriginOf: From>, - UnsignedValidator: ValidateUnsigned>, { fn execute_block(block: Block) { - Executive::::execute_block(block); + Executive::::execute_block(block); } } @@ -135,13 +122,12 @@ impl< System: frame_system::Trait, Block: traits::Block, Context: Default, - UnsignedValidator, AllModules: OnRuntimeUpgrade + OnInitialize + OnFinalize + - OffchainWorker, -> Executive + OffchainWorker + +> Executive where Block::Extrinsic: Checkable + Codec, CheckedOf: @@ -149,7 +135,6 @@ where GetDispatchInfo, CallOf: Dispatchable, OriginOf: From>, - UnsignedValidator: ValidateUnsigned>, { /// Start the execution of a particular block. pub fn initialize_block(header: &System::Header) { @@ -307,7 +292,7 @@ where // Decode parameters and dispatch let dispatch_info = xt.get_dispatch_info(); - let r = Applyable::apply::(xt, &dispatch_info, encoded_len)?; + let r = Applyable::apply(xt, &dispatch_info, encoded_len)?; >::note_applied_extrinsic(&r, encoded_len as u32, dispatch_info); @@ -348,7 +333,7 @@ where let xt = uxt.check(&Default::default())?; let dispatch_info = xt.get_dispatch_info(); - xt.validate::(source, &dispatch_info, encoded_len) + xt.validate(source, &dispatch_info, encoded_len) } /// Start an offchain worker and generate extrinsics. @@ -386,7 +371,7 @@ mod tests { use sp_runtime::{ generic::Era, Perbill, DispatchError, testing::{Digest, Header, Block}, traits::{Header as HeaderT, BlakeTwo256, IdentityLookup, ConvertInto}, - transaction_validity::{InvalidTransaction, UnknownTransaction, TransactionValidityError}, + transaction_validity::{InvalidTransaction, TransactionValidityError}, }; use frame_support::{ impl_outer_event, impl_outer_origin, parameter_types, impl_outer_dispatch, @@ -509,7 +494,7 @@ mod tests { } impl custom::Trait for Runtime {} - impl ValidateUnsigned for Runtime { + impl traits::ValidateUnsigned for Runtime { type Call = Call; fn pre_dispatch(_call: &Self::Call) -> Result<(), TransactionValidityError> { @@ -522,7 +507,7 @@ mod tests { ) -> TransactionValidity { match call { Call::Balances(BalancesCall::set_balance(_, _, _)) => Ok(Default::default()), - _ => UnknownTransaction::NoUnsignedValidator.into(), + _ => InvalidTransaction::NoUnsignedValidator.into(), } } } @@ -543,17 +528,19 @@ mod tests { frame_system::CheckEra, frame_system::CheckNonce, frame_system::CheckWeight, + frame_system::ValidateUnsigned, pallet_transaction_payment::ChargeTransactionPayment, ); type AllModules = (System, Balances, Custom); type TestXt = sp_runtime::testing::TestXt; - type Executive = super::Executive, ChainContext, Runtime, AllModules>; + type Executive = super::Executive, ChainContext, AllModules>; fn extra(nonce: u64, fee: u64) -> SignedExtra { ( frame_system::CheckEra::from(Era::Immortal), frame_system::CheckNonce::from(nonce), frame_system::CheckWeight::new(), + frame_system::ValidateUnsigned::new(), pallet_transaction_payment::ChargeTransactionPayment::from(fee) ) } diff --git a/frame/support/src/unsigned.rs b/frame/support/src/unsigned.rs index 3bc6f692affc2..d1ba466006c52 100644 --- a/frame/support/src/unsigned.rs +++ b/frame/support/src/unsigned.rs @@ -18,7 +18,7 @@ pub use crate::sp_runtime::traits::ValidateUnsigned; #[doc(hidden)] pub use crate::sp_runtime::transaction_validity::{ - TransactionValidity, UnknownTransaction, TransactionValidityError, TransactionSource, + TransactionValidity, InvalidTransaction, TransactionValidityError, TransactionSource, }; @@ -69,12 +69,19 @@ macro_rules! impl_outer_validate_unsigned { impl $crate::unsigned::ValidateUnsigned for $runtime { type Call = Call; - fn pre_dispatch(call: &Self::Call) -> Result<(), $crate::unsigned::TransactionValidityError> { + fn pre_dispatch(call: &Self::Call)-> Result< + (), $crate::unsigned::TransactionValidityError + > { #[allow(unreachable_patterns)] match call { $( Call::$module(inner_call) => $module::pre_dispatch(inner_call), )* - // pre-dispatch should not stop inherent extrinsics, validation should prevent - // including arbitrary (non-inherent) extrinsics to blocks. + // Inherent extrinsics are going through this path as well. Their pallets + // do not specify `ValidateUnsigned`, so we do allow calls to those pallets + // to be dispatched. + // i.e. This means that unsigned calls to in-runtime pallets that don't + // specify `ValidateUnsigned` are not failing during `pre_dispatch`, but + // if the pallet provides `ValidateUnsigned` then it can control that + // behavior. _ => Ok(()), } } @@ -87,7 +94,11 @@ macro_rules! impl_outer_validate_unsigned { #[allow(unreachable_patterns)] match call { $( Call::$module(inner_call) => $module::validate_unsigned(source, inner_call), )* - _ => $crate::unsigned::UnknownTransaction::NoUnsignedValidator.into(), + // `validate_unsigned` always fails if the call is not whitelisted. A + // consequence of this and `pre_dispatch` behavior is to allow inherent + // extrinsics to pallets even if these pallets don't specify + // `UnsignedValidator`. + _ => $crate::unsigned::InvalidTransaction::NoUnsignedValidator.into(), } } } diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index a38a8854c75c3..5bd4f18b2f3cb 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -103,7 +103,7 @@ use sp_runtime::{ generic::{self, Era}, transaction_validity::{ ValidTransaction, TransactionPriority, TransactionLongevity, TransactionValidityError, - InvalidTransaction, TransactionValidity, + InvalidTransaction, TransactionValidity, TransactionSource, }, traits::{ self, CheckEqual, AtLeast32Bit, Zero, SignedExtension, Lookup, LookupError, @@ -1280,16 +1280,18 @@ impl SignedExtension for CheckWeight where _call: &Self::Call, info: &DispatchInfoOf, len: usize, - ) -> Result<(), TransactionValidityError> { + ) -> Result { if info.class == DispatchClass::Mandatory { Err(InvalidTransaction::MandatoryDispatch)? } + Self::do_pre_dispatch(info, len) } fn validate( &self, _who: &Self::AccountId, + _source: TransactionSource, _call: &Self::Call, info: &DispatchInfoOf, len: usize, @@ -1304,11 +1306,12 @@ impl SignedExtension for CheckWeight where _call: &Self::Call, info: &DispatchInfoOf, len: usize, - ) -> Result<(), TransactionValidityError> { + ) -> Result { Self::do_pre_dispatch(info, len) } fn validate_unsigned( + _source: TransactionSource, _call: &Self::Call, info: &DispatchInfoOf, len: usize, @@ -1345,6 +1348,60 @@ impl Debug for CheckWeight { } } + +/// Unsigned transactions validation. +#[derive(Encode, Decode, Clone, Eq, PartialEq)] +pub struct ValidateUnsigned(PhantomData); + +impl ValidateUnsigned { + /// Creates new `SignedExtension` to validate unsigned transactions. + pub fn new() -> Self { + Self(PhantomData) + } +} + +impl SignedExtension for ValidateUnsigned where + T: Trait + traits::ValidateUnsigned::Call> + Send + Sync, +{ + type AccountId = T::AccountId; + type Call = ::Call; + type AdditionalSigned = (); + type Pre = (); + + const IDENTIFIER: &'static str = "ValidateUnsigned"; + + fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { Ok(()) } + + fn pre_dispatch_unsigned( + call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> Result { + ::pre_dispatch(call) + } + + fn validate_unsigned( + source: TransactionSource, + call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> TransactionValidity { + ::validate_unsigned(source, call) + } +} + +impl Debug for ValidateUnsigned { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + write!(f, "ValidateUnsigned") + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + Ok(()) + } +} + /// Nonce check and increment to give replay protection for transactions. #[derive(Encode, Decode, Clone, Eq, PartialEq)] pub struct CheckNonce(#[codec(compact)] T::Index); @@ -1385,7 +1442,7 @@ impl SignedExtension for CheckNonce where _call: &Self::Call, _info: &DispatchInfoOf, _len: usize, - ) -> Result<(), TransactionValidityError> { + ) -> Result { let mut account = Account::::get(who); if self.0 != account.nonce { return Err( @@ -1404,6 +1461,7 @@ impl SignedExtension for CheckNonce where fn validate( &self, who: &Self::AccountId, + _source: TransactionSource, _call: &Self::Call, info: &DispatchInfoOf, _len: usize, @@ -1470,6 +1528,7 @@ impl SignedExtension for CheckEra { fn validate( &self, _who: &Self::AccountId, + _source: TransactionSource, _call: &Self::Call, _info: &DispatchInfoOf, _len: usize, @@ -1864,16 +1923,17 @@ mod tests { fn signed_ext_check_nonce_works() { new_test_ext().execute_with(|| { Account::::insert(1, AccountInfo { nonce: 1, refcount: 0, data: 0 }); + let source = TransactionSource::External; let info = DispatchInfo::default(); let len = 0_usize; // stale - assert!(CheckNonce::(0).validate(&1, CALL, &info, len).is_err()); + assert!(CheckNonce::(0).validate(&1, source, CALL, &info, len).is_err()); assert!(CheckNonce::(0).pre_dispatch(&1, CALL, &info, len).is_err()); // correct - assert!(CheckNonce::(1).validate(&1, CALL, &info, len).is_ok()); + assert!(CheckNonce::(1).validate(&1, source, CALL, &info, len).is_ok()); assert!(CheckNonce::(1).pre_dispatch(&1, CALL, &info, len).is_ok()); // future - assert!(CheckNonce::(5).validate(&1, CALL, &info, len).is_ok()); + assert!(CheckNonce::(5).validate(&1, source, CALL, &info, len).is_ok()); assert!(CheckNonce::(5).pre_dispatch(&1, CALL, &info, len).is_err()); }) } @@ -1958,18 +2018,19 @@ mod tests { #[test] fn signed_ext_check_weight_priority_works() { new_test_ext().execute_with(|| { + let source = TransactionSource::External; let normal = DispatchInfo { weight: 100, class: DispatchClass::Normal, pays_fee: true }; let op = DispatchInfo { weight: 100, class: DispatchClass::Operational, pays_fee: true }; let len = 0_usize; let priority = CheckWeight::(PhantomData) - .validate(&1, CALL, &normal, len) + .validate(&1, source, CALL, &normal, len) .unwrap() .priority; assert_eq!(priority, 100); let priority = CheckWeight::(PhantomData) - .validate(&1, CALL, &op, len) + .validate(&1, source, CALL, &op, len) .unwrap() .priority; assert_eq!(priority, u64::max_value()); @@ -2019,6 +2080,7 @@ mod tests { #[test] fn signed_ext_check_era_should_change_longevity() { new_test_ext().execute_with(|| { + let source = TransactionSource::External; let normal = DispatchInfo { weight: 100, class: DispatchClass::Normal, pays_fee: true }; let len = 0_usize; let ext = ( @@ -2028,7 +2090,7 @@ mod tests { System::set_block_number(17); >::insert(16, H256::repeat_byte(1)); - assert_eq!(ext.validate(&1, CALL, &normal, len).unwrap().longevity, 15); + assert_eq!(ext.validate(&1, source, CALL, &normal, len).unwrap().longevity, 15); }) } diff --git a/frame/transaction-payment/src/lib.rs b/frame/transaction-payment/src/lib.rs index 7cf364d700f42..80a588cc11d60 100644 --- a/frame/transaction-payment/src/lib.rs +++ b/frame/transaction-payment/src/lib.rs @@ -42,7 +42,7 @@ use sp_runtime::{ Fixed64, transaction_validity::{ TransactionPriority, ValidTransaction, InvalidTransaction, TransactionValidityError, - TransactionValidity, + TransactionValidity, TransactionSource, }, traits::{ Zero, Saturating, SignedExtension, SaturatedConversion, Convert, Dispatchable, @@ -221,6 +221,7 @@ impl SignedExtension for ChargeTransactionPayment whe fn validate( &self, who: &Self::AccountId, + _source: TransactionSource, _call: &Self::Call, info: &DispatchInfoOf, len: usize, @@ -493,6 +494,7 @@ mod tests { assert_eq!(Balances::free_balance(1), 0); let len = 100; + let source = TransactionSource::External; // This is a completely free (and thus wholly insecure/DoS-ridden) transaction. let operational_transaction = DispatchInfo { @@ -502,7 +504,7 @@ mod tests { }; assert!( ChargeTransactionPayment::::from(0) - .validate(&1, CALL, &operational_transaction , len) + .validate(&1, source, CALL, &operational_transaction , len) .is_ok() ); @@ -514,7 +516,7 @@ mod tests { }; assert!( ChargeTransactionPayment::::from(0) - .validate(&1, CALL, &free_transaction , len) + .validate(&1, source, CALL, &free_transaction , len) .is_err() ); }); diff --git a/primitives/runtime/src/generic/checked_extrinsic.rs b/primitives/runtime/src/generic/checked_extrinsic.rs index a329f334c0d77..7da623a31631f 100644 --- a/primitives/runtime/src/generic/checked_extrinsic.rs +++ b/primitives/runtime/src/generic/checked_extrinsic.rs @@ -18,7 +18,7 @@ //! stage. use crate::traits::{ - self, Member, MaybeDisplay, SignedExtension, Dispatchable, DispatchInfoOf, ValidateUnsigned, + self, Member, MaybeDisplay, SignedExtension, Dispatchable, DispatchInfoOf, }; use crate::transaction_validity::{TransactionValidity, TransactionSource}; @@ -45,24 +45,20 @@ where { type Call = Call; - fn validate>( + fn validate( &self, - // TODO [#5006;ToDr] should source be passed to `SignedExtension`s? - // Perhaps a change for 2.0 to avoid breaking too much APIs? source: TransactionSource, info: &DispatchInfoOf, len: usize, ) -> TransactionValidity { if let Some((ref id, ref extra)) = self.signed { - Extra::validate(extra, id, &self.function, info, len) + Extra::validate(extra, id, source, &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)) + Extra::validate_unsigned(source, &self.function, info, len) } } - fn apply>( + fn apply( self, info: &DispatchInfoOf, len: usize, @@ -72,7 +68,6 @@ where (Some(id), pre) } else { let pre = Extra::pre_dispatch_unsigned(&self.function, info, len)?; - U::pre_dispatch(&self.function)?; (None, pre) }; let res = self.function.dispatch(Origin::from(maybe_who)); @@ -85,3 +80,84 @@ where Ok(res) } } + +#[cfg(test)] +mod tests { + use super::*; + use traits::Applyable; + use crate::transaction_validity::{ + TransactionValidityError, InvalidTransaction, ValidTransaction, + }; + + type AccountId = u32; + #[derive(PartialEq, Eq, Clone, Copy, Debug)] + struct Call; + + impl traits::Dispatchable for Call { + type Origin = Option; + type Trait = (); + type PostInfo = (); + type Info = (); + + fn dispatch(self, origin: Self::Origin) -> crate::DispatchResultWithInfo { + origin + .map(|_| ()) + .ok_or_else(|| panic!("Should not dispatch unsigned transactions.")) + } + } + + #[derive(Debug, Eq, Clone, PartialEq, codec::Encode, codec::Decode)] + struct Extra; + impl SignedExtension for Extra { + const IDENTIFIER: &'static str = "test"; + + type AccountId = AccountId; + type Call = Call; + type AdditionalSigned = (); + type Pre = (); + + fn additional_signed(&self) -> Result { + Ok(()) + } + + } + + type Extrinsic = CheckedExtrinsic; + + fn extrinsics() -> (Extrinsic, Extrinsic) { + let unsigned = Extrinsic { + signed: None, + function: Call, + }; + let signed = Extrinsic { + signed: Some((1, (Extra, ))), + function: Call, + }; + (signed, unsigned) + } + + fn is_applyable() {} + + #[test] + fn should_allow_signed_and_forbid_unsigned_during_apply() { + is_applyable::(); + let (signed, unsigned) = extrinsics(); + + let res = signed.apply(&Default::default(), 1); + assert_eq!(res.unwrap().unwrap(), ()); + let res = unsigned.apply(&Default::default(), 1); + assert_eq!(res.unwrap_err(), InvalidTransaction::NoUnsignedValidator.into()); + } + + #[test] + fn should_allow_signed_and_forbid_unsigned_during_validate() { + is_applyable::(); + let (signed, unsigned) = extrinsics(); + let source = TransactionSource::External; + + let res = signed.validate(source, &Default::default(), 1); + assert_eq!(res.unwrap(), ValidTransaction::default()); + let res = unsigned.validate(source, &Default::default(), 1); + assert_eq!(res.unwrap_err(), InvalidTransaction::NoUnsignedValidator.into()); + } +} diff --git a/primitives/runtime/src/generic/unchecked_extrinsic.rs b/primitives/runtime/src/generic/unchecked_extrinsic.rs index 3e9e52ba8beda..991d0a54fa5e5 100644 --- a/primitives/runtime/src/generic/unchecked_extrinsic.rs +++ b/primitives/runtime/src/generic/unchecked_extrinsic.rs @@ -136,7 +136,7 @@ where signed: Some((signed, extra)), function, } - } + }, None => CheckedExtrinsic { signed: None, function: self.function, diff --git a/primitives/runtime/src/testing.rs b/primitives/runtime/src/testing.rs index 1414a5f4f0a75..747c967b91bf8 100644 --- a/primitives/runtime/src/testing.rs +++ b/primitives/runtime/src/testing.rs @@ -23,7 +23,6 @@ use crate::traits::{ self, Checkable, Applyable, BlakeTwo256, OpaqueKeys, SignedExtension, Dispatchable, DispatchInfoOf, }; -use crate::traits::ValidateUnsigned; use crate::{generic, KeyTypeId, ApplyExtrinsicResult}; pub use sp_core::{H256, sr25519}; use sp_core::{crypto::{CryptoType, Dummy, key_types, Public}, U256}; @@ -353,7 +352,7 @@ impl Applyable for TestXt where type Call = Call; /// Checks to see if this is a valid *transaction*. It returns information on it if so. - fn validate>( + fn validate( &self, _source: TransactionSource, _info: &DispatchInfoOf, @@ -364,7 +363,7 @@ impl Applyable for TestXt where /// Executes all necessary logic needed prior to dispatch and deconstructs into function call, /// index and sender. - fn apply>( + fn apply( self, info: &DispatchInfoOf, len: usize, diff --git a/primitives/runtime/src/traits.rs b/primitives/runtime/src/traits.rs index fdf1d6396d26f..9388b4b5f6890 100644 --- a/primitives/runtime/src/traits.rs +++ b/primitives/runtime/src/traits.rs @@ -29,7 +29,7 @@ use sp_core::{self, Hasher, TypeId, RuntimeDebug}; use crate::codec::{Codec, Encode, Decode}; use crate::transaction_validity::{ ValidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, - UnknownTransaction, + UnknownTransaction, InvalidTransaction, }; use crate::generic::{Digest, DigestItem}; pub use sp_arithmetic::traits::{ @@ -689,6 +689,7 @@ pub trait SignedExtension: Codec + Debug + Sync + Send + Clone + Eq + PartialEq fn validate( &self, _who: &Self::AccountId, + _source: TransactionSource, _call: &Self::Call, _info: &DispatchInfoOf, _len: usize, @@ -711,7 +712,7 @@ pub trait SignedExtension: Codec + Debug + Sync + Send + Clone + Eq + PartialEq info: &DispatchInfoOf, len: usize, ) -> Result { - self.validate(who, call, info.clone(), len) + self.validate(who, TransactionSource::InBlock, call, info.clone(), len) .map(|_| Self::Pre::default()) .map_err(Into::into) } @@ -724,7 +725,13 @@ pub trait SignedExtension: Codec + Debug + Sync + Send + Clone + Eq + PartialEq /// and quickly eliminate ones that are stale or incorrect. /// /// Make sure to perform the same checks in `pre_dispatch_unsigned` function. + /// + /// Returning a `Err(InvalidTransaction::NoUnsignedValidator)` indicates that + /// this particular extension is not interested in the call, but if multiple + /// `SignedExtensions` are composed, it allows others to handle and potentially + /// whitelist the call. fn validate_unsigned( + _source: TransactionSource, _call: &Self::Call, _info: &DispatchInfoOf, _len: usize, @@ -740,13 +747,22 @@ pub trait SignedExtension: Codec + Debug + Sync + Send + Clone + Eq + PartialEq /// /// If you ever override this function, you need to make sure to always /// perform the same validation as in `validate_unsigned`. + /// + /// Returning a `Err(InvalidTransaction::NoUnsignedValidator)` indicates that + /// this particular extension is not interested in the call, but if multiple + /// `SignedExtensions` are composed, it allows others to handle and potentially + /// whitelist the call. fn pre_dispatch_unsigned( call: &Self::Call, info: &DispatchInfoOf, len: usize, ) -> Result { - Self::validate_unsigned(call, info.clone(), len) - .map(|_| Self::Pre::default()) + Self::validate_unsigned(TransactionSource::InBlock, call, info.clone(), len) + .and_then(|res| if res.has_provides_tags() { + Ok(Self::Pre::default()) + } else { + Err(InvalidTransaction::NoUnsignedValidator.into()) + }) .map_err(Into::into) } @@ -801,29 +817,48 @@ impl SignedExtension for Tuple { fn validate( &self, who: &Self::AccountId, + source: TransactionSource, call: &Self::Call, info: &DispatchInfoOf, len: usize, ) -> TransactionValidity { let valid = ValidTransaction::default(); - for_tuples!( #( let valid = valid.combine_with(Tuple.validate(who, call, info, len)?); )* ); + for_tuples!( #( + let valid = valid.combine_with(Tuple.validate(who, source, call, info, len)?); + )* ); Ok(valid) } fn pre_dispatch(self, who: &Self::AccountId, call: &Self::Call, info: &DispatchInfoOf, len: usize) -> Result { - Ok(for_tuples!( ( #( Tuple.pre_dispatch(who, call, info, len)? ),* ) )) + Ok(for_tuples!(( + #( Tuple.pre_dispatch(who, call, info, len)?),* + ))) } fn validate_unsigned( + source: TransactionSource, call: &Self::Call, info: &DispatchInfoOf, len: usize, ) -> TransactionValidity { let valid = ValidTransaction::default(); - for_tuples!( #( let valid = valid.combine_with(Tuple::validate_unsigned(call, info, len)?); )* ); - Ok(valid) + for_tuples!( #( + let valid = valid.combine_with( + match Tuple::validate_unsigned(source, call, info, len) { + Ok(valid) => valid, + Err(e) if e.missing_unsigned_validator() => Default::default(), + Err(e) => return Err(e), + } + ); + )* ); + + if valid.has_provides_tags() { + Ok(valid) + } else { + Err(InvalidTransaction::NoUnsignedValidator.into()) + } } fn pre_dispatch_unsigned( @@ -831,7 +866,23 @@ impl SignedExtension for Tuple { info: &DispatchInfoOf, len: usize, ) -> Result { - Ok(for_tuples!( ( #( Tuple::pre_dispatch_unsigned(call, info, len)? ),* ) )) + let mut is_whitelisted = false; + let tuple = for_tuples!(( #( + match Tuple::pre_dispatch_unsigned(call, info, len) { + Ok(pre) => { + is_whitelisted = true; + pre + }, + Err(e) if e.missing_unsigned_validator() => Default::default(), + Err(e) => return Err(e), + } + ),* )); + + if is_whitelisted { + Ok(tuple) + } else { + Err(InvalidTransaction::NoUnsignedValidator.into()) + } } fn post_dispatch( @@ -874,7 +925,7 @@ pub trait Applyable: Sized + Send + Sync { type Call: Dispatchable; /// Checks to see if this is a valid *transaction*. It returns information on it if so. - fn validate>( + fn validate( &self, source: TransactionSource, info: &DispatchInfoOf, @@ -883,7 +934,7 @@ pub trait Applyable: Sized + Send + Sync { /// Executes all necessary logic needed prior to dispatch and deconstructs into function call, /// index and sender. - fn apply>( + fn apply( self, info: &DispatchInfoOf, len: usize, @@ -1238,6 +1289,12 @@ impl Printable for &T { } } +impl Printable for () { + fn print(&self) { + "()".print() + } +} + impl Printable for u8 { fn print(&self) { (*self as u64).print() @@ -1284,12 +1341,6 @@ impl Printable for bool { } } -impl Printable for () { - fn print(&self) { - "()".print() - } -} - #[impl_for_tuples(1, 12)] impl Printable for Tuple { fn print(&self) { diff --git a/primitives/runtime/src/transaction_validity.rs b/primitives/runtime/src/transaction_validity.rs index 95903b4876262..1edba08e5ced7 100644 --- a/primitives/runtime/src/transaction_validity.rs +++ b/primitives/runtime/src/transaction_validity.rs @@ -59,6 +59,17 @@ pub enum InvalidTransaction { /// A transaction with a mandatory dispatch. This is invalid; only inherent extrinsics are /// allowed to have mandatory dispatches. MandatoryDispatch, + /// None of the unsigned validators whitelisted this call. + /// + /// Unsigned transactions are considered invalid by default. Some `UnsignedValidator` + /// or `SignedExtension` needs to whitelist them first. This error usually means that either you + /// are either missing a `UnsignedValidator` in a pallet or you forgot to include + /// a `SignedExtension` to extrinsic type. + /// + /// Returning this error in `SignedExtension::pre_dispatch_unsigned` does not short-circuit + /// the validation pipeline. This means that other `SignedExtensions` are allowed to validate the call + /// as well. Only if all of them return this error we fail. + NoUnsignedValidator, } impl InvalidTransaction { @@ -77,6 +88,14 @@ impl InvalidTransaction { _ => false, } } + + /// Returns if the reason for the invalidity was missing unsigned validator. + pub fn missing_unsigned_validator(&self) -> bool { + match self { + Self::NoUnsignedValidator => true, + _ => false, + } + } } impl From for &'static str { @@ -96,34 +115,46 @@ impl From for &'static str { InvalidTransaction::MandatoryDispatch => "Tranaction dispatch is mandatory; transactions may not have mandatory dispatches.", InvalidTransaction::Custom(_) => "InvalidTransaction custom error", + InvalidTransaction::NoUnsignedValidator => + "Missing SignedExtension or UnsignedValidator to whitelist unsigned transaction.", } } } -/// An unknown transaction validity. -#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, RuntimeDebug)] -#[cfg_attr(feature = "std", derive(serde::Serialize))] -pub enum UnknownTransaction { - /// Could not lookup some information that is required to validate the transaction. - CannotLookup, - /// No validator found for the given unsigned transaction. - NoUnsignedValidator, - /// Any other custom unknown validity that is not covered by this enum. - Custom(u8), -} +#[allow(deprecated)] // NoUnsignedValidator in generated code +mod unknown { + use super::*; + + /// An unknown transaction validity. + #[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, RuntimeDebug)] + #[cfg_attr(feature = "std", derive(serde::Serialize))] + pub enum UnknownTransaction { + /// Could not lookup some information that is required to validate the transaction. + CannotLookup, + /// No validator found for the given unsigned transaction. + /// + /// Replaced by `InvalidTransaction::NoUnsignedValidator`. + #[deprecated] + NoUnsignedValidator, + /// Any other custom unknown validity that is not covered by this enum. + Custom(u8), + } -impl From for &'static str { - fn from(unknown: UnknownTransaction) -> &'static str { - match unknown { - UnknownTransaction::CannotLookup => - "Could not lookup information required to validate the transaction", - UnknownTransaction::NoUnsignedValidator => - "Could not find an unsigned validator for the unsigned transaction", - UnknownTransaction::Custom(_) => "UnknownTransaction custom error", + impl From for &'static str { + fn from(unknown: UnknownTransaction) -> &'static str { + match unknown { + UnknownTransaction::CannotLookup => + "Could not lookup information required to validate the transaction", + UnknownTransaction::NoUnsignedValidator => + "Could not find an unsigned validator for the unsigned transaction", + UnknownTransaction::Custom(_) => "UnknownTransaction custom error", + } } } } +pub use self::unknown::UnknownTransaction; + /// Errors that can occur while checking the validity of a transaction. #[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, RuntimeDebug)] #[cfg_attr(feature = "std", derive(serde::Serialize))] @@ -143,6 +174,15 @@ impl TransactionValidityError { } } + /// Returns `true` if the reason for the error was it being a unsigned transaction that was + /// not explicitly whitelisted by the callee. + pub fn missing_unsigned_validator(&self) -> bool { + match self { + Self::Invalid(e) => e.missing_unsigned_validator(), + Self::Unknown(_) => false, + } + } + /// Returns `true` if the reason for the error was it being a mandatory dispatch that could not /// be completed successfully. pub fn was_mandatory(&self) -> bool { @@ -287,6 +327,19 @@ impl ValidTransaction { propagate: self.propagate && other.propagate, } } + + /// Does this validity contain any `provides` tags? + /// + /// Transactions with no `provides` tags are considered invalid, but we let + /// `ValidTransaction` to be created in that way, to allow composition + /// with results from `SignedExtension`s that are not able to fully validate the + /// transaction, but would like to augment the result in case it's validated somewhere down + /// the pipeline. + /// Transactions with no tags should be rejected from the transaction pool and, what's more + /// important, should never be allowed in a block (i.e. the `pre_dispatch` should fail). + pub fn has_provides_tags(&self) -> bool { + !self.provides.is_empty() + } } /// `ValidTransaction` builder.