diff --git a/CHANGELOG.md b/CHANGELOG.md index 56b776d2ff..4115114337 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### 2026-01-23 +- Compute base blob fee once per block [#6006](https://github.com/lambdaclass/ethrex/pull/6006) - Reuse cache in prewarm workers [#5999](https://github.com/lambdaclass/ethrex/pull/5999) ### 2026-01-21 diff --git a/crates/common/types/block.rs b/crates/common/types/block.rs index e1b36065a2..7797635a1a 100644 --- a/crates/common/types/block.rs +++ b/crates/common/types/block.rs @@ -437,6 +437,7 @@ pub fn calculate_base_fee_per_blob_gas(parent_excess_blob_gas: u64, update_fract if update_fraction == 0 { return U256::zero(); } + fake_exponential( U256::from(MIN_BASE_FEE_PER_BLOB_GAS), U256::from(parent_excess_blob_gas), diff --git a/crates/vm/backends/levm/mod.rs b/crates/vm/backends/levm/mod.rs index 18b0116a33..f50c4c7754 100644 --- a/crates/vm/backends/levm/mod.rs +++ b/crates/vm/backends/levm/mod.rs @@ -58,6 +58,12 @@ impl LEVM { ) -> Result { Self::prepare_block(block, db, vm_type)?; + // Compute base blob fee once for the entire block + let chain_config = db.store.get_chain_config()?; + let config = EVMConfig::new_from_chain_config(&chain_config, &block.header); + let block_excess_blob_gas = block.header.excess_blob_gas.map(U256::from); + let base_blob_fee_per_gas = get_base_fee_per_blob_gas(block_excess_blob_gas, &config)?; + let mut receipts = Vec::new(); let mut cumulative_gas_used = 0; @@ -72,7 +78,14 @@ impl LEVM { ))); } - let report = Self::execute_tx(tx, tx_sender, &block.header, db, vm_type)?; + let report = Self::execute_tx( + tx, + tx_sender, + &block.header, + db, + vm_type, + base_blob_fee_per_gas, + )?; cumulative_gas_used += report.gas_used; let receipt = Receipt::new( @@ -109,6 +122,12 @@ impl LEVM { ) -> Result { Self::prepare_block(block, db, vm_type)?; + // Compute base blob fee once for the entire block + let chain_config = db.store.get_chain_config()?; + let config = EVMConfig::new_from_chain_config(&chain_config, &block.header); + let block_excess_blob_gas = block.header.excess_blob_gas.map(U256::from); + let base_blob_fee_per_gas = get_base_fee_per_blob_gas(block_excess_blob_gas, &config)?; + let mut shared_stack_pool = Vec::with_capacity(STACK_LIMIT); let mut receipts = Vec::new(); @@ -135,6 +154,7 @@ impl LEVM { &block.header, db, vm_type, + base_blob_fee_per_gas, &mut shared_stack_pool, )?; if queue_length.load(Ordering::Relaxed) == 0 && tx_since_last_flush > 5 { @@ -208,6 +228,12 @@ impl LEVM { ) -> Result<(), EvmError> { let mut db = GeneralizedDatabase::new(store.clone()); + // Compute base blob fee once for the entire block + let chain_config = db.store.get_chain_config()?; + let config = EVMConfig::new_from_chain_config(&chain_config, &block.header); + let block_excess_blob_gas = block.header.excess_blob_gas.map(U256::from); + let base_blob_fee_per_gas = get_base_fee_per_blob_gas(block_excess_blob_gas, &config)?; + block .body .get_transactions_with_sender() @@ -226,6 +252,7 @@ impl LEVM { &block.header, &mut db, vm_type, + base_blob_fee_per_gas, stack_pool, ); }, @@ -267,6 +294,7 @@ impl LEVM { block_header: &BlockHeader, db: &GeneralizedDatabase, vm_type: VMType, + base_blob_fee_per_gas: U256, ) -> Result { let chain_config = db.store.get_chain_config()?; let gas_price: U256 = calculate_gas_price_for_tx( @@ -287,7 +315,7 @@ impl LEVM { prev_randao: Some(block_header.prev_randao), chain_id: chain_config.chain_id.into(), base_fee_per_gas: block_header.base_fee_per_gas.unwrap_or_default().into(), - base_blob_fee_per_gas: get_base_fee_per_blob_gas(block_excess_blob_gas, &config)?, + base_blob_fee_per_gas, gas_price, block_excess_blob_gas, block_blob_gas_used: block_header.blob_gas_used.map(U256::from), @@ -314,8 +342,16 @@ impl LEVM { block_header: &BlockHeader, db: &mut GeneralizedDatabase, vm_type: VMType, + base_blob_fee_per_gas: U256, ) -> Result { - let env = Self::setup_env(tx, tx_sender, block_header, db, vm_type)?; + let env = Self::setup_env( + tx, + tx_sender, + block_header, + db, + vm_type, + base_blob_fee_per_gas, + )?; let mut vm = VM::new(env, db, tx, LevmCallTracer::disabled(), vm_type)?; vm.execute().map_err(VMError::into) @@ -331,9 +367,17 @@ impl LEVM { block_header: &BlockHeader, db: &mut GeneralizedDatabase, vm_type: VMType, + base_blob_fee_per_gas: U256, stack_pool: &mut Vec, ) -> Result { - let env = Self::setup_env(tx, tx_sender, block_header, db, vm_type)?; + let env = Self::setup_env( + tx, + tx_sender, + block_header, + db, + vm_type, + base_blob_fee_per_gas, + )?; let mut vm = VM::new(env, db, tx, LevmCallTracer::disabled(), vm_type)?; std::mem::swap(&mut vm.stack_pool, stack_pool); diff --git a/crates/vm/backends/levm/tracing.rs b/crates/vm/backends/levm/tracing.rs index 8a0fe0b927..d6a7ffb38c 100644 --- a/crates/vm/backends/levm/tracing.rs +++ b/crates/vm/backends/levm/tracing.rs @@ -1,7 +1,8 @@ use ethrex_common::types::{Block, Transaction}; -use ethrex_common::{tracing::CallTrace, types::BlockHeader}; +use ethrex_common::{U256, tracing::CallTrace, types::BlockHeader}; +use ethrex_levm::utils::get_base_fee_per_blob_gas; use ethrex_levm::vm::VMType; -use ethrex_levm::{db::gen_db::GeneralizedDatabase, tracing::LevmCallTracer, vm::VM}; +use ethrex_levm::{EVMConfig, db::gen_db::GeneralizedDatabase, tracing::LevmCallTracer, vm::VM}; use crate::{EvmError, backends::levm::LEVM}; @@ -16,6 +17,12 @@ impl LEVM { ) -> Result<(), EvmError> { Self::prepare_block(block, db, vm_type)?; + // Compute base blob fee once for the entire block + let chain_config = db.store.get_chain_config()?; + let config = EVMConfig::new_from_chain_config(&chain_config, &block.header); + let block_excess_blob_gas = block.header.excess_blob_gas.map(U256::from); + let base_blob_fee_per_gas = get_base_fee_per_blob_gas(block_excess_blob_gas, &config)?; + // Executes transactions and stops when the index matches the stop index. for (index, (tx, sender)) in block .body @@ -28,7 +35,14 @@ impl LEVM { break; } - Self::execute_tx(tx, sender, &block.header, db, vm_type)?; + Self::execute_tx( + tx, + sender, + &block.header, + db, + vm_type, + base_blob_fee_per_gas, + )?; } // Process withdrawals only if the whole block has been executed. @@ -50,6 +64,12 @@ impl LEVM { with_log: bool, vm_type: VMType, ) -> Result { + // Compute base blob fee per gas + let chain_config = db.store.get_chain_config()?; + let config = EVMConfig::new_from_chain_config(&chain_config, block_header); + let block_excess_blob_gas = block_header.excess_blob_gas.map(U256::from); + let base_blob_fee_per_gas = get_base_fee_per_blob_gas(block_excess_blob_gas, &config)?; + let env = Self::setup_env( tx, tx.sender().map_err(|error| { @@ -58,6 +78,7 @@ impl LEVM { block_header, db, vm_type, + base_blob_fee_per_gas, )?; let mut vm = VM::new( env, diff --git a/crates/vm/backends/mod.rs b/crates/vm/backends/mod.rs index 31acf3f1c8..b5f1bcf1d4 100644 --- a/crates/vm/backends/mod.rs +++ b/crates/vm/backends/mod.rs @@ -9,11 +9,12 @@ use ethrex_common::types::{ AccessList, AccountUpdate, Block, BlockHeader, Fork, GenericTransaction, Receipt, Transaction, Withdrawal, }; -use ethrex_common::{Address, types::fee_config::FeeConfig}; +use ethrex_common::{Address, U256, types::fee_config::FeeConfig}; pub use ethrex_levm::call_frame::CallFrameBackup; use ethrex_levm::db::gen_db::GeneralizedDatabase; pub use ethrex_levm::db::{CachingDatabase, Database as LevmDatabase}; use ethrex_levm::vm::VMType; +use ethrex_levm::{EVMConfig, utils::get_base_fee_per_blob_gas}; use std::sync::Arc; use std::sync::atomic::AtomicUsize; use std::sync::mpsc::Sender; @@ -102,8 +103,20 @@ impl Evm { remaining_gas: &mut u64, sender: Address, ) -> Result<(Receipt, u64), EvmError> { - let execution_report = - LEVM::execute_tx(tx, sender, block_header, &mut self.db, self.vm_type)?; + // Compute base blob fee per gas + let chain_config = self.db.store.get_chain_config()?; + let config = EVMConfig::new_from_chain_config(&chain_config, block_header); + let block_excess_blob_gas = block_header.excess_blob_gas.map(U256::from); + let base_blob_fee_per_gas = get_base_fee_per_blob_gas(block_excess_blob_gas, &config)?; + + let execution_report = LEVM::execute_tx( + tx, + sender, + block_header, + &mut self.db, + self.vm_type, + base_blob_fee_per_gas, + )?; *remaining_gas = remaining_gas.saturating_sub(execution_report.gas_used); diff --git a/crates/vm/levm/src/hooks/default_hook.rs b/crates/vm/levm/src/hooks/default_hook.rs index 01fb450154..2cac76cd30 100644 --- a/crates/vm/levm/src/hooks/default_hook.rs +++ b/crates/vm/levm/src/hooks/default_hook.rs @@ -457,11 +457,8 @@ pub fn deduct_caller( // Up front cost is the maximum amount of wei that a user is willing to pay for. Gaslimit * gasprice + value + blob_gas_cost let value = vm.current_call_frame.msg_value; - let blob_gas_cost = calculate_blob_gas_cost( - &vm.env.tx_blob_hashes, - vm.env.block_excess_blob_gas, - &vm.env.config, - )?; + let blob_gas_cost = + calculate_blob_gas_cost(&vm.env.tx_blob_hashes, vm.env.base_blob_fee_per_gas)?; // The real cost to deduct is calculated as effective_gas_price * gas_limit + value + blob_gas_cost let up_front_cost = gas_limit_price_product diff --git a/crates/vm/levm/src/utils.rs b/crates/vm/levm/src/utils.rs index a4fc4914bb..e7b5397200 100644 --- a/crates/vm/levm/src/utils.rs +++ b/crates/vm/levm/src/utils.rs @@ -102,9 +102,11 @@ pub fn get_base_fee_per_blob_gas( evm_config: &EVMConfig, ) -> Result { let base_fee_update_fraction = evm_config.blob_schedule.base_fee_update_fraction; + let excess_blob_gas = block_excess_blob_gas.unwrap_or_default(); + fake_exponential( MIN_BASE_FEE_PER_BLOB_GAS, - block_excess_blob_gas.unwrap_or_default(), + excess_blob_gas, base_fee_update_fraction, ) .map_err(|err| VMError::Internal(InternalError::FakeExponentialError(err))) @@ -135,8 +137,7 @@ pub fn get_max_blob_gas_price( /// Calculate the actual blob gas cost. pub fn calculate_blob_gas_cost( tx_blob_hashes: &[H256], - block_excess_blob_gas: Option, - evm_config: &EVMConfig, + base_blob_fee_per_gas: U256, ) -> Result { let blobhash_amount: u64 = tx_blob_hashes .len() @@ -147,11 +148,9 @@ pub fn calculate_blob_gas_cost( .checked_mul(BLOB_GAS_PER_BLOB) .unwrap_or_default(); - let base_fee_per_blob_gas = get_base_fee_per_blob_gas(block_excess_blob_gas, evm_config)?; - let blob_gas_used: U256 = blob_gas_used.into(); let blob_fee: U256 = blob_gas_used - .checked_mul(base_fee_per_blob_gas) + .checked_mul(base_blob_fee_per_gas) .ok_or(InternalError::Overflow)?; Ok(blob_fee)