Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions crates/common/types/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
52 changes: 48 additions & 4 deletions crates/vm/backends/levm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ impl LEVM {
) -> Result<BlockExecutionResult, 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)?;

let mut receipts = Vec::new();
let mut cumulative_gas_used = 0;

Expand All @@ -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(
Expand Down Expand Up @@ -109,6 +122,12 @@ impl LEVM {
) -> Result<BlockExecutionResult, 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)?;

let mut shared_stack_pool = Vec::with_capacity(STACK_LIMIT);

let mut receipts = Vec::new();
Expand All @@ -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 {
Expand Down Expand Up @@ -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()
Expand All @@ -226,6 +252,7 @@ impl LEVM {
&block.header,
&mut db,
vm_type,
base_blob_fee_per_gas,
stack_pool,
);
},
Expand Down Expand Up @@ -267,6 +294,7 @@ impl LEVM {
block_header: &BlockHeader,
db: &GeneralizedDatabase,
vm_type: VMType,
base_blob_fee_per_gas: U256,
) -> Result<Environment, EvmError> {
let chain_config = db.store.get_chain_config()?;
let gas_price: U256 = calculate_gas_price_for_tx(
Expand All @@ -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),
Expand All @@ -314,8 +342,16 @@ impl LEVM {
block_header: &BlockHeader,
db: &mut GeneralizedDatabase,
vm_type: VMType,
base_blob_fee_per_gas: U256,
) -> Result<ExecutionReport, EvmError> {
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)
Expand All @@ -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<Stack>,
) -> Result<ExecutionReport, EvmError> {
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);
Expand Down
27 changes: 24 additions & 3 deletions crates/vm/backends/levm/tracing.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand All @@ -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
Expand All @@ -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.
Expand All @@ -50,6 +64,12 @@ impl LEVM {
with_log: bool,
vm_type: VMType,
) -> Result<CallTrace, EvmError> {
// 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| {
Expand All @@ -58,6 +78,7 @@ impl LEVM {
block_header,
db,
vm_type,
base_blob_fee_per_gas,
)?;
let mut vm = VM::new(
env,
Expand Down
19 changes: 16 additions & 3 deletions crates/vm/backends/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down
7 changes: 2 additions & 5 deletions crates/vm/levm/src/hooks/default_hook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 5 additions & 6 deletions crates/vm/levm/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,11 @@ pub fn get_base_fee_per_blob_gas(
evm_config: &EVMConfig,
) -> Result<U256, VMError> {
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)))
Expand Down Expand Up @@ -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<U256>,
evm_config: &EVMConfig,
base_blob_fee_per_gas: U256,
) -> Result<U256, VMError> {
let blobhash_amount: u64 = tx_blob_hashes
.len()
Expand All @@ -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)
Expand Down
Loading