diff --git a/substrate/bin/node-template/runtime/src/lib.rs b/substrate/bin/node-template/runtime/src/lib.rs index a9944199b8209d34f2db5cd567960d06cdeb3258..a1bcd157ad63a7e677a92ea4f2cdcc4c444ce040 100644 --- a/substrate/bin/node-template/runtime/src/lib.rs +++ b/substrate/bin/node-template/runtime/src/lib.rs @@ -315,6 +315,10 @@ impl_runtime_apis! { Executive::apply_extrinsic(extrinsic) } + fn apply_trusted_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_trusted_extrinsic(extrinsic) + } + fn finalize_block() -> <Block as BlockT>::Header { Executive::finalize_block() } diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index a98b1700fe1aa15c9aa10dbf326f91ebd26fa072..956fc0bb0eb5f7da2bb267c3d413b784501172a9 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -82,7 +82,7 @@ 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: 221, + spec_version: 222, impl_version: 0, apis: RUNTIME_API_VERSIONS, }; @@ -686,6 +686,10 @@ impl_runtime_apis! { Executive::apply_extrinsic(extrinsic) } + fn apply_trusted_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_trusted_extrinsic(extrinsic) + } + fn finalize_block() -> <Block as BlockT>::Header { Executive::finalize_block() } diff --git a/substrate/client/basic-authorship/src/basic_authorship.rs b/substrate/client/basic-authorship/src/basic_authorship.rs index cab1231e870685121f964e768cfd4c2107f4ca8f..a99453544e5f63e3a6a6e5d62845b465d4bc86d8 100644 --- a/substrate/client/basic-authorship/src/basic_authorship.rs +++ b/substrate/client/basic-authorship/src/basic_authorship.rs @@ -195,7 +195,7 @@ impl<Block, B, E, RA, A> ProposerInner<Block, SubstrateClient<B, E, Block, RA>, inherent_data )? { - block_builder.push(extrinsic)?; + block_builder.push_trusted(extrinsic)?; } // proceed with transactions @@ -218,7 +218,7 @@ impl<Block, B, E, RA, A> ProposerInner<Block, SubstrateClient<B, E, Block, RA>, let pending_tx_data = pending_tx.data().clone(); let pending_tx_hash = pending_tx.hash().clone(); trace!("[{:?}] Pushing to the block.", pending_tx_hash); - match sc_block_builder::BlockBuilder::push(&mut block_builder, pending_tx_data) { + match sc_block_builder::BlockBuilder::push_trusted(&mut block_builder, pending_tx_data) { Ok(()) => { debug!("[{:?}] Pushed to the block.", pending_tx_hash); } diff --git a/substrate/client/block-builder/src/lib.rs b/substrate/client/block-builder/src/lib.rs index d0eb8b2892630f177e6cce45e9def226643b077a..26bc9ecea8deac6ecd5f3988878555183cc26aa7 100644 --- a/substrate/client/block-builder/src/lib.rs +++ b/substrate/client/block-builder/src/lib.rs @@ -121,11 +121,23 @@ where backend, }) } - + /// Push onto the block's list of extrinsics. /// - /// This will ensure the extrinsic can be validly executed (by executing it); + /// This will ensure the extrinsic can be validly executed (by executing it). pub fn push(&mut self, xt: <Block as BlockT>::Extrinsic) -> Result<(), ApiErrorFor<A, Block>> { + self.push_internal(xt, false) + } + + /// Push onto the block's list of extrinsics. + /// + /// This will treat incoming extrinsic `xt` as untrusted and perform additional checks + /// (currenty checking signature). + pub fn push_trusted(&mut self, xt: <Block as BlockT>::Extrinsic) -> Result<(), ApiErrorFor<A, Block>> { + self.push_internal(xt, true) + } + + fn push_internal(&mut self, xt: <Block as BlockT>::Extrinsic, skip_signature: bool) -> Result<(), ApiErrorFor<A, Block>> { let block_id = &self.block_id; let extrinsics = &mut self.extrinsics; @@ -152,12 +164,29 @@ where } }) } else { - self.api.map_api_result(|api| { - match api.apply_extrinsic_with_context( + let use_trusted = skip_signature && self + .api + .has_api_with::<dyn BlockBuilderApi<Block, Error = ApiErrorFor<A, Block>>, _>( block_id, - ExecutionContext::BlockConstruction, - xt.clone(), - )? { + |version| version >= 5, + )?; + + self.api.map_api_result(|api| { + let apply_result = if use_trusted { + api.apply_trusted_extrinsic_with_context( + block_id, + ExecutionContext::BlockConstruction, + xt.clone(), + )? + } else { + api.apply_extrinsic_with_context( + block_id, + ExecutionContext::BlockConstruction, + xt.clone(), + )? + }; + + match apply_result { Ok(_) => { extrinsics.push(xt); Ok(()) diff --git a/substrate/client/rpc/src/state/tests.rs b/substrate/client/rpc/src/state/tests.rs index a0ab11e9772045a78ec801892257947367ee8f2d..6508d46ddde65c2be0960364122d0e2810576a58 100644 --- a/substrate/client/rpc/src/state/tests.rs +++ b/substrate/client/rpc/src/state/tests.rs @@ -402,7 +402,7 @@ fn should_return_runtime_version() { let result = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":1,\ \"specVersion\":1,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",2],\ - [\"0x37e397fc7c91f5e4\",1],[\"0xd2bc9897eed08f15\",1],[\"0x40fe3ad401f8959a\",4],\ + [\"0x37e397fc7c91f5e4\",1],[\"0xd2bc9897eed08f15\",1],[\"0x40fe3ad401f8959a\",5],\ [\"0xc6e9a76309f39b09\",1],[\"0xdd718d5cc53262d4\",1],[\"0xcbca25e39f142387\",1],\ [\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],[\"0xbc9d89904f5b923f\",1]]}"; diff --git a/substrate/client/service/src/lib.rs b/substrate/client/service/src/lib.rs index c358e5993e369424f31db7dd69ed77a7e1a094f4..a2e53616aba94ad39d7c096e4b67fe5ad4f90fc7 100644 --- a/substrate/client/service/src/lib.rs +++ b/substrate/client/service/src/lib.rs @@ -705,6 +705,7 @@ mod tests { use futures::executor::block_on; use sp_consensus::SelectChain; use sp_runtime::traits::BlindCheckable; + use sp_runtime::generic::CheckSignature; use substrate_test_runtime_client::{prelude::*, runtime::{Extrinsic, Transfer}}; use sc_transaction_pool::{BasicPool, FullChainApi}; @@ -733,7 +734,7 @@ mod tests { // then assert_eq!(transactions.len(), 1); - assert!(transactions[0].1.clone().check().is_ok()); + assert!(transactions[0].1.clone().check(CheckSignature::Yes).is_ok()); // this should not panic let _ = transactions[0].1.transfer(); } diff --git a/substrate/frame/executive/src/lib.rs b/substrate/frame/executive/src/lib.rs index 2d1b306531ba1534188637b993e17fbb1a519c90..c112298051fde41787ee6b55ce16d480e28cd763 100644 --- a/substrate/frame/executive/src/lib.rs +++ b/substrate/frame/executive/src/lib.rs @@ -79,13 +79,15 @@ use sp_std::{prelude::*, marker::PhantomData}; use frame_support::weights::{GetDispatchInfo, WeighBlock, DispatchInfo}; use sp_runtime::{ - generic::Digest, ApplyExtrinsicResult, + generic::Digest, + ApplyExtrinsicResult, traits::{ self, Header, Zero, One, Checkable, Applyable, CheckEqual, OnFinalize, OnInitialize, NumberFor, Block as BlockT, OffchainWorker, Dispatchable, Saturating, }, transaction_validity::TransactionValidity, }; +use sp_runtime::generic::CheckSignature; #[allow(deprecated)] use sp_runtime::traits::ValidateUnsigned; use codec::{Codec, Encode}; @@ -255,13 +257,22 @@ where pub fn apply_extrinsic(uxt: Block::Extrinsic) -> ApplyExtrinsicResult { let encoded = uxt.encode(); let encoded_len = encoded.len(); - Self::apply_extrinsic_with_len(uxt, encoded_len, Some(encoded)) + Self::apply_extrinsic_with_len(uxt, encoded_len, Some(encoded), CheckSignature::Yes) + } + + /// Apply extrinsic outside of the block execution function. + /// + /// Same as `apply_extrinsic`, but skips signature checks. + pub fn apply_trusted_extrinsic(uxt: Block::Extrinsic) -> ApplyExtrinsicResult { + let encoded = uxt.encode(); + let encoded_len = encoded.len(); + Self::apply_extrinsic_with_len(uxt, encoded_len, Some(encoded), CheckSignature::No) } /// Apply an extrinsic inside the block execution function. fn apply_extrinsic_no_note(uxt: Block::Extrinsic) { let l = uxt.encode().len(); - match Self::apply_extrinsic_with_len(uxt, l, None) { + match Self::apply_extrinsic_with_len(uxt, l, None, CheckSignature::Yes) { Ok(_) => (), Err(e) => { let err: &'static str = e.into(); panic!(err) }, } @@ -272,9 +283,13 @@ where uxt: Block::Extrinsic, encoded_len: usize, to_note: Option<Vec<u8>>, + check_signature: CheckSignature, ) -> ApplyExtrinsicResult { // Verify that the signature is good. - let xt = uxt.check(&Default::default())?; + let xt = uxt.check( + check_signature, + &Default::default(), + )?; // We don't need to make sure to `note_extrinsic` only after we know it's going to be // executed to prevent it from leaking in storage since at this point, it will either @@ -322,7 +337,7 @@ where /// Changes made to storage should be discarded. pub fn validate_transaction(uxt: Block::Extrinsic) -> TransactionValidity { let encoded_len = uxt.using_encoded(|d| d.len()); - let xt = uxt.check(&Default::default())?; + let xt = uxt.check(CheckSignature::Yes, &Default::default())?; let dispatch_info = xt.get_dispatch_info(); xt.validate::<UnsignedValidator>(dispatch_info, encoded_len) @@ -516,8 +531,8 @@ mod tests { ) } - fn sign_extra(who: u64, nonce: u64, fee: u64) -> Option<(u64, SignedExtra)> { - Some((who, extra(nonce, fee))) + fn sign_extra(who: u64, nonce: u64, fee: u64) -> (u64, SignedExtra) { + (who, extra(nonce, fee)) } #[test] @@ -526,7 +541,7 @@ mod tests { pallet_balances::GenesisConfig::<Runtime> { balances: vec![(1, 211)], }.assimilate_storage(&mut t).unwrap(); - let xt = sp_runtime::testing::TestXt(sign_extra(1, 0, 0), Call::Balances(BalancesCall::transfer(2, 69))); + let xt = sp_runtime::testing::TestXt::new_signed(sign_extra(1, 0, 0), Call::Balances(BalancesCall::transfer(2, 69))); let weight = xt.get_dispatch_info().weight as u64; let mut t = sp_io::TestExternalities::new(t); t.execute_with(|| { @@ -606,7 +621,7 @@ mod tests { fn bad_extrinsic_not_inserted() { let mut t = new_test_ext(1); // bad nonce check! - let xt = sp_runtime::testing::TestXt(sign_extra(1, 30, 0), Call::Balances(BalancesCall::transfer(33, 69))); + let xt = sp_runtime::testing::TestXt::new_signed(sign_extra(1, 30, 0), Call::Balances(BalancesCall::transfer(33, 69))); t.execute_with(|| { Executive::initialize_block(&Header::new( 1, @@ -624,7 +639,7 @@ mod tests { fn block_weight_limit_enforced() { let mut t = new_test_ext(10000); // given: TestXt uses the encoded len as fixed Len: - let xt = sp_runtime::testing::TestXt(sign_extra(1, 0, 0), Call::Balances(BalancesCall::transfer(33, 0))); + let xt = sp_runtime::testing::TestXt::new_signed(sign_extra(1, 0, 0), Call::Balances(BalancesCall::transfer(33, 0))); let encoded = xt.encode(); let encoded_len = encoded.len() as Weight; let limit = AvailableBlockRatio::get() * MaximumBlockWeight::get() - 175; @@ -641,7 +656,7 @@ mod tests { assert_eq!(<frame_system::Module<Runtime>>::all_extrinsics_weight(), 175); for nonce in 0..=num_to_exhaust_block { - let xt = sp_runtime::testing::TestXt( + let xt = sp_runtime::testing::TestXt::new_signed( sign_extra(1, nonce.into(), 0), Call::Balances(BalancesCall::transfer(33, 0)), ); let res = Executive::apply_extrinsic(xt); @@ -661,9 +676,9 @@ mod tests { #[test] fn block_weight_and_size_is_stored_per_tx() { - let xt = sp_runtime::testing::TestXt(sign_extra(1, 0, 0), Call::Balances(BalancesCall::transfer(33, 0))); - let x1 = sp_runtime::testing::TestXt(sign_extra(1, 1, 0), Call::Balances(BalancesCall::transfer(33, 0))); - let x2 = sp_runtime::testing::TestXt(sign_extra(1, 2, 0), Call::Balances(BalancesCall::transfer(33, 0))); + let xt = sp_runtime::testing::TestXt::new_signed(sign_extra(1, 0, 0), Call::Balances(BalancesCall::transfer(33, 0))); + let x1 = sp_runtime::testing::TestXt::new_signed(sign_extra(1, 1, 0), Call::Balances(BalancesCall::transfer(33, 0))); + let x2 = sp_runtime::testing::TestXt::new_signed(sign_extra(1, 2, 0), Call::Balances(BalancesCall::transfer(33, 0))); let len = xt.clone().encode().len() as u32; let mut t = new_test_ext(1); t.execute_with(|| { @@ -687,7 +702,7 @@ mod tests { #[test] fn validate_unsigned() { - let xt = sp_runtime::testing::TestXt(None, Call::Balances(BalancesCall::set_balance(33, 69, 69))); + let xt = sp_runtime::testing::TestXt::new_unsigned(Call::Balances(BalancesCall::set_balance(33, 69, 69))); let mut t = new_test_ext(1); t.execute_with(|| { @@ -696,6 +711,28 @@ mod tests { }); } + #[test] + fn apply_trusted_skips_signature_check_but_not_others() { + let xt1 = sp_runtime::testing::TestXt::new_signed(sign_extra(1, 0, 0), Call::Balances(BalancesCall::transfer(33, 0))) + .badly_signed(); + + let mut t = new_test_ext(1); + + t.execute_with(|| { + assert_eq!(Executive::apply_trusted_extrinsic(xt1), Ok(Ok(()))); + }); + + let xt2 = sp_runtime::testing::TestXt::new_signed(sign_extra(1, 0, 0), Call::Balances(BalancesCall::transfer(33, 0))) + .invalid(TransactionValidityError::Invalid(InvalidTransaction::Call)); + + t.execute_with(|| { + assert_eq!( + Executive::apply_trusted_extrinsic(xt2), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) + ); + }); + } + #[test] fn can_pay_for_tx_fee_on_full_lock() { let id: LockIdentifier = *b"0 "; @@ -708,7 +745,7 @@ mod tests { 110, lock, ); - let xt = sp_runtime::testing::TestXt( + let xt = sp_runtime::testing::TestXt::new_signed( sign_extra(1, 0, 0), Call::System(SystemCall::remark(vec![1u8])), ); diff --git a/substrate/frame/im-online/src/tests.rs b/substrate/frame/im-online/src/tests.rs index 808978d4036e8c1919d1b9680314c547fabba38d..80056023e505c48fdc066cfdcc47559d84bab257 100644 --- a/substrate/frame/im-online/src/tests.rs +++ b/substrate/frame/im-online/src/tests.rs @@ -222,7 +222,7 @@ fn should_generate_heartbeats() { assert_eq!(state.read().transactions.len(), 2); // check stuff about the transaction. let ex: Extrinsic = Decode::decode(&mut &*transaction).unwrap(); - let heartbeat = match ex.1 { + let heartbeat = match ex.call { crate::mock::Call::ImOnline(crate::Call::heartbeat(h, _)) => h, e => panic!("Unexpected call: {:?}", e), }; @@ -332,7 +332,7 @@ fn should_not_send_a_report_if_already_online() { assert_eq!(pool_state.read().transactions.len(), 0); // check stuff about the transaction. let ex: Extrinsic = Decode::decode(&mut &*transaction).unwrap(); - let heartbeat = match ex.1 { + let heartbeat = match ex.call { crate::mock::Call::ImOnline(crate::Call::heartbeat(h, _)) => h, e => panic!("Unexpected call: {:?}", e), }; diff --git a/substrate/primitives/block-builder/src/lib.rs b/substrate/primitives/block-builder/src/lib.rs index f050979bd8ecb8ed42ff55da882c14faf30883d9..e4e98e1f40cbf4979f6c04113e520fcb308ed96f 100644 --- a/substrate/primitives/block-builder/src/lib.rs +++ b/substrate/primitives/block-builder/src/lib.rs @@ -43,7 +43,7 @@ pub mod compatibility_v3 { sp_api::decl_runtime_apis! { /// The `BlockBuilder` api trait that provides the required functionality for building a block. - #[api_version(4)] + #[api_version(5)] pub trait BlockBuilder { /// Compatibility version of `apply_extrinsic` for v3. /// @@ -58,6 +58,10 @@ sp_api::decl_runtime_apis! { /// Returns an inclusion outcome which specifies if this extrinsic is included in /// this block or not. fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult; + /// Apply the given extrinsic. + /// + /// Same as `apply_extrinsic`, but skips signature verification. + fn apply_trusted_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult; /// Finish the current block. #[renamed("finalise_block", 3)] fn finalize_block() -> <Block as BlockT>::Header; diff --git a/substrate/primitives/runtime/src/generic/mod.rs b/substrate/primitives/runtime/src/generic/mod.rs index 5e9928ba1909ab2aa114babaadcc1cc774131e60..f6399fff1382ade73804f5b105625b82dc379ec7 100644 --- a/substrate/primitives/runtime/src/generic/mod.rs +++ b/substrate/primitives/runtime/src/generic/mod.rs @@ -39,6 +39,15 @@ pub use self::digest::{ use crate::codec::Encode; use sp_std::prelude::*; +/// Perform singature check. +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum CheckSignature { + /// Perform. + Yes, + /// Don't perform. + No, +} + fn encode_with_vec_prefix<T: Encode, F: Fn(&mut Vec<u8>)>(encoder: F) -> Vec<u8> { let size = ::sp_std::mem::size_of::<T>(); let reserve = match size { diff --git a/substrate/primitives/runtime/src/generic/unchecked_extrinsic.rs b/substrate/primitives/runtime/src/generic/unchecked_extrinsic.rs index a516bc1f7fa999f1087718f93f072ff8eff45099..0db60e32a6e5e3684eaebd915a9949b4840fa45d 100644 --- a/substrate/primitives/runtime/src/generic/unchecked_extrinsic.rs +++ b/substrate/primitives/runtime/src/generic/unchecked_extrinsic.rs @@ -24,7 +24,8 @@ use crate::{ self, Member, MaybeDisplay, SignedExtension, Checkable, Extrinsic, ExtrinsicMetadata, IdentifyAccount, }, - generic::CheckedExtrinsic, transaction_validity::{TransactionValidityError, InvalidTransaction}, + generic::{CheckSignature, CheckedExtrinsic}, + transaction_validity::{TransactionValidityError, InvalidTransaction}, }; const TRANSACTION_VERSION: u8 = 4; @@ -120,18 +121,26 @@ where { type Checked = CheckedExtrinsic<AccountId, Call, Extra>; - fn check(self, lookup: &Lookup) -> Result<Self::Checked, TransactionValidityError> { + fn check(self, check_signature: CheckSignature, lookup: &Lookup) -> Result<Self::Checked, TransactionValidityError> { Ok(match self.signature { Some((signed, signature, extra)) => { let signed = lookup.lookup(signed)?; - let raw_payload = SignedPayload::new(self.function, extra)?; - if !raw_payload.using_encoded(|payload| { - signature.verify(payload, &signed) - }) { - return Err(InvalidTransaction::BadProof.into()) - } - let (function, extra, _) = raw_payload.deconstruct(); + let (function, extra) = if let CheckSignature::No = check_signature { + (self.function, extra) + } else { + let raw_payload = SignedPayload::new(self.function, extra)?; + + if !raw_payload.using_encoded(|payload| { + signature.verify(payload, &signed) + }) { + return Err(InvalidTransaction::BadProof.into()) + } + let (function, extra, _) = raw_payload.deconstruct(); + + (function, extra) + }; + CheckedExtrinsic { signed: Some((signed, extra)), function, @@ -322,6 +331,7 @@ mod tests { use sp_io::hashing::blake2_256; use crate::codec::{Encode, Decode}; use crate::traits::{SignedExtension, IdentifyAccount, IdentityLookup}; + use crate::generic::CheckSignature; use serde::{Serialize, Deserialize}; type TestContext = IdentityLookup<u64>; @@ -402,7 +412,7 @@ mod tests { fn unsigned_check_should_work() { let ux = Ex::new_unsigned(vec![0u8; 0]); assert!(!ux.is_signed().unwrap_or(false)); - assert!(<Ex as Checkable<TestContext>>::check(ux, &Default::default()).is_ok()); + assert!(<Ex as Checkable<TestContext>>::check(ux, CheckSignature::Yes, &Default::default()).is_ok()); } #[test] @@ -415,7 +425,7 @@ mod tests { ); assert!(ux.is_signed().unwrap_or(false)); assert_eq!( - <Ex as Checkable<TestContext>>::check(ux, &Default::default()), + <Ex as Checkable<TestContext>>::check(ux, CheckSignature::Yes, &Default::default()), Err(InvalidTransaction::BadProof.into()), ); } @@ -430,7 +440,7 @@ mod tests { ); assert!(ux.is_signed().unwrap_or(false)); assert_eq!( - <Ex as Checkable<TestContext>>::check(ux, &Default::default()), + <Ex as Checkable<TestContext>>::check(ux, CheckSignature::Yes, &Default::default()), Ok(CEx { signed: Some((TEST_ACCOUNT, TestExtra)), function: vec![0u8; 0] }), ); } diff --git a/substrate/primitives/runtime/src/testing.rs b/substrate/primitives/runtime/src/testing.rs index e3e94c3c9f08999ca4034618cda6edcb697f453c..be0e36b2d1a6b76ce7ec61382856c70b6fddffba 100644 --- a/substrate/primitives/runtime/src/testing.rs +++ b/substrate/primitives/runtime/src/testing.rs @@ -25,11 +25,10 @@ use crate::traits::{ }; #[allow(deprecated)] use crate::traits::ValidateUnsigned; -use crate::{generic, KeyTypeId, ApplyExtrinsicResult}; +use crate::{generic::{self, CheckSignature}, KeyTypeId, ApplyExtrinsicResult}; pub use sp_core::{H256, sr25519}; use sp_core::{crypto::{CryptoType, Dummy, key_types, Public}, U256}; -use crate::transaction_validity::{TransactionValidity, TransactionValidityError}; - +use crate::transaction_validity::{TransactionValidity, TransactionValidityError, InvalidTransaction}; /// Authority Id #[derive(Default, PartialEq, Eq, Clone, Encode, Decode, Debug, Hash, Serialize, Deserialize, PartialOrd, Ord)] pub struct UintAuthorityId(pub u64); @@ -295,12 +294,69 @@ impl<'a, Xt> Deserialize<'a> for Block<Xt> where Block<Xt>: Decode { } } -/// Test transaction, tuple of (sender, call, signed_extra) -/// with index only used if sender is some. +/// Test validity. +#[derive(PartialEq, Eq, Clone, Encode, Decode)] +pub enum TestValidity { + /// Valid variant that will pass all checks. + Valid, + /// Variant with invalid signature. + /// + /// Will fail signature check. + SignatureInvalid(TransactionValidityError), + /// Variant with invalid logic. + /// + /// Will fail all checks. + OtherInvalid(TransactionValidityError), +} + +/// Test transaction. /// -/// If sender is some then the transaction is signed otherwise it is unsigned. +/// Used to mock actual transaction. #[derive(PartialEq, Eq, Clone, Encode, Decode)] -pub struct TestXt<Call, Extra>(pub Option<(u64, Extra)>, pub Call); +pub struct TestXt<Call, Extra> { + /// Signature with extra. + /// + /// if some, then the transaction is signed. Transaction is unsigned otherwise. + pub signature: Option<(u64, Extra)>, + /// Validity. + /// + /// Instantiate invalid variant and transaction will fail correpsonding checks. + pub validity: TestValidity, + /// Call. + pub call: Call, +} + +impl<Call, Extra> TestXt<Call, Extra> { + /// New signed test `TextXt`. + pub fn new_signed(signature: (u64, Extra), call: Call) -> Self { + TestXt { + signature: Some(signature), + validity: TestValidity::Valid, + call, + } + } + + /// New unsigned test `TextXt`. + pub fn new_unsigned(call: Call) -> Self { + TestXt { + signature: None, + validity: TestValidity::Valid, + call, + } + } + + /// Build invalid variant of `TestXt`. + pub fn invalid(mut self, err: TransactionValidityError) -> Self { + self.validity = TestValidity::OtherInvalid(err); + self + } + + /// Build badly signed variant of `TestXt`. + pub fn badly_signed(mut self) -> Self { + self.validity = TestValidity::SignatureInvalid(TransactionValidityError::Invalid(InvalidTransaction::BadProof)); + self + } + } // Non-opaque extrinsics always 0. parity_util_mem::malloc_size_of_is_0!(any: TestXt<Call, Extra>); @@ -313,24 +369,39 @@ impl<Call, Extra> Serialize for TestXt<Call, Extra> where TestXt<Call, Extra>: E impl<Call, Extra> Debug for TestXt<Call, Extra> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "TestXt({:?}, ...)", self.0.as_ref().map(|x| &x.0)) + write!(f, "TestXt({:?}, {}, ...)", + self.signature.as_ref().map(|x| &x.0), + if let TestValidity::Valid = self.validity { "valid" } else { "invalid" } + ) } } impl<Call: Codec + Sync + Send, Context, Extra> Checkable<Context> for TestXt<Call, Extra> { type Checked = Self; - fn check(self, _: &Context) -> Result<Self::Checked, TransactionValidityError> { Ok(self) } + fn check(self, signature: CheckSignature, _: &Context) -> Result<Self::Checked, TransactionValidityError> { + match self.validity { + TestValidity::Valid => Ok(self), + TestValidity::SignatureInvalid(e) => + if let CheckSignature::No = signature { + Ok(self) + } else { + Err(e) + }, + TestValidity::OtherInvalid(e) => Err(e), + } + } } + impl<Call: Codec + Sync + Send, Extra> traits::Extrinsic for TestXt<Call, Extra> { type Call = Call; type SignaturePayload = (u64, Extra); fn is_signed(&self) -> Option<bool> { - Some(self.0.is_some()) + Some(self.signature.is_some()) } - fn new(c: Call, sig: Option<Self::SignaturePayload>) -> Option<Self> { - Some(TestXt(sig, c)) + fn new(call: Call, signature: Option<Self::SignaturePayload>) -> Option<Self> { + Some(TestXt { signature, call, validity: TestValidity::Valid }) } } @@ -344,7 +415,7 @@ impl<Origin, Call, Extra, Info> Applyable for TestXt<Call, Extra> where type Call = Call; type DispatchInfo = Info; - fn sender(&self) -> Option<&Self::AccountId> { self.0.as_ref().map(|x| &x.0) } + fn sender(&self) -> Option<&Self::AccountId> { self.signature.as_ref().map(|x| &x.0) } /// Checks to see if this is a valid *transaction*. It returns information on it if so. #[allow(deprecated)] // Allow ValidateUnsigned @@ -364,14 +435,14 @@ impl<Origin, Call, Extra, Info> Applyable for TestXt<Call, Extra> where info: Self::DispatchInfo, len: usize, ) -> ApplyExtrinsicResult { - let maybe_who = if let Some((who, extra)) = self.0 { - Extra::pre_dispatch(extra, &who, &self.1, info, len)?; + let maybe_who = if let Some((who, extra)) = self.signature { + Extra::pre_dispatch(extra, &who, &self.call, info, len)?; Some(who) } else { - Extra::pre_dispatch_unsigned(&self.1, info, len)?; + Extra::pre_dispatch_unsigned(&self.call, info, len)?; None }; - Ok(self.1.dispatch(maybe_who.into()).map_err(Into::into)) + Ok(self.call.dispatch(maybe_who.into()).map_err(Into::into)) } } diff --git a/substrate/primitives/runtime/src/traits.rs b/substrate/primitives/runtime/src/traits.rs index 1b9b9eec70c10441e885d05671609b2ffdd0439f..183df08ab8b0f0b327a43c4d35cafcf08fd2f728 100644 --- a/substrate/primitives/runtime/src/traits.rs +++ b/substrate/primitives/runtime/src/traits.rs @@ -31,7 +31,7 @@ use crate::codec::{Codec, Encode, Decode}; use crate::transaction_validity::{ ValidTransaction, TransactionValidity, TransactionValidityError, UnknownTransaction, }; -use crate::generic::{Digest, DigestItem}; +use crate::generic::{Digest, DigestItem, CheckSignature}; pub use sp_arithmetic::traits::{ AtLeast32Bit, UniqueSaturatedInto, UniqueSaturatedFrom, Saturating, SaturatedConversion, Zero, One, Bounded, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, @@ -637,7 +637,7 @@ pub trait Checkable<Context>: Sized { type Checked; /// Check self, given an instance of Context. - fn check(self, c: &Context) -> Result<Self::Checked, TransactionValidityError>; + fn check(self, signature: CheckSignature, c: &Context) -> Result<Self::Checked, TransactionValidityError>; } /// A "checkable" piece of information, used by the standard Substrate Executive in order to @@ -649,15 +649,15 @@ pub trait BlindCheckable: Sized { type Checked; /// Check self. - fn check(self) -> Result<Self::Checked, TransactionValidityError>; + fn check(self, signature: CheckSignature) -> Result<Self::Checked, TransactionValidityError>; } // Every `BlindCheckable` is also a `StaticCheckable` for arbitrary `Context`. impl<T: BlindCheckable, Context> Checkable<Context> for T { type Checked = <Self as BlindCheckable>::Checked; - fn check(self, _c: &Context) -> Result<Self::Checked, TransactionValidityError> { - BlindCheckable::check(self) + fn check(self, signature: CheckSignature, _c: &Context) -> Result<Self::Checked, TransactionValidityError> { + BlindCheckable::check(self, signature) } } diff --git a/substrate/test-utils/runtime/src/lib.rs b/substrate/test-utils/runtime/src/lib.rs index 2505bdde22f4d882f250936b60d8ebb0a7859f2d..138e79cdd5c591c8a6c2535de1192c86e27afb7b 100644 --- a/substrate/test-utils/runtime/src/lib.rs +++ b/substrate/test-utils/runtime/src/lib.rs @@ -41,6 +41,7 @@ use sp_runtime::{ BlindCheckable, BlakeTwo256, Block as BlockT, Extrinsic as ExtrinsicT, GetNodeBlockType, GetRuntimeBlockType, Verify, IdentityLookup, }, + generic::CheckSignature, }; use sp_version::RuntimeVersion; pub use sp_core::{hash::H256}; @@ -126,7 +127,7 @@ impl serde::Serialize for Extrinsic { impl BlindCheckable for Extrinsic { type Checked = Self; - fn check(self) -> Result<Self, TransactionValidityError> { + fn check(self, _signature: CheckSignature) -> Result<Self, TransactionValidityError> { match self { Extrinsic::AuthoritiesChange(new_auth) => Ok(Extrinsic::AuthoritiesChange(new_auth)), Extrinsic::Transfer(transfer, signature) => { @@ -493,6 +494,10 @@ cfg_if! { system::execute_transaction(extrinsic) } + fn apply_trusted_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult { + system::execute_transaction(extrinsic) + } + fn finalize_block() -> <Block as BlockT>::Header { system::finalize_block() } @@ -680,6 +685,10 @@ cfg_if! { system::execute_transaction(extrinsic) } + fn apply_trusted_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult { + system::execute_transaction(extrinsic) + } + fn finalize_block() -> <Block as BlockT>::Header { system::finalize_block() } diff --git a/substrate/test-utils/runtime/src/system.rs b/substrate/test-utils/runtime/src/system.rs index d0a38c7c7788248b933580bd5ebe89519803fcaf..b410d317a1babaeb52443bb7de7e3bfaf01586c0 100644 --- a/substrate/test-utils/runtime/src/system.rs +++ b/substrate/test-utils/runtime/src/system.rs @@ -29,6 +29,7 @@ use sp_runtime::{ transaction_validity::{ TransactionValidity, ValidTransaction, InvalidTransaction, TransactionValidityError, }, + generic::CheckSignature, }; use codec::{KeyedVec, Encode, Decode}; use frame_system::Trait; @@ -243,7 +244,7 @@ pub fn finalize_block() -> Header { #[inline(always)] fn check_signature(utx: &Extrinsic) -> Result<(), TransactionValidityError> { use sp_runtime::traits::BlindCheckable; - utx.clone().check().map_err(|_| InvalidTransaction::BadProof.into()).map(|_| ()) + utx.clone().check(CheckSignature::Yes).map_err(|_| InvalidTransaction::BadProof.into()).map(|_| ()) } fn execute_transaction_backend(utx: &Extrinsic) -> ApplyExtrinsicResult {