diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index d7ebc9470633e..1e09cd6fe7d34 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -90,10 +90,10 @@ use sp_runtime::{ generic::{self, ExtrinsicFormat, Preamble}, impl_opaque_keys, traits::{ - BlakeTwo256, Block as BlockT, Checkable, ConvertInto, DispatchInfoOf, Dispatchable, - Extrinsic as ExtrinsicT, ExtrinsicMetadata, IdentityLookup, Keccak256, Lookup, OpaqueKeys, - PostDispatchInfoOf, SaturatedConversion, TransactionExtension, TransactionExtensionBase, - ValidateUnsigned, Verify, + Applyable, BlakeTwo256, Block as BlockT, Checkable, ConvertInto, DispatchInfoOf, + Dispatchable, Extrinsic as ExtrinsicT, ExtrinsicMetadata, IdentityLookup, Keccak256, + Lookup, OpaqueKeys, PostDispatchInfoOf, SaturatedConversion, TransactionExtension, + TransactionExtensionBase, ValidateUnsigned, Verify, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, ApplyExtrinsicResult, ApplyExtrinsicResultWithInfo, BoundToRuntimeAppPublic, FixedU128, @@ -826,24 +826,11 @@ where // so the actual block number is `n`. .saturating_sub(1); let tip = 0; - let inner_tx: ( - pallet_transaction_payment::SetFeeAgent< + let inner_tx: InnerUnsigned = ( + pallet_transaction_payment::SetFeeAgent::::default(), + frame_support::transaction_extensions::SignedOriginSignature::< sp_runtime::MultiSignature, - ( - frame_system::CheckNonce, - frame_support::transaction_extensions::CheckSignedPayload< - sp_runtime::MultiSignature, - ( - RuntimeCall, - BaseTxExtension, - ::Implicit, - ), - >, - ), - >, - BaseTxExtension, - ) = ( - pallet_transaction_payment::SetFeeAgent::default(), + >::default(), ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), @@ -870,12 +857,9 @@ where .using_encoded(|payload| C::sign(&sp_io::hashing::blake2_256(payload)[..], public))?; let (call, inner_tx, _) = raw_payload; let address = ::Lookup::unlookup(account.clone()); - let tx_ext = frame_support::transaction_extensions::SignedOriginSignature::new_with_sign( - signature.clone(), - account, - inner_tx, - ); - Some((call, (address, signature, (tx_ext,)))) + let tx_ext = + (frame_support::transaction_extensions::SignedOriginSignature::default(), inner_tx); + Some((call, (address, signature, tx_ext.into()))) } } @@ -1582,6 +1566,16 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// `BlockId` type as expected by this runtime. pub type BlockId = generic::BlockId; +/// The extension to the basic transaction logic. +pub type TxExtension = ( + frame_support::transaction_extensions::SignedOriginSignature, + InnerUnsigned, +); +pub type InnerUnsigned = ( + pallet_transaction_payment::SetFeeAgent, + frame_support::transaction_extensions::SignedOriginSignature, + BaseTxExtension, +); pub type BaseTxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, @@ -1592,48 +1586,8 @@ pub type BaseTxExtension = ( frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, ); -pub type InnerUnsigned = ( - pallet_transaction_payment::SetFeeAgent< - sp_runtime::MultiSignature, - ( - frame_system::CheckNonce, - frame_support::transaction_extensions::CheckSignedPayload< - sp_runtime::MultiSignature, - ( - RuntimeCall, - BaseTxExtension, - ::Implicit, - ), - >, - ), - >, - BaseTxExtension, -); -/// The extension to the basic transaction logic. -pub type TxExtension = ( - frame_support::transaction_extensions::SignedOriginSignature< - sp_runtime::MultiSignature, - ( - pallet_transaction_payment::SetFeeAgent< - sp_runtime::MultiSignature, - ( - frame_system::CheckNonce, - frame_support::transaction_extensions::CheckSignedPayload< - sp_runtime::MultiSignature, - ( - RuntimeCall, - BaseTxExtension, - ::Implicit, - ), - >, - ), - >, - BaseTxExtension, - ), - >, -); -impl sp_runtime::traits::Applyable for CheckedExtrinsic { +impl Applyable for CheckedExtrinsic { type Call = RuntimeCall; fn validate>( @@ -1651,16 +1605,15 @@ impl sp_runtime::traits::Applyable for CheckedExtrinsic { }, ExtrinsicFormat::Signed(ref signer, ref extension) => { let origin = Some(signer.clone()).into(); - extension - .0 - .extension + let (_signature_check, inner_extension) = extension; + inner_extension .validate( origin, &self.0.function, info, len, &mut Default::default(), - extension.0.extension.implicit()?, + inner_extension.implicit()?, &self.0.function, ) .map(|x| x.0) @@ -1707,7 +1660,7 @@ impl sp_runtime::traits::Applyable for CheckedExtrinsic { }, ExtrinsicFormat::Signed(signer, extension) => { // extension.dispatch_transaction(Some(signer).into(), self.function, info, len), - let inner_extension: InnerUnsigned = extension.0.extension; + let (_signature_check, inner_extension) = extension; let mut context = pallet_transaction_payment::Context::default(); let (_, val, origin) = inner_extension .validate( @@ -1760,6 +1713,18 @@ impl frame_support::dispatch::GetDispatchInfo for CheckedExtrinsic { } } +#[cfg(test)] +struct DummyValidateUnsigned; + +#[cfg(test)] +impl ValidateUnsigned for DummyValidateUnsigned { + type Call = RuntimeCall; + + fn validate_unsigned(_source: TransactionSource, _call: &Self::Call) -> TransactionValidity { + Ok(sp_runtime::transaction_validity::ValidTransaction::default()) + } +} + #[test] fn free_transaction_extension_test() { sp_io::TestExternalities::default().execute_with(|| { @@ -1771,31 +1736,13 @@ fn free_transaction_extension_test() { use sp_io::hashing::blake2_256; use sp_runtime::{traits::TransactionExtensionBase, MultiSignature}; - type CheckSignedInnerTxExtension = - frame_support::transaction_extensions::CheckSignedPayload< - sp_runtime::MultiSignature, - ( - RuntimeCall, - BaseTxExtension, - ::Implicit, - ), - >; - - type InnerUnsignedTxExtension = ( - pallet_transaction_payment::SetFeeAgent< - sp_runtime::MultiSignature, - (frame_system::CheckNonce, CheckSignedInnerTxExtension), - >, + // The part of `TxExtension` that has to be provided and signed by the fee agent, + // the user who sponsors the transaction fee. + type SignedTxExtension = ( + frame_support::transaction_extensions::SignedOriginSignature, BaseTxExtension, ); - type OuterSignedTxExtension = ( - frame_support::transaction_extensions::SignedOriginSignature< - sp_runtime::MultiSignature, - InnerUnsignedTxExtension, - >, - ); - frame_system::GenesisConfig::::default().build(); System::set_block_number(1); @@ -1822,7 +1769,6 @@ fn free_transaction_extension_test() { Balances::force_set_balance(RuntimeOrigin::root(), bob_account.clone().into(), bob_balance) .unwrap(); - Balances::force_set_balance( RuntimeOrigin::root(), charlie_account.clone().into(), @@ -1839,7 +1785,7 @@ fn free_transaction_extension_test() { frame_system::Pallet::::account(&charlie_account).nonce; // Alice builds the transaction extension for the sponsored transaction. - let alice_base_ext: BaseTxExtension = ( + let stmt_ext: BaseTxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -1858,48 +1804,50 @@ fn free_transaction_extension_test() { ) .into(); - let alice_base_ext_implicit = alice_base_ext.implicit().unwrap(); - // Alice signs the transaction extension and shares it. - let alice_base_sign = MultiSignature::Sr25519( - (call.clone(), alice_base_ext.clone(), alice_base_ext_implicit.clone()) + let stmt_sign = MultiSignature::Sr25519( + (call.clone(), stmt_ext.clone(), stmt_ext.implicit().unwrap()) .using_encoded(|e| alice_keyring.sign(&blake2_256(e))), ); + let statement = (call.clone(), stmt_ext, stmt_sign).encode(); + + type Statement = (RuntimeCall, BaseTxExtension, MultiSignature); + let (stmt_call, stmt_ext, stmt_sign) = Statement::decode(&mut &statement[..]).unwrap(); + // Bob constructs the transaction based on Alice statemnt. - let alice_signed_tx_ext = CheckSignedInnerTxExtension::new_with_sign( - alice_base_sign, - alice_account.clone(), - (call.clone(), alice_base_ext.clone(), alice_base_ext_implicit.clone()), - ); + let signed_tx_ext = frame_support::transaction_extensions::SignedOriginSignature::< + MultiSignature, + >::new_with_sign(stmt_sign, alice_account.clone()); - let bob_unsigned_tx_ext = ( - pallet_transaction_payment::SetFeeAgent::new_with_agent( - bob_account.clone(), - ( - frame_system::CheckNonce::::from( - frame_system::Pallet::::account(&bob_account).nonce, - ), - alice_signed_tx_ext, - ), - ), - alice_base_ext, - ); + let mut signed_tx_ext = signed_tx_ext.encode(); + signed_tx_ext.append(&mut stmt_ext.encode()); + let signed_tx_ext: SignedTxExtension = + SignedTxExtension::decode(&mut &signed_tx_ext[..]).unwrap(); - let bob_unsigned_tx_ext_implicit = bob_unsigned_tx_ext.implicit().unwrap(); - let bob_sign = MultiSignature::Sr25519( - (call.clone(), bob_unsigned_tx_ext.clone(), bob_unsigned_tx_ext_implicit) + // Bob signs the transaction with Alice's part to poof he is willing to sponser the fee. + let signed_tx_sign = MultiSignature::Sr25519( + (stmt_call, signed_tx_ext.clone(), signed_tx_ext.implicit().unwrap()) .using_encoded(|e| bob_keyring.sign(&blake2_256(e))), ); - let bob_signed_tx_ext: OuterSignedTxExtension = - (frame_support::transaction_extensions::SignedOriginSignature::new_with_sign( - bob_sign, - bob_account.clone(), - bob_unsigned_tx_ext, - ),); + let tx_ext = pallet_transaction_payment::SetFeeAgent::::new_with_agent( + signed_tx_sign, + bob_account.clone(), + ); + + let empty_signature_ext = frame_support::transaction_extensions::SignedOriginSignature::< + MultiSignature, + >::default(); + + let mut tx_ext_encoded = empty_signature_ext.encode(); + tx_ext_encoded.append(&mut tx_ext.encode()); + tx_ext_encoded.append(&mut signed_tx_ext.encode()); + + // The final valid for submission transaction extension. + let bob_signed_tx_ext: TxExtension = TxExtension::decode(&mut &tx_ext_encoded[..]).unwrap(); // Dispatch the transaction { @@ -1921,8 +1869,7 @@ fn free_transaction_extension_test() { let res = call.dispatch(origin); let post_info = res.unwrap_or_else(|err| err.post_info); let pd_res = res.map(|_| ()).map_err(|e| e.error); - OuterSignedTxExtension::post_dispatch(pre, &info, &post_info, len, &pd_res, &context) - .unwrap(); + TxExtension::post_dispatch(pre, &info, &post_info, len, &pd_res, &context).unwrap(); } // Alice balance is unchanged, Bob paid the transaction fee. @@ -1942,16 +1889,15 @@ fn free_transaction_extension_test() { alice_initial_nonce.saturating_add(1), frame_system::Pallet::::account(&alice_account).nonce ); - assert_eq!( - bob_initial_nonce.saturating_add(1), - frame_system::Pallet::::account(&bob_account).nonce - ); + assert_eq!(bob_initial_nonce, frame_system::Pallet::::account(&bob_account).nonce); // The call that Charlie wants to be executed. let call = RuntimeCall::System(frame_system::Call::remark_with_event { remark: vec![3] }); let charlie_unsigned_tx_ext = ( pallet_transaction_payment::SetFeeAgent::default(), + frame_support::transaction_extensions::SignedOriginSignature::::default( + ), ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), @@ -1972,12 +1918,13 @@ fn free_transaction_extension_test() { .using_encoded(|e| charlie_keyring.sign(&blake2_256(e))), ); - let charlie_signed_tx_ext: OuterSignedTxExtension = - (frame_support::transaction_extensions::SignedOriginSignature::new_with_sign( + let charlie_signed_tx_ext: TxExtension = ( + frame_support::transaction_extensions::SignedOriginSignature::new_with_sign( charlie_sign, charlie_account.clone(), - charlie_unsigned_tx_ext, - ),); + ), + charlie_unsigned_tx_ext, + ); // Dispatch the transaction { @@ -2001,8 +1948,7 @@ fn free_transaction_extension_test() { let res = call.dispatch(origin); let post_info = res.unwrap_or_else(|err| err.post_info); let pd_res = res.map(|_| ()).map_err(|e| e.error); - OuterSignedTxExtension::post_dispatch(pre, &info, &post_info, len, &pd_res, &context) - .unwrap(); + TxExtension::post_dispatch(pre, &info, &post_info, len, &pd_res, &context).unwrap(); } assert!(charlie_balance > Balances::free_balance(charlie_account.clone())); @@ -2023,6 +1969,222 @@ fn free_transaction_extension_test() { }); } +#[test] +fn applied_free_transaction_extension_test() { + sp_io::TestExternalities::default().execute_with(|| { + use frame_support::{dispatch::DispatchInfo, traits::BuildGenesisConfig}; + use keyring::AccountKeyring; + use sp_io::hashing::blake2_256; + use sp_runtime::{traits::TransactionExtensionBase, MultiSignature}; + + // The part of `TxExtension` that has to be provided and signed by the fee agent, + // the user who sponsors the transaction fee. + type SignedTxExtension = ( + frame_support::transaction_extensions::SignedOriginSignature, + BaseTxExtension, + ); + + frame_system::GenesisConfig::::default().build(); + System::set_block_number(1); + + // Alice wants the transaction fee to be sponsored by Bob. + let alice_keyring = AccountKeyring::Alice; + let bob_keyring = AccountKeyring::Bob; + let charlie_keyring = AccountKeyring::Charlie; + + let alice_account = AccountId::from(alice_keyring.public()); + let bob_account = AccountId::from(bob_keyring.public()); + let charlie_account = AccountId::from(charlie_keyring.public()); + + // Setup the initial balances. + let alice_balance = 10 * ExistentialDeposit::get(); + let bob_balance = 10 * ExistentialDeposit::get(); + let charlie_balance = 10 * ExistentialDeposit::get(); + + Balances::force_set_balance( + RuntimeOrigin::root(), + alice_account.clone().into(), + alice_balance, + ) + .unwrap(); + + Balances::force_set_balance(RuntimeOrigin::root(), bob_account.clone().into(), bob_balance) + .unwrap(); + Balances::force_set_balance( + RuntimeOrigin::root(), + charlie_account.clone().into(), + charlie_balance, + ) + .unwrap(); + + // The call that Alice wants to be executed. + let call = RuntimeCall::System(frame_system::Call::remark_with_event { remark: vec![1] }); + + let alice_initial_nonce = frame_system::Pallet::::account(&alice_account).nonce; + let bob_initial_nonce = frame_system::Pallet::::account(&bob_account).nonce; + let charlie_initial_nonce = + frame_system::Pallet::::account(&charlie_account).nonce; + + // Alice builds the transaction extension for the sponsored transaction. + let stmt_ext: BaseTxExtension = ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckMortality::::from(sp_runtime::generic::Era::immortal()), + frame_system::CheckNonce::::from( + frame_system::Pallet::::account(&alice_account).nonce, + ), + frame_system::CheckWeight::::new(), + // In case if any agent can sponsor the transaction fee. + // pallet_transaction_payment::ChargeTransactionPayment::::with_any_agent(), + // Only Bob can sponsor the transaction fee. + pallet_transaction_payment::ChargeTransactionPayment::::with_agent( + bob_account.clone(), + ), + ) + .into(); + + // Alice signs the transaction extension and shares it. + + let stmt_sign = MultiSignature::Sr25519( + (call.clone(), stmt_ext.clone(), stmt_ext.implicit().unwrap()) + .using_encoded(|e| alice_keyring.sign(&blake2_256(e))), + ); + + let statement = (call.clone(), stmt_ext, stmt_sign).encode(); + + type Statement = (RuntimeCall, BaseTxExtension, MultiSignature); + let (stmt_call, stmt_ext, stmt_sign) = Statement::decode(&mut &statement[..]).unwrap(); + + // Bob constructs the transaction based on Alice statemnt. + + let signed_tx_ext = frame_support::transaction_extensions::SignedOriginSignature::< + MultiSignature, + >::new_with_sign(stmt_sign, alice_account.clone()); + + let mut signed_tx_ext = signed_tx_ext.encode(); + signed_tx_ext.append(&mut stmt_ext.encode()); + let signed_tx_ext: SignedTxExtension = + SignedTxExtension::decode(&mut &signed_tx_ext[..]).unwrap(); + + // Bob signs the transaction with Alice's part to poof he is willing to sponser the fee. + let signed_tx_sign = MultiSignature::Sr25519( + (stmt_call, signed_tx_ext.clone(), signed_tx_ext.implicit().unwrap()) + .using_encoded(|e| bob_keyring.sign(&blake2_256(e))), + ); + + let tx_ext = pallet_transaction_payment::SetFeeAgent::::new_with_agent( + signed_tx_sign, + bob_account.clone(), + ); + + let empty_signature_ext = frame_support::transaction_extensions::SignedOriginSignature::< + MultiSignature, + >::default(); + + let mut tx_ext_encoded = empty_signature_ext.encode(); + tx_ext_encoded.append(&mut tx_ext.encode()); + tx_ext_encoded.append(&mut signed_tx_ext.encode()); + + // The final valid for submission transaction extension. + let bob_signed_tx_ext: TxExtension = TxExtension::decode(&mut &tx_ext_encoded[..]).unwrap(); + + let info = DispatchInfo::default(); + let len = call.encoded_size(); + + let xt = UncheckedExtrinsic(generic::UncheckedExtrinsic::< + Address, + RuntimeCall, + Signature, + TxExtension, + pallet_transaction_payment::Context, + >::new_transaction(call.clone(), bob_signed_tx_ext.clone())); + let checked_xt = xt.check(&Default::default()).unwrap(); + assert!(checked_xt.apply::(&info, len).is_ok()); + + // Alice balance is unchanged, Bob paid the transaction fee. + assert_eq!(alice_balance, Balances::free_balance(alice_account.clone())); + assert!(bob_balance > Balances::free_balance(bob_account.clone())); + + assert!(System::events().iter().any(|ev| ev.event == + RuntimeEvent::System(frame_system::Event::Remarked { + sender: alice_account.clone(), + hash: + <::Hashing as sp_runtime::traits::Hash>::hash( + &[1u8] + ) + }))); + + assert_eq!( + alice_initial_nonce.saturating_add(1), + frame_system::Pallet::::account(&alice_account).nonce + ); + assert_eq!(bob_initial_nonce, frame_system::Pallet::::account(&bob_account).nonce); + + // The call that Charlie wants to be executed. + let call = RuntimeCall::System(frame_system::Call::remark_with_event { remark: vec![3] }); + + let charlie_unsigned_tx_ext = ( + pallet_transaction_payment::SetFeeAgent::default(), + frame_support::transaction_extensions::SignedOriginSignature::::default( + ), + ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckMortality::::from(sp_runtime::generic::Era::immortal()), + frame_system::CheckNonce::::from( + frame_system::Pallet::::account(&charlie_account).nonce, + ), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(0), + ), + ); + + let charlie_unsigned_tx_ext_implicit = charlie_unsigned_tx_ext.implicit().unwrap(); + let charlie_sign = MultiSignature::Sr25519( + (call.clone(), charlie_unsigned_tx_ext.clone(), charlie_unsigned_tx_ext_implicit) + .using_encoded(|e| charlie_keyring.sign(&blake2_256(e))), + ); + + let charlie_signed_tx_ext: TxExtension = ( + frame_support::transaction_extensions::SignedOriginSignature::new_with_sign( + charlie_sign, + charlie_account.clone(), + ), + charlie_unsigned_tx_ext, + ); + + let xt = UncheckedExtrinsic(generic::UncheckedExtrinsic::< + Address, + RuntimeCall, + Signature, + TxExtension, + pallet_transaction_payment::Context, + >::new_transaction(call.clone(), charlie_signed_tx_ext.clone())); + let checked_xt = xt.check(&Default::default()).unwrap(); + assert!(checked_xt.apply::(&info, len).is_ok()); + + assert!(charlie_balance > Balances::free_balance(charlie_account.clone())); + + assert!(System::events().iter().any(|ev| ev.event == + RuntimeEvent::System(frame_system::Event::Remarked { + sender: charlie_account.clone(), + hash: + <::Hashing as sp_runtime::traits::Hash>::hash( + &[3u8] + ) + }))); + + assert_eq!( + charlie_initial_nonce.saturating_add(1), + frame_system::Pallet::::account(&charlie_account).nonce + ); + }); +} + pub struct NominationPoolsMigrationV4OldPallet; impl Get for NominationPoolsMigrationV4OldPallet { fn get() -> Perbill { diff --git a/substrate/frame/support/src/transaction_extensions.rs b/substrate/frame/support/src/transaction_extensions.rs index 050431be68406..2bd8f94a0b5bf 100644 --- a/substrate/frame/support/src/transaction_extensions.rs +++ b/substrate/frame/support/src/transaction_extensions.rs @@ -94,205 +94,67 @@ where /// Transaction extension that sets the origin to the given account ID if the provided signature by /// that account is valid for all subsequent extensions. If signature is not provided, this -/// extension is no-op. Will run wrapped extension logic after the origin validation. +/// extension is no-op. // TODO better doc. #[derive( CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, RuntimeDebugNoBound, TypeInfo, )] #[codec(encode_bound())] #[codec(decode_bound())] -pub struct SignedOriginSignature +pub struct SignedOriginSignature where V: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, ::AccountId: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, - InnerTx: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, { - pub signature: Option<(V, ::AccountId)>, - pub extension: InnerTx, + signature: Option<(V, ::AccountId)>, } -impl SignedOriginSignature +impl Default for SignedOriginSignature where V: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, ::AccountId: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, - InnerTx: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, { - pub fn new_with_sign( - signature: V, - account_id: ::AccountId, - extension: InnerTx, - ) -> Self { - Self { signature: Some((signature, account_id)), extension } - } -} - -impl TransactionExtensionBase for SignedOriginSignature -where - V: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, - ::AccountId: - Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, - InnerTx: Codec - + Debug - + Sync - + Send - + Clone - + Eq - + PartialEq - + StaticTypeInfo - + TransactionExtensionBase, -{ - const IDENTIFIER: &'static str = "SignedOriginSignature"; - type Implicit = (); -} - -impl - TransactionExtension for SignedOriginSignature -where - V: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, - ::AccountId: - Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, - ::RuntimeOrigin: From::AccountId>>, - InnerTx: Codec - + Debug - + Sync - + Send - + Clone - + Eq - + PartialEq - + StaticTypeInfo - + TransactionExtension, -{ - type Val = InnerTx::Val; - type Pre = InnerTx::Pre; - - fn validate( - &self, - origin: ::RuntimeOrigin, - call: &Call, - info: &DispatchInfoOf, - len: usize, - context: &mut Context, - _self_implicit: (), - inherited_implication: &impl Encode, - ) -> Result< - (ValidTransaction, Self::Val, ::RuntimeOrigin), - TransactionValidityError, - > { - let (signature, account_id) = match &self.signature { - Some((s, a)) => (s, a.clone()), // TODO check if origin None - None => { - let implicit = self.extension.implicit()?; - return self.extension.validate( - origin, - call, - info, - len, - context, - implicit, - inherited_implication, - ) - }, - }; - - let implicit = self.extension.implicit()?; - let signed_payload = (call, &self.extension, &implicit); - if !signed_payload - .using_encoded(|payload| signature.verify(&blake2_256(payload)[..], &account_id)) - { - return Err(InvalidTransaction::BadProof.into()) - } - - let origin = Some(account_id).into(); - self.extension - .validate(origin, call, info, len, context, implicit, inherited_implication) - } - - fn prepare( - self, - val: Self::Val, - origin: &sp_runtime::traits::OriginOf, - call: &Call, - info: &DispatchInfoOf, - len: usize, - context: &Context, - ) -> Result { - self.extension.prepare(val, origin, call, info, len, context) - } - - fn post_dispatch( - pre: Self::Pre, - info: &DispatchInfoOf, - post_info: &sp_runtime::traits::PostDispatchInfoOf, - len: usize, - result: &sp_runtime::DispatchResult, - context: &Context, - ) -> Result<(), TransactionValidityError> { - InnerTx::post_dispatch(pre, info, post_info, len, result, context) + fn default() -> Self { + Self { signature: None } } } -/// Transaction extension that sets the origin to the given account ID if the provided signature by -/// that account is valid for the provided payload. -#[derive( - CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, RuntimeDebugNoBound, TypeInfo, -)] -#[codec(encode_bound())] -#[codec(decode_bound())] -pub struct CheckSignedPayload +impl SignedOriginSignature where V: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, ::AccountId: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, - P: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, -{ - pub signature: Option<(V, ::AccountId, P)>, -} - -impl CheckSignedPayload -where - V: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, - ::AccountId: - Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, - P: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, { pub fn new_with_sign( signature: V, account_id: ::AccountId, - payload: P, ) -> Self { - Self { signature: Some((signature, account_id, payload)) } - } - - pub fn new() -> Self { - Self { signature: None } + Self { signature: Some((signature, account_id)) } } } -impl TransactionExtensionBase for CheckSignedPayload +impl TransactionExtensionBase for SignedOriginSignature where V: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, ::AccountId: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, - P: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, { - const IDENTIFIER: &'static str = "CheckSignedPayload"; + const IDENTIFIER: &'static str = "SignedOriginSignature"; type Implicit = (); } -impl - TransactionExtension for CheckSignedPayload +impl TransactionExtension + for SignedOriginSignature where V: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, ::AccountId: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, - P: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, ::RuntimeOrigin: From::AccountId>>, { type Val = (); type Pre = (); - impl_tx_ext_default!(Call; Context; prepare); fn validate( @@ -301,23 +163,23 @@ where _call: &Call, _info: &DispatchInfoOf, _len: usize, - _context: &mut Context, - _self_implicit: (), - _inherited_implication: &impl Encode, + _: &mut Context, + _: (), + inherited_implication: &impl Encode, ) -> Result< (ValidTransaction, Self::Val, ::RuntimeOrigin), TransactionValidityError, > { - let (signature, account_id, payload) = match &self.signature { - Some((s, a, p)) => (s, a.clone(), p), // TODO check if origin None + let (signature, account_id) = match &self.signature { + Some((s, a)) => (s, a.clone()), // TODO check if origin None None => return Ok((ValidTransaction::default(), (), origin)), }; - if !payload.using_encoded(|payload| signature.verify(&blake2_256(payload)[..], &account_id)) - { - return Err(InvalidTransaction::BadProof.into()) - } + let msg = inherited_implication.using_encoded(blake2_256); + if !signature.verify(&msg[..], &account_id) { + Err(InvalidTransaction::BadProof)? + } let origin = Some(account_id).into(); Ok((ValidTransaction::default(), (), origin)) } diff --git a/substrate/frame/transaction-payment/src/lib.rs b/substrate/frame/transaction-payment/src/lib.rs index 625a7a188e78d..70de8600260c8 100644 --- a/substrate/frame/transaction-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/src/lib.rs @@ -964,7 +964,11 @@ use codec::Codec; use core::fmt::Debug; use frame_support::{CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound}; use scale_info::StaticTypeInfo; -use sp_runtime::traits::{IdentifyAccount, Verify}; +use sp_io::hashing::blake2_256; +use sp_runtime::{ + impl_tx_ext_default, + traits::{IdentifyAccount, Verify}, +}; #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] pub enum FeeAgent { @@ -999,79 +1003,67 @@ impl Default for Context { )] #[codec(encode_bound())] #[codec(decode_bound())] -pub struct SetFeeAgent +pub struct SetFeeAgent where V: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, ::AccountId: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, - Tx: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, { - pub agent: Option<::AccountId>, - pub rest: Option, + pub agent: Option<(V, ::AccountId)>, } -impl Default for SetFeeAgent +impl Default for SetFeeAgent where V: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, ::AccountId: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, - Tx: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, { fn default() -> Self { - Self { agent: None, rest: None } + Self { agent: None } } } -impl SetFeeAgent +impl SetFeeAgent where V: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, ::AccountId: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, - Tx: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, { - pub fn new_with_agent(account_id: ::AccountId, tx: Tx) -> Self { - Self { agent: Some(account_id), rest: Some(tx) } + pub fn new_with_agent( + signature: V, + account_id: ::AccountId, + ) -> Self { + Self { agent: Some((signature, account_id)) } } } -impl TransactionExtensionBase for SetFeeAgent +impl TransactionExtensionBase for SetFeeAgent where V: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, ::AccountId: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, - Tx: TransactionExtensionBase - + Codec - + Debug - + Sync - + Send - + Clone - + Eq - + PartialEq - + StaticTypeInfo, { const IDENTIFIER: &'static str = "SetFeeAgent"; type Implicit = (); } -impl - TransactionExtension::AccountId>> - for SetFeeAgent +impl + TransactionExtension::AccountId>> for SetFeeAgent where - Call: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, V: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, ::AccountId: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, - InnerTx: TransactionExtension::AccountId>>, { - type Val = Option; - type Pre = Option; + type Val = (); + type Pre = (); + impl_tx_ext_default!(Call; Context<::AccountId>; prepare); fn validate( &self, origin: ::RuntimeOrigin, - call: &Call, - info: &DispatchInfoOf, - len: usize, + _call: &Call, + _info: &DispatchInfoOf, + _len: usize, context: &mut Context<::AccountId>, _: (), inherited_implication: &impl Encode, @@ -1079,53 +1071,17 @@ where (ValidTransaction, Self::Val, ::RuntimeOrigin), TransactionValidityError, > { - let account_id = match &self.agent { - None => return Ok((ValidTransaction::default(), None, origin)), - Some(a) => a.clone(), // TODO check if origin None + let (signature, account_id) = match &self.agent { + None => return Ok((ValidTransaction::default(), (), origin)), + Some((s, a)) => (s, a.clone()), // TODO check if origin None }; - let rest = match &self.rest { - Some(inner) => inner, - None => return Ok((ValidTransaction::default(), None, origin)), - }; + let msg = inherited_implication.using_encoded(blake2_256); - *context = Context { fee_agent: Some(account_id) }; - let rest_implicit = rest.implicit()?; - let (validity, val, origin) = - rest.validate(origin, call, info, len, context, rest_implicit, inherited_implication)?; - Ok((validity, Some(val), origin)) - } - - fn prepare( - self, - val: Self::Val, - origin: &::RuntimeOrigin, - call: &Call, - info: &DispatchInfoOf, - len: usize, - context: &Context<::AccountId>, - ) -> Result { - match self.rest { - Some(inner) => { - let val = val.unwrap(); - let pre = inner.prepare(val, origin, call, info, len, context)?; - Ok(Some(pre)) - }, - None => Ok(None), - } - } - - fn post_dispatch( - pre: Self::Pre, - info: &DispatchInfoOf, - post_info: &PostDispatchInfoOf, - len: usize, - result: &DispatchResult, - context: &Context<::AccountId>, - ) -> Result<(), TransactionValidityError> { - match pre { - Some(pre) => InnerTx::post_dispatch(pre, info, post_info, len, result, context), - None => Ok(()), + if !signature.verify(&msg[..], &account_id) { + Err(InvalidTransaction::BadProof)? } + *context = Context { fee_agent: Some(account_id) }; + Ok((ValidTransaction::default(), (), origin)) } }