diff --git a/etherlink/kernel_evm/ethereum/src/tx_common.rs b/etherlink/kernel_evm/ethereum/src/tx_common.rs index c26948f187b2d0cbcd08dac80d21335244eb3785..75ac5e06463b678b9889d1f413f84b452b8ee372 100644 --- a/etherlink/kernel_evm/ethereum/src/tx_common.rs +++ b/etherlink/kernel_evm/ethereum/src/tx_common.rs @@ -19,6 +19,7 @@ use thiserror::Error; use crate::{ access_list::AccessList, + block::BlockFees, rlp_helpers::{ append_h256, append_option, append_vec, decode_field, decode_field_h256, decode_list, decode_option, next, @@ -69,13 +70,16 @@ pub struct EthereumTransactionCommon { /// A scalar value equal to the number of transactions sent by the sender pub nonce: U256, - /// Amount of fee to be paid per gas in addition - /// to the base_fee_per_gas in order - /// to incentivize miners to include the transaction. - /// base_fee_per_gas is re-evaluated every block - /// from the actual usage of gas in the previous block. + /// Normally, this would be a fee paid per gas in addition to base fee per gas. + /// This would incentivise miners to include the transaction. /// More details see here https://eips.ethereum.org/EIPS/eip-1559#abstract - pub max_priority_fee_per_gas: U256, + /// + /// We choose to ignore this, however, as we actually do not implement eip-1559 + /// mechanism exactly. The sequencer is compensated via the data availability fee + /// and gas fee. + /// + /// We keep this field purely for compatibility with existing ethereum tooling. + max_priority_fee_per_gas: U256, /// Maximum amount of fee to be paid per gas. /// Thus, as a transaction might be included in the block /// with higher base_fee_per_gas then one @@ -551,22 +555,21 @@ impl EthereumTransactionCommon { } } - /// Returns effective_gas_price of the transaction. + /// Returns overall_gas_price of the transaction. /// - /// For more details see [eip-1559](https://eips.ethereum.org/EIPS/eip-1559#specification). - pub fn effective_gas_price( + /// *NB* this is not the gas price used _for execution_, but rather the gas price that + /// should be reported in the transaction receipt. + pub fn overall_gas_price( &self, - block_base_fee_per_gas: U256, + block_fees: &BlockFees, ) -> Result { - let priority_fee_per_gas = U256::min( - self.max_priority_fee_per_gas, - self.max_fee_per_gas - .checked_sub(block_base_fee_per_gas) - .ok_or_else(|| anyhow::anyhow!("Underflow when calculating gas price"))?, - ); - priority_fee_per_gas - .checked_add(block_base_fee_per_gas) - .ok_or_else(|| anyhow::anyhow!("Overflow")) + let block_base_fee_per_gas = block_fees.base_fee_per_gas(); + + if self.max_fee_per_gas >= block_base_fee_per_gas { + Ok(block_base_fee_per_gas) + } else { + Err(anyhow::anyhow!("Underflow when calculating gas price")) + } } /// Returns the gas limit for executing this transaction. diff --git a/etherlink/kernel_evm/kernel/src/apply.rs b/etherlink/kernel_evm/kernel/src/apply.rs index f854e222beaa9c2f741feae8291d2d08a29fb59b..50eca9813d58211f9556d41c799500dadb5c81bf 100644 --- a/etherlink/kernel_evm/kernel/src/apply.rs +++ b/etherlink/kernel_evm/kernel/src/apply.rs @@ -15,7 +15,7 @@ use evm_execution::precompiles::PrecompileBTreeMap; use evm_execution::{run_transaction, EthereumError}; use primitive_types::{H160, U256}; use tezos_data_encoding::enc::BinWriter; -use tezos_ethereum::block::BlockConstants; +use tezos_ethereum::block::{BlockConstants, BlockFees}; use tezos_ethereum::transaction::TransactionHash; use tezos_ethereum::tx_common::EthereumTransactionCommon; use tezos_ethereum::tx_signature::TxSignature; @@ -57,13 +57,14 @@ impl Transaction { } } - // This function returns effective_gas_price - // For more details see the first paragraph here https://eips.ethereum.org/EIPS/eip-1559#specification - fn gas_price(&self, block_base_fee_per_gas: U256) -> Result { + // This function returns effective_gas_price of the transaction. + // + // This includes both the gas paid for execution, and for the additional flat & data-availability fees. + fn overall_gas_price(&self, block_fees: &BlockFees) -> Result { match &self.content { TransactionContent::Deposit(_) => Ok(U256::zero()), TransactionContent::Ethereum(transaction) => { - transaction.effective_gas_price(block_base_fee_per_gas) + transaction.overall_gas_price(block_fees) } } } @@ -99,6 +100,7 @@ pub struct TransactionReceiptInfo { pub effective_gas_price: U256, } +#[derive(Debug)] pub struct TransactionObjectInfo { pub from: H160, pub gas_used: U256, @@ -137,12 +139,12 @@ fn make_object_info( from: H160, index: u32, gas_used: U256, - block_base_fee_per_gas: U256, + block_fees: &BlockFees, ) -> Result { Ok(TransactionObjectInfo { from, gas_used, - gas_price: transaction.gas_price(block_base_fee_per_gas)?, + gas_price: transaction.overall_gas_price(block_fees)?, hash: transaction.tx_hash, input: transaction.data(), nonce: transaction.nonce(), @@ -255,12 +257,8 @@ fn is_valid_ethereum_transaction_common( return Ok(Validity::InvalidCode); } - // EIP 1559 checks // ensure that the user was willing to at least pay the base fee - // and that max is greater than both fees - if transaction.max_fee_per_gas < block_constant.base_fee_per_gas() - || transaction.max_fee_per_gas < transaction.max_priority_fee_per_gas - { + if transaction.max_fee_per_gas < block_constant.base_fee_per_gas() { log!(host, Debug, "Transaction status: ERROR_MAX_BASE_FEE"); return Ok(Validity::InvalidMaxBaseFee); } @@ -283,8 +281,7 @@ fn apply_ethereum_transaction_common( transaction: &EthereumTransactionCommon, allocated_ticks: u64, ) -> Result, anyhow::Error> { - let effective_gas_price = - transaction.effective_gas_price(block_constants.base_fee_per_gas())?; + let effective_gas_price = block_constants.base_fee_per_gas(); let caller = match is_valid_ethereum_transaction_common( host, evm_account_storage, @@ -528,7 +525,7 @@ pub fn apply_transaction( caller, index, gas_used, - block_constants.base_fee_per_gas(), + &block_constants.block_fees, )?; let receipt_info = make_receipt_info( @@ -825,40 +822,6 @@ mod tests { ); } - #[test] - fn test_tx_is_invalid_max_fee_less_than_priority_fee() { - let mut host = MockHost::default(); - let mut evm_account_storage = - evm_execution::account_storage::init_account_storage().unwrap(); - let block_constants = mock_block_constants(); - - // setup - let address = address_from_str("af1276cbb260bb13deddb4209ae99ae6e497f446"); - let gas_price = U256::from(21000); - let balance = U256::from(21000) * gas_price; - let mut transaction = valid_tx(); - // set a max_priority_fee bigger than, - transaction.max_priority_fee_per_gas = U256::from(22000); - transaction = resign(transaction); - - // fund account - set_balance(&mut host, &mut evm_account_storage, &address, balance); - - // act - let res = is_valid_ethereum_transaction_common( - &mut host, - &mut evm_account_storage, - &transaction, - &block_constants, - gas_price, - ); - assert_eq!( - Validity::InvalidMaxBaseFee, - res.expect("Verification should not have raise an error"), - "Transaction should have been rejected" - ); - } - #[test] // when the user specify a max fee per gas lower than base fee, // the function should fail gracefully @@ -880,12 +843,14 @@ mod tests { )), }; + let block_fees = BlockFees::new(U256::from(9)); + let obj = make_object_info( &transaction, H160::zero(), 0u32, U256::from(21_000), - U256::from(9), + &block_fees, ); assert!(obj.is_err()) } diff --git a/etherlink/kernel_evm/kernel/src/block.rs b/etherlink/kernel_evm/kernel/src/block.rs index 87bb9ef8385ec65a170a2288c63eb5277dde69d5..37612dee04ea3c763cfc2232f06ac5510de96ec9 100644 --- a/etherlink/kernel_evm/kernel/src/block.rs +++ b/etherlink/kernel_evm/kernel/src/block.rs @@ -850,12 +850,13 @@ mod tests { ); let sender = dummy_eth_caller(); + let initial_sender_balance = U256::from(10000000000000000000u64); let mut evm_account_storage = init_account_storage().unwrap(); set_balance( &mut host, &mut evm_account_storage, &sender, - U256::from(10000000000000000000u64), + initial_sender_balance, ); produce( @@ -872,8 +873,14 @@ mod tests { let dest_balance = get_balance(&mut host, &mut evm_account_storage, &dest_address); - assert_eq!(sender_balance, U256::from(9999999159500000000u64)); - assert_eq!(dest_balance, U256::from(500000000u64)) + let expected_dest_balance = U256::from(500000000u64); + let expected_gas = 21000; + let expected_fees = dummy_block_fees().base_fee_per_gas() * expected_gas; + let expected_sender_balance = + initial_sender_balance - expected_dest_balance - expected_fees; + + assert_eq!(dest_balance, expected_dest_balance); + assert_eq!(sender_balance, expected_sender_balance); } #[test] diff --git a/etherlink/kernel_evm/kernel/src/simulation.rs b/etherlink/kernel_evm/kernel/src/simulation.rs index 47091e48ea6df447d1a52967fef729827e914e3b..abbeb31dfe0b58e0725271804aa0c65270f932ab 100644 --- a/etherlink/kernel_evm/kernel/src/simulation.rs +++ b/etherlink/kernel_evm/kernel/src/simulation.rs @@ -222,8 +222,7 @@ impl TxValidation { tx_data_size, ); - let gas_price = if let Ok(gas_price) = - transaction.effective_gas_price(block_fees.base_fee_per_gas()) + let gas_price = if let Ok(gas_price) = transaction.overall_gas_price(&block_fees) { gas_price } else { @@ -284,9 +283,7 @@ impl TxValidation { return Ok(TxValidationOutcome::OutOfTicks); } // Check if the gas price is high enough - if tx.max_fee_per_gas < block_fees.base_fee_per_gas() - || tx.max_fee_per_gas < tx.max_priority_fee_per_gas - { + if tx.max_fee_per_gas < block_fees.base_fee_per_gas() { return Ok(TxValidationOutcome::MaxGasFeeTooLow); } // TODO: #6498