Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2029: Passkey withdraw token fees for tx #2040

Merged
merged 47 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
9628adb
check account signature on passkey
saraswatpuneet Jun 18, 2024
521919e
reference
saraswatpuneet Jun 18, 2024
5e035f6
setup mock and account types
saraswatpuneet Jun 18, 2024
2a5edbb
set unused flag for now
saraswatpuneet Jun 18, 2024
079f764
fix build
saraswatpuneet Jun 18, 2024
e387a2d
address feedback
saraswatpuneet Jun 20, 2024
7195c75
set correct AccountId in mock
saraswatpuneet Jun 20, 2024
b0f4699
set spec version in future
saraswatpuneet Jun 20, 2024
913b619
update spec version
saraswatpuneet Jun 20, 2024
3535034
sync with types PR
saraswatpuneet Jun 21, 2024
2fbec5a
cleanup
saraswatpuneet Jun 21, 2024
e6ceee2
set pallet balances as dependency
saraswatpuneet Jun 22, 2024
3730996
refactor common types from payment
saraswatpuneet Jun 24, 2024
a395a83
withdraw tx fee
saraswatpuneet Jun 24, 2024
a3454dc
lint
saraswatpuneet Jun 24, 2024
b69ca9b
Merge branch 'main' into passkey_tx_charge
saraswatpuneet Jun 24, 2024
04eeb69
Merge branch 'main' into passkey_tx_charge
saraswatpuneet Jun 25, 2024
4acf6bf
set functions to charge tx fees
saraswatpuneet Jun 26, 2024
d952208
add negative test without funds fails
saraswatpuneet Jun 26, 2024
04329f1
charge tx and add tests
saraswatpuneet Jun 26, 2024
339642a
cleanup
saraswatpuneet Jun 26, 2024
c130441
Merge branch 'main' into passkey_tx_charge
saraswatpuneet Jun 26, 2024
40eed8b
cleanup
saraswatpuneet Jun 26, 2024
88ae756
charge fee post validation
saraswatpuneet Jun 26, 2024
ab7488a
add more tests
saraswatpuneet Jun 26, 2024
5b91e44
improve logic
saraswatpuneet Jun 27, 2024
df1de03
docs
saraswatpuneet Jun 27, 2024
d3fbc5e
Merge branch 'main' into passkey_tx_charge
saraswatpuneet Jun 28, 2024
c4445c0
Merge branch 'main' into passkey_tx_charge
saraswatpuneet Jun 28, 2024
c4a8da3
improve pattern inline with signed ext
saraswatpuneet Jun 28, 2024
9a0e534
fix tests
saraswatpuneet Jun 28, 2024
066428f
address feedback, undo refactor
saraswatpuneet Jun 28, 2024
5cf9ae9
cleanup
saraswatpuneet Jun 28, 2024
44f191f
Merge branch 'main' into passkey_tx_charge
saraswatpuneet Jun 28, 2024
6859481
fund accounts for benchamrking, add helper
saraswatpuneet Jul 1, 2024
d20d2ff
add more tests
saraswatpuneet Jul 1, 2024
42fcca4
add test suggested by aramik
saraswatpuneet Jul 1, 2024
e2378f9
add condition
saraswatpuneet Jul 1, 2024
32ba98e
use fungible trait/remove Curreny trait
saraswatpuneet Jul 1, 2024
6df024b
fix redundant calls and fix order of calls
saraswatpuneet Jul 1, 2024
a3874b4
fix
saraswatpuneet Jul 1, 2024
45aef19
combine tx validaity
saraswatpuneet Jul 1, 2024
36bc6db
cleanups
saraswatpuneet Jul 1, 2024
fb7b463
fix build
saraswatpuneet Jul 1, 2024
d0d9598
address feedback: check for error type
saraswatpuneet Jul 2, 2024
9669093
update spec version
saraswatpuneet Jul 2, 2024
6f88951
Merge branch 'main' into passkey_tx_charge
saraswatpuneet Jul 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pallets/frequency-tx-payment/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ where
) -> Result<(BalanceOf<T>, InitialPayment<T>), TransactionValidityError> {
let fee = pallet_transaction_payment::Pallet::<T>::compute_fee(len as u32, info, tip);
if fee.is_zero() {
return Ok((fee, InitialPayment::Free))
return Ok((fee, InitialPayment::Free));
}

<OnChargeTransactionOf<T> as OnChargeTransaction<T>>::withdraw_fee(
Expand Down
1 change: 1 addition & 0 deletions pallets/passkey/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ sp-std = { workspace = true }
frame-benchmarking = { workspace = true, optional = true }
sp-core = { workspace = true }
log = { workspace = true, default-features = false }
pallet-transaction-payment = { workspace = true }

# Frequency related dependencies
common-primitives = { default-features = false, path = "../../common/primitives" }
Expand Down
7 changes: 5 additions & 2 deletions pallets/passkey/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ type SignerId = app_sr25519::Public;

fn generate_payload<T: Config>() -> PasskeyPayload<T> {
let test_account_1_pk = SignerId::generate_pair(None);
let test_account_1_account_id =
T::AccountId::decode(&mut &test_account_1_pk.encode()[..]).unwrap();
T::Currency::set_balance(&test_account_1_account_id.clone().into(), 1000000000u32.into());
let passkey_public_key = [0u8; 33];
let wrapped_binary = wrap_binary_data(passkey_public_key.to_vec());
let signature: MultiSignature =
Expand All @@ -32,7 +35,7 @@ fn generate_payload<T: Config>() -> PasskeyPayload<T> {
frame_system::Call::<T>::remark { remark: vec![] }.into();

let call: PasskeyCall<T> = PasskeyCall {
account_id: T::AccountId::decode(&mut &test_account_1_pk.encode()[..]).unwrap(),
account_id: test_account_1_account_id,
account_nonce: T::Nonce::zero(),
account_ownership_proof: signature,
call: Box::new(inner_call),
Expand All @@ -50,7 +53,7 @@ fn generate_payload<T: Config>() -> PasskeyPayload<T> {
}

benchmarks! {
where_clause { where <T as frame_system::Config>::RuntimeCall: Dispatchable<Info = DispatchInfo> }
where_clause { where <T as frame_system::Config>::RuntimeCall: From<Call<T>> + Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> }

validate {
let payload = generate_payload::<T>();
Expand Down
123 changes: 110 additions & 13 deletions pallets/passkey/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,28 @@
rustdoc::invalid_codeblock_attributes,
missing_docs
)]

use common_primitives::utils::wrap_binary_data;
use common_runtime::extensions::check_nonce::CheckNonce;
use frame_support::{
dispatch::{DispatchInfo, GetDispatchInfo, PostDispatchInfo},
pallet_prelude::*,
traits::Contains,
};
use frame_system::pallet_prelude::*;
use pallet_transaction_payment::OnChargeTransaction;
use sp_runtime::{
traits::{Convert, Dispatchable, SignedExtension, Verify},
traits::{Convert, DispatchInfoOf, Dispatchable, SignedExtension, Verify, Zero},
transaction_validity::{TransactionValidity, TransactionValidityError},
AccountId32, MultiSignature,
};
use sp_std::{vec, vec::Vec};

use common_runtime::extensions::check_nonce::CheckNonce;
/// Type aliases used for interaction with `OnChargeTransaction`.
pub(crate) type OnChargeTransactionOf<T> =
<T as pallet_transaction_payment::Config>::OnChargeTransaction;

/// Balance type alias.
pub(crate) type BalanceOf<T> = <OnChargeTransactionOf<T> as OnChargeTransaction<T>>::Balance;

#[cfg(test)]
mod mock;
Expand All @@ -40,6 +47,9 @@ mod tests;
pub mod weights;
pub use weights::*;

#[cfg(feature = "runtime-benchmarks")]
use frame_support::traits::tokens::fungible::Mutate;

#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;

Expand All @@ -58,7 +68,7 @@ pub mod module {
pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);

#[pallet::config]
pub trait Config: frame_system::Config {
pub trait Config: frame_system::Config + pallet_transaction_payment::Config {
/// The overarching event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;

Expand All @@ -67,7 +77,8 @@ pub mod module {
+ Dispatchable<RuntimeOrigin = Self::RuntimeOrigin, PostInfo = PostDispatchInfo>
+ GetDispatchInfo
+ From<frame_system::Call<Self>>
+ IsType<<Self as frame_system::Config>::RuntimeCall>;
+ IsType<<Self as frame_system::Config>::RuntimeCall>
+ From<Call<Self>>;

/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;
Expand All @@ -77,6 +88,10 @@ pub mod module {

/// Filters the inner calls for passkey which is set in runtime
type PasskeyCallFilter: Contains<<Self as Config>::RuntimeCall>;

/// Helper Curreny method for benchmarking
#[cfg(feature = "runtime-benchmarks")]
type Currency: Mutate<Self::AccountId>;
}

#[pallet::error]
Expand Down Expand Up @@ -131,28 +146,46 @@ pub mod module {
#[pallet::validate_unsigned]
impl<T: Config> ValidateUnsigned for Pallet<T>
where
<T as frame_system::Config>::RuntimeCall: Dispatchable<Info = DispatchInfo>,
<T as frame_system::Config>::RuntimeCall:
From<Call<T>> + Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
{
type Call = Call<T>;
fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity {
let valid_tx = ValidTransaction::default();
let payload = Self::filter_valid_calls(&call)?;
Self::validate_signatures(&payload)?;

let signature_validity = Self::validate_signatures(&payload)?;
let nonce_check = PasskeyNonce::new(payload.passkey_call.clone());
nonce_check.validate()
let nonce_validity = nonce_check.validate()?;
let tx_charge = ChargeTransactionPayment::<T>(
payload.passkey_call.account_id.clone(),
call.clone(),
);
let tx_payment_validity = tx_charge.validate()?;
let valid_tx = valid_tx
.combine_with(signature_validity)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

.combine_with(nonce_validity)
.combine_with(tx_payment_validity);
Ok(valid_tx)
}

fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> {
Self::validate_unsigned(TransactionSource::InBlock, call)?;

let payload = Self::filter_valid_calls(&call)?;
Self::validate_signatures(&payload)?;
let nonce_check = PasskeyNonce::new(payload.passkey_call.clone());
nonce_check.pre_dispatch()
nonce_check.pre_dispatch()?;
let tx_charge = ChargeTransactionPayment::<T>(
payload.passkey_call.account_id.clone(),
saraswatpuneet marked this conversation as resolved.
Show resolved Hide resolved
call.clone(),
);
tx_charge.pre_dispatch()
aramikm marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

impl<T: Config> Pallet<T> {
impl<T: Config> Pallet<T>
where
<T as frame_system::Config>::RuntimeCall: From<Call<T>> + Dispatchable<Info = DispatchInfo>,
{
fn filter_valid_calls(call: &Call<T>) -> Result<PasskeyPayload<T>, TransactionValidityError> {
match call {
Call::proxy { payload }
Expand Down Expand Up @@ -232,3 +265,67 @@ where
passkey_nonce.pre_dispatch(&who, &some_call.clone().into(), info, 0usize)
}
}

/// Passkey related tx payment
#[derive(Encode, Decode, Clone, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct ChargeTransactionPayment<T: Config>(pub T::AccountId, pub Call<T>);

impl<T: Config> ChargeTransactionPayment<T>
where
<T as frame_system::Config>::RuntimeCall:
From<Call<T>> + Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
{
/// Validates the transaction fee paid with tokens.
pub fn pre_dispatch(&self) -> Result<(), TransactionValidityError> {
let info = &self.1.get_dispatch_info();
let len = self.0.using_encoded(|c| c.len());
let runtime_call: <T as frame_system::Config>::RuntimeCall =
<T as frame_system::Config>::RuntimeCall::from(self.1.clone());
let who = self.0.clone();
self.withdraw_token_fee(&who, &runtime_call, info, len, Zero::zero())?;
saraswatpuneet marked this conversation as resolved.
Show resolved Hide resolved
Ok(())
}

/// Validates the transaction fee paid with tokens.
pub fn validate(&self) -> TransactionValidity {
let info = &self.1.get_dispatch_info();
let len = self.0.using_encoded(|c| c.len());
let runtime_call: <T as frame_system::Config>::RuntimeCall =
saraswatpuneet marked this conversation as resolved.
Show resolved Hide resolved
<T as frame_system::Config>::RuntimeCall::from(self.1.clone());
let who = self.0.clone();
let fee = self.withdraw_token_fee(&who, &runtime_call, info, len, Zero::zero())?;

let priority = pallet_transaction_payment::ChargeTransactionPayment::<T>::get_priority(
info,
len,
Zero::zero(),
fee,
);

Ok(ValidTransaction { priority, ..Default::default() })
}

/// Withdraws transaction fee paid with tokens.
/// # Arguments
/// * `who` - The account id of the payer
/// * `call` - The call
/// # Return
/// * `Ok((fee, initial_payment))` if the fee is successfully withdrawn
/// * `Err(InvalidTransaction::Payment)` if the fee cannot be withdrawn
fn withdraw_token_fee(
&self,
who: &T::AccountId,
call: &<T as frame_system::Config>::RuntimeCall,
info: &DispatchInfoOf<<T as frame_system::Config>::RuntimeCall>,
len: usize,
tip: BalanceOf<T>,
) -> Result<BalanceOf<T>, TransactionValidityError> {
let fee = pallet_transaction_payment::Pallet::<T>::compute_fee(len as u32, info, tip);
<OnChargeTransactionOf<T> as OnChargeTransaction<T>>::withdraw_fee(
who, call, info, fee, tip,
)
.map(|_| (fee))
.map_err(|_| -> TransactionValidityError { InvalidTransaction::Payment.into() })
}
}
48 changes: 43 additions & 5 deletions pallets/passkey/src/mock.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,53 @@
//! Mocks for the Passkey module.
use crate as pallet_passkey;
use crate::*;
use frame_support::{
construct_runtime,
construct_runtime, parameter_types,
traits::{ConstU32, ConstU64, Contains, Everything},
weights::WeightToFee as WeightToFeeTrait,
};
use sp_core::H256;
use pallet_transaction_payment::CurrencyAdapter;
use sp_core::{ConstU8, H256};
use sp_runtime::{
traits::{ConvertInto, IdentityLookup},
BuildStorage,
BuildStorage, SaturatedConversion,
};

use crate as pallet_passkey;

use common_primitives::node::AccountId;

type Block = frame_system::mocking::MockBlockU32<Test>;

// Needs parameter_types! for the impls below
parameter_types! {
pub static WeightToFee: u64 = 1;
pub static TransactionByteFee: u64 = 1;
}

impl WeightToFeeTrait for WeightToFee {
type Balance = u64;

fn weight_to_fee(weight: &Weight) -> Self::Balance {
Self::Balance::saturated_from(weight.ref_time())
.saturating_mul(WEIGHT_TO_FEE.with(|v| *v.borrow()))
}
}

impl WeightToFeeTrait for TransactionByteFee {
type Balance = u64;

fn weight_to_fee(weight: &Weight) -> Self::Balance {
Self::Balance::saturated_from(weight.ref_time())
.saturating_mul(TRANSACTION_BYTE_FEE.with(|v| *v.borrow()))
}
}

construct_runtime!(
pub enum Test
{
System: frame_system::{Pallet, Call, Storage, Config<T>, Event<T>},
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
Passkey: pallet_passkey::{Pallet, Storage, Call, Event<T>, ValidateUnsigned},
TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event<T>},
}
);

Expand Down Expand Up @@ -51,12 +78,23 @@ impl frame_system::Config for Test {
type MaxConsumers = ConstU32<16>;
}

impl pallet_transaction_payment::Config for Test {
type RuntimeEvent = RuntimeEvent;
type OnChargeTransaction = CurrencyAdapter<Balances, ()>;
type WeightToFee = WeightToFee;
type LengthToFee = TransactionByteFee;
type FeeMultiplierUpdate = ();
type OperationalFeeMultiplier = ConstU8<5>;
}

impl pallet_passkey::Config for Test {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type RuntimeCall = RuntimeCall;
type ConvertIntoAccountId32 = ConvertInto;
type PasskeyCallFilter = MockPasskeyCallFilter;
#[cfg(feature = "runtime-benchmarks")]
type Currency = Balances;
}

impl pallet_balances::Config for Test {
Expand Down
Loading