From 71150e7c9b752b801e7dec283831c5f0033f19c9 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Mon, 02 Oct 2023 22:07:35 +0000 Subject: [PATCH] feat: use new ethereum sponsoring interface --- --- a/pallets/evm-transaction-payment/src/lib.rs +++ b/pallets/evm-transaction-payment/src/lib.rs @@ -60,29 +60,62 @@ pub struct Pallet(_); } -/// Implements [`fp_evm::TransactionValidityHack`], which provides sponsor address to pallet-evm -pub struct TransactionValidityHack(PhantomData<*const T>); -impl fp_evm::TransactionValidityHack for TransactionValidityHack { - fn who_pays_fee( - origin: H160, - max_fee: U256, - reason: &WithdrawReason, - ) -> Option { - match reason { - WithdrawReason::Call { target, input } => { - let origin_sub = T::CrossAccountId::from_eth(origin); - let call_context = CallContext { - contract_address: *target, - input: input.clone(), - max_fee, - }; - T::EvmSponsorshipHandler::get_sponsor(&origin_sub, &call_context) - } - _ => None, +fn who_pays_fee( + origin: H160, + max_fee: U256, + reason: &WithdrawReason, +) -> Option { + match reason { + WithdrawReason::Call { target, input, .. } => { + let origin_sub = T::CrossAccountId::from_eth(origin); + let call_context = CallContext { + contract_address: *target, + input: input.clone(), + max_fee, + }; + T::EvmSponsorshipHandler::get_sponsor(&origin_sub, &call_context) } + _ => None, } } +fn get_sponsor( + source: H160, + max_fee_per_gas: Option, + gas_limit: u64, + reason: &WithdrawReason, + is_transactional: bool, + is_check: bool, +) -> Option { + let accept_gas_fee = |gas_fee| { + let (base_fee, _) = T::FeeCalculator::min_gas_price(); + base_fee <= gas_fee && gas_fee <= base_fee * 21 / 10 + }; + let (max_fee_per_gas, may_sponsor) = match (max_fee_per_gas, is_transactional) { + (Some(max_fee_per_gas), _) => (max_fee_per_gas, accept_gas_fee(max_fee_per_gas)), + // Gas price check is skipped for non-transactional calls that don't + // define a `max_fee_per_gas` input. + (None, false) => (Default::default(), true), + _ => return None, + }; + + let max_fee = max_fee_per_gas.saturating_mul(gas_limit.into()); + + // #[cfg(feature = "debug-logging")] + // log::trace!(target: "sponsoring", "checking who will pay fee for {:?} {:?}", source, reason); + with_transaction(|| { + let result = may_sponsor + .then(|| who_pays_fee::(source, max_fee, reason)) + .flatten(); + if is_check { + TransactionOutcome::Rollback(Ok::<_, DispatchError>(result)) + } else { + TransactionOutcome::Commit(Ok(result)) + } + }) + .ok() + .flatten() +} /// Implements sponsoring for evm calls performed from pallet-evm (via api.tx.ethereum.transact/api.tx.evm.call) pub struct BridgeSponsorshipHandler(PhantomData); impl SponsorshipHandler for BridgeSponsorshipHandler @@ -127,3 +160,135 @@ } } } + +/// Set transaction sponsor if available and enough balance. +pub struct TransactionValidity(PhantomData); +impl OnCheckEvmTransaction for TransactionValidity { + fn on_check_evm_transaction( + v: &mut CheckEvmTransaction, + origin: &T::CrossAccountId, + ) -> Result<(), TransactionValidationError> { + let who = &v.who; + let max_fee_per_gas = Some(v.transaction_fee_input()?.0); + let gas_limit = v.transaction.gas_limit.low_u64(); + let reason = if let Some(to) = v.transaction.to { + WithdrawReason::Call { + target: to, + input: v.transaction.input.clone(), + max_fee_per_gas, + gas_limit, + is_transactional: v.config.is_transactional, + is_check: true, + } + } else { + WithdrawReason::Create + }; + let sponsor = get_sponsor::( + *origin.as_eth(), + max_fee_per_gas, + gas_limit, + &reason, + v.config.is_transactional, + true, + ) + .as_ref() + .map(pallet_evm::Pallet::::account_basic_by_id) + .map(|v| v.0); + let fee = max_fee_per_gas + .unwrap() + .saturating_mul(v.transaction.gas_limit); + if let Some(sponsor) = sponsor.as_ref() { + if who.balance < v.transaction.value || sponsor.balance < fee { + return Err(TransactionValidationError::BalanceTooLow.into()); + } + } else { + let total_payment = v.transaction.value.saturating_add(fee); + if who.balance < total_payment { + return Err(TransactionValidationError::BalanceTooLow.into()); + } + } + + let who = sponsor.unwrap_or_else(|| v.who.clone()); + v.who.balance = who.balance; + Ok(()) + } +} + +/// Implements the transaction payment for a pallet implementing the `Currency` +/// trait (eg. the pallet_balances) using an unbalance handler (implementing +/// `OnUnbalanced`). +/// Similar to `CurrencyAdapter` of `pallet_transaction_payment` +pub struct WrappedEVMCurrencyAdapter(sp_std::marker::PhantomData<(C, OU)>); +impl OnChargeEVMTransaction for WrappedEVMCurrencyAdapter +where + T: Config, + C: Currency<::AccountId>, + C::PositiveImbalance: Imbalance< + ::AccountId>>::Balance, + Opposite = C::NegativeImbalance, + >, + C::NegativeImbalance: Imbalance< + ::AccountId>>::Balance, + Opposite = C::PositiveImbalance, + >, + OU: OnUnbalanced>, + U256: UniqueSaturatedInto<::AccountId>>::Balance>, +{ + // Kept type as Option to satisfy bound of Default + type LiquidityInfo = (Option>, Option); + + fn withdraw_fee( + who: &T::CrossAccountId, + reason: WithdrawReason, + fee: U256, + ) -> Result> { + let sponsor = match reason { + WithdrawReason::Call { + max_fee_per_gas, + gas_limit, + is_transactional, + is_check, + .. + } => get_sponsor::( + *who.as_eth(), + max_fee_per_gas, + gas_limit, + &reason, + is_transactional, + is_check, + ), + _ => None, + }; + + let who = sponsor.as_ref().unwrap_or(who); + as OnChargeEVMTransaction>::withdraw_fee( + who, reason, fee, + ) + .map(|li| (li, sponsor)) + } + + fn correct_and_deposit_fee( + who: &T::CrossAccountId, + corrected_fee: U256, + base_fee: U256, + already_withdrawn: Self::LiquidityInfo, + ) -> Self::LiquidityInfo { + let (already_withdrawn, sponsor) = already_withdrawn; + let who = sponsor.as_ref().unwrap_or(who); + ( + as OnChargeEVMTransaction>::correct_and_deposit_fee( + who, + corrected_fee, + base_fee, + already_withdrawn, + ), + None + ) + } + + fn pay_priority_fee(tip: Self::LiquidityInfo) { + as OnChargeEVMTransaction>::pay_priority_fee( + tip.0, + ) + } +} --- a/runtime/common/config/ethereum.rs +++ b/runtime/common/config/ethereum.rs @@ -89,12 +89,13 @@ type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate; type ChainId = ChainId; type Runner = pallet_evm::runner::stack::Runner; - type OnChargeTransaction = pallet_evm::EVMCurrencyAdapter; - type TransactionValidityHack = pallet_evm_transaction_payment::TransactionValidityHack; + type OnChargeTransaction = + pallet_evm_transaction_payment::WrappedEVMCurrencyAdapter; type FindAuthor = EthereumFindAuthor; type Timestamp = crate::Timestamp; type WeightInfo = pallet_evm::weights::SubstrateWeight; type GasLimitPovSizeRatio = ProofSizePerGas; + type OnCheckEvmTransaction = pallet_evm_transaction_payment::TransactionValidity; } impl pallet_evm_migration::Config for Runtime { -- gitstuff