Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
8 changes: 1 addition & 7 deletions cmd/ef_tests/state/runner/levm_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,12 @@ use ethrex_common::{
H256, U256,
};
use ethrex_levm::{
db::gen_db::GeneralizedDatabase,
errors::{ExecutionReport, TxValidationError, VMError},
vm::{EVMConfig, VM},
Environment,
db::gen_db::GeneralizedDatabase, errors::{ExecutionReport, TxValidationError, VMError}, vm::VM, EVMConfig, Environment
};
use ethrex_rlp::encode::RLPEncode;
use ethrex_storage::AccountUpdate;
use ethrex_vm::backends;
use keccak_hash::keccak;
use std::collections::HashMap;

pub async fn run_ef_test(test: &EFTest) -> Result<EFTestReport, EFTestRunnerError> {
// There are some tests that don't have a hash, unwrap will panic
Expand Down Expand Up @@ -168,7 +164,6 @@ pub fn prepare_vm_for_tx<'a>(
VM::new(
Environment {
origin: test_tx.sender,
refunded_gas: 0,
gas_limit: test_tx.gas_limit,
config,
block_number: test.env.current_number,
Expand All @@ -187,7 +182,6 @@ pub fn prepare_vm_for_tx<'a>(
tx_max_fee_per_blob_gas: test_tx.max_fee_per_blob_gas,
tx_nonce: test_tx.nonce,
block_gas_limit: test.env.current_gas_limit,
transient_storage: HashMap::new(),
is_privileged: false,
},
db,
Expand Down
8 changes: 2 additions & 6 deletions crates/vm/backends/levm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ use ethrex_common::{
};
use ethrex_levm::db::gen_db::GeneralizedDatabase;
use ethrex_levm::errors::TxValidationError;
use ethrex_levm::EVMConfig;
use ethrex_levm::{
errors::{ExecutionReport, TxResult, VMError},
vm::{EVMConfig, Substate, VM},
vm::{Substate, VM},
Environment,
};
use ethrex_storage::error::StoreError;
Expand Down Expand Up @@ -115,7 +116,6 @@ impl LEVM {
let config = EVMConfig::new_from_chain_config(&chain_config, block_header);
let env = Environment {
origin: tx_sender,
refunded_gas: 0,
gas_limit: tx.gas_limit(),
config,
block_number: block_header.number.into(),
Expand All @@ -133,7 +133,6 @@ impl LEVM {
tx_max_fee_per_blob_gas: tx.max_fee_per_blob_gas().map(U256::from),
tx_nonce: tx.nonce(),
block_gas_limit: block_header.gas_limit,
transient_storage: HashMap::new(),
difficulty: block_header.difficulty,
is_privileged: matches!(tx, Transaction::PrivilegedL2Transaction(_)),
};
Expand Down Expand Up @@ -596,7 +595,6 @@ pub fn generic_system_contract_levm(
block_excess_blob_gas: block_header.excess_blob_gas.map(U256::from),
block_blob_gas_used: block_header.blob_gas_used.map(U256::from),
block_gas_limit: 30_000_000,
transient_storage: HashMap::new(),
config,
..Default::default()
};
Expand Down Expand Up @@ -711,7 +709,6 @@ fn env_from_generic(
let config = EVMConfig::new_from_chain_config(&chain_config, header);
Ok(Environment {
origin: tx.from.0.into(),
refunded_gas: 0,
gas_limit: tx.gas.unwrap_or(header.gas_limit), // Ensure tx doesn't fail due to gas limit
config,
block_number: header.number.into(),
Expand All @@ -729,7 +726,6 @@ fn env_from_generic(
tx_max_fee_per_blob_gas: tx.max_fee_per_blob_gas,
tx_nonce: tx.nonce.unwrap_or_default(),
block_gas_limit: header.gas_limit,
transient_storage: HashMap::new(),
difficulty: header.difficulty,
is_privileged: false,
})
Expand Down
90 changes: 86 additions & 4 deletions crates/vm/levm/src/environment.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use ethrex_common::{Address, H256, U256};
use ethrex_common::{types::{BlockHeader, ChainConfig, Fork, ForkBlobSchedule}, Address, H256, U256};

use crate::vm::EVMConfig;
use crate::constants::{BLOB_BASE_FEE_UPDATE_FRACTION, BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE, MAX_BLOB_COUNT, MAX_BLOB_COUNT_ELECTRA, TARGET_BLOB_GAS_PER_BLOCK, TARGET_BLOB_GAS_PER_BLOCK_PECTRA};

use std::collections::HashMap;
/// [EIP-1153]: https://eips.ethereum.org/EIPS/eip-1153#reference-implementation
Expand All @@ -11,7 +11,6 @@ pub type TransientStorage = HashMap<(Address, U256), U256>;
pub struct Environment {
/// The sender address of the external transaction.
pub origin: Address,
pub refunded_gas: u64,
/// Gas limit of the Transaction
pub gas_limit: u64,
pub config: EVMConfig,
Expand All @@ -32,6 +31,89 @@ pub struct Environment {
pub tx_max_fee_per_blob_gas: Option<U256>,
pub tx_nonce: u64,
pub block_gas_limit: u64,
pub transient_storage: TransientStorage,
pub is_privileged: bool,
}

#[derive(Debug, Clone, Copy)]
/// This struct holds special configuration variables specific to the
/// EVM. In most cases, at least at the time of writing (February
/// 2025), you want to use the default blob_schedule values for the
/// specified Fork. The "intended" way to do this is by using the `EVMConfig::canonical_values(fork: Fork)` function.
///
/// However, that function should NOT be used IF you want to use a
/// custom `ForkBlobSchedule`, like it's described in [EIP-7840](https://eips.ethereum.org/EIPS/eip-7840)
/// Values are determined by [EIP-7691](https://eips.ethereum.org/EIPS/eip-7691#specification)
pub struct EVMConfig {
pub fork: Fork,
pub blob_schedule: ForkBlobSchedule,
}

impl EVMConfig {
pub fn new(fork: Fork, blob_schedule: ForkBlobSchedule) -> EVMConfig {
EVMConfig {
fork,
blob_schedule,
}
}

pub fn new_from_chain_config(chain_config: &ChainConfig, block_header: &BlockHeader) -> Self {
let fork = chain_config.fork(block_header.timestamp);

let blob_schedule = chain_config
.get_fork_blob_schedule(block_header.timestamp)
.unwrap_or_else(|| EVMConfig::canonical_values(fork));

EVMConfig::new(fork, blob_schedule)
}

/// This function is used for running the EF tests. If you don't
/// have acces to a EVMConfig (mainly in the form of a
/// genesis.json file) you can use this function to get the
/// "Default" ForkBlobSchedule for that specific Fork.
/// NOTE: This function could potentially be expanded to include
/// other types of "default"s.
pub fn canonical_values(fork: Fork) -> ForkBlobSchedule {
let max_blobs_per_block: u64 = Self::max_blobs_per_block(fork);
let target: u64 = Self::get_target_blob_gas_per_block_(fork);
let base_fee_update_fraction: u64 = Self::get_blob_base_fee_update_fraction_value(fork);

ForkBlobSchedule {
target,
max: max_blobs_per_block,
base_fee_update_fraction,
}
}

const fn max_blobs_per_block(fork: Fork) -> u64 {
match fork {
Fork::Prague => MAX_BLOB_COUNT_ELECTRA,
Fork::Osaka => MAX_BLOB_COUNT_ELECTRA,
_ => MAX_BLOB_COUNT,
}
}

const fn get_blob_base_fee_update_fraction_value(fork: Fork) -> u64 {
match fork {
Fork::Prague | Fork::Osaka => BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE,
_ => BLOB_BASE_FEE_UPDATE_FRACTION,
}
}

const fn get_target_blob_gas_per_block_(fork: Fork) -> u64 {
match fork {
Fork::Prague | Fork::Osaka => TARGET_BLOB_GAS_PER_BLOCK_PECTRA,
Comment thread
JereSalo marked this conversation as resolved.
_ => TARGET_BLOB_GAS_PER_BLOCK,
}
}
}

impl Default for EVMConfig {
/// The default EVMConfig depends on the default Fork.
fn default() -> Self {
let fork = core::default::Default::default();
EVMConfig {
fork,
blob_schedule: Self::canonical_values(fork),
}
}
}
16 changes: 8 additions & 8 deletions crates/vm/levm/src/execution_handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
gas_cost::CODE_DEPOSIT_COST,
opcodes::Opcode,
utils::*,
vm::{StateBackup, VM},
vm::{Substate, VM},
};

use bytes::Bytes;
Expand All @@ -14,14 +14,14 @@ impl<'a> VM<'a> {
pub fn handle_precompile_result(
&mut self,
precompile_result: Result<Bytes, VMError>,
backup: StateBackup,
backup: Substate,
current_call_frame: &mut CallFrame,
) -> Result<ExecutionReport, VMError> {
match precompile_result {
Ok(output) => Ok(ExecutionReport {
result: TxResult::Success,
gas_used: current_call_frame.gas_used,
gas_refunded: self.env.refunded_gas,
gas_refunded: self.accrued_substate.refunded_gas,
output,
logs: std::mem::take(&mut current_call_frame.logs),
}),
Expand All @@ -35,7 +35,7 @@ impl<'a> VM<'a> {
Ok(ExecutionReport {
result: TxResult::Revert(error),
gas_used: current_call_frame.gas_limit,
gas_refunded: self.env.refunded_gas,
gas_refunded: self.accrued_substate.refunded_gas,
output: Bytes::new(),
logs: vec![],
})
Expand Down Expand Up @@ -153,7 +153,7 @@ impl<'a> VM<'a> {
current_call_frame: &mut CallFrame,
) -> Result<ExecutionReport, VMError> {
let backup = self
.backups
.substate_backups
.pop()
.ok_or(VMError::Internal(InternalError::CouldNotPopCallframe))?;
// On successful create check output validity
Expand Down Expand Up @@ -204,7 +204,7 @@ impl<'a> VM<'a> {
return Ok(ExecutionReport {
result: TxResult::Revert(error),
gas_used: current_call_frame.gas_used,
gas_refunded: self.env.refunded_gas,
gas_refunded: self.accrued_substate.refunded_gas,
output: std::mem::take(&mut current_call_frame.output),
logs: vec![],
});
Expand All @@ -215,7 +215,7 @@ impl<'a> VM<'a> {
Ok(ExecutionReport {
result: TxResult::Success,
gas_used: current_call_frame.gas_used,
gas_refunded: self.env.refunded_gas,
gas_refunded: self.accrued_substate.refunded_gas,
output: std::mem::take(&mut current_call_frame.output),
logs: std::mem::take(&mut current_call_frame.logs),
})
Expand All @@ -227,7 +227,7 @@ impl<'a> VM<'a> {
current_call_frame: &mut CallFrame,
) -> Result<ExecutionReport, VMError> {
let backup = self
.backups
.substate_backups
.pop()
.ok_or(VMError::Internal(InternalError::CouldNotPopCallframe))?;
if error.should_propagate() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl<'a> VM<'a> {
let key = self.current_call_frame_mut()?.stack.pop()?;
let to = self.current_call_frame()?.to;
let value = self
.env
.accrued_substate
.transient_storage
.get(&(to, key))
.cloned()
Expand Down Expand Up @@ -63,7 +63,7 @@ impl<'a> VM<'a> {
let value = current_call_frame.stack.pop()?;
(key, value, current_call_frame.to)
};
self.env.transient_storage.insert((to, key), value);
self.accrued_substate.transient_storage.insert((to, key), value);

Ok(OpcodeResult::Continue { pc_increment: 1 })
}
Expand Down Expand Up @@ -186,7 +186,7 @@ impl<'a> VM<'a> {

// Gas Refunds
// Sync gas refund with global env, ensuring consistency accross contexts.
let mut gas_refunds = self.env.refunded_gas;
let mut gas_refunds = self.accrued_substate.refunded_gas;

// https://eips.ethereum.org/EIPS/eip-2929
let (remove_slot_cost, restore_empty_slot_cost, restore_slot_cost) = (4800, 19900, 2800);
Expand Down Expand Up @@ -224,7 +224,7 @@ impl<'a> VM<'a> {
}
}

self.env.refunded_gas = gas_refunds;
self.accrued_substate.refunded_gas = gas_refunds;

self.current_call_frame_mut()?
.increase_consumed_gas(gas_cost::sstore(
Expand Down
22 changes: 7 additions & 15 deletions crates/vm/levm/src/opcode_handlers/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
memory::{self, calculate_memory_size},
precompiles::is_precompile,
utils::{address_to_word, word_to_address, *},
vm::{RetData, StateBackup, VM},
vm::{RetData, VM},
};
use bytes::Bytes;
use ethrex_common::{
Expand Down Expand Up @@ -733,13 +733,9 @@ impl<'a> VM<'a> {
value: value_in_wei_to_send,
max_message_call_gas,
});
// Backup of Database, Substate, Gas Refunds and Transient Storage if sub-context is reverted
let backup = StateBackup::new(
self.accrued_substate.clone(),
self.env.refunded_gas,
self.env.transient_storage.clone(),
);
self.backups.push(backup);
// Backup of Substate, a copy of the substate to restore if sub-context is reverted
let backup = self.accrued_substate.clone();
self.substate_backups.push(backup);
Ok(OpcodeResult::Continue { pc_increment: 0 })
}

Expand Down Expand Up @@ -847,13 +843,9 @@ impl<'a> VM<'a> {
false,
);
self.call_frames.push(new_call_frame);
// Backup of Database, Substate, Gas Refunds and Transient Storage if sub-context is reverted
let backup = StateBackup::new(
self.accrued_substate.clone(),
self.env.refunded_gas,
self.env.transient_storage.clone(),
);
self.backups.push(backup);
// Backup of Substate, a copy of the substate to restore if sub-context is reverted
let backup = self.accrued_substate.clone();
self.substate_backups.push(backup);

if is_precompile(&code_address, self.env.config.fork) {
let _report = self.run_execution()?;
Expand Down
11 changes: 3 additions & 8 deletions crates/vm/levm/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
use crate::{
constants::*,
db::gen_db::GeneralizedDatabase,
errors::{InternalError, OutOfGasError, TxValidationError, VMError},
gas_cost::{
constants::*, db::gen_db::GeneralizedDatabase, errors::{InternalError, OutOfGasError, TxValidationError, VMError}, gas_cost::{
self, fake_exponential, ACCESS_LIST_ADDRESS_COST, ACCESS_LIST_STORAGE_KEY_COST,
BLOB_GAS_PER_BLOB, COLD_ADDRESS_ACCESS_COST, CREATE_BASE_COST, STANDARD_TOKEN_COST,
TOTAL_COST_FLOOR_PER_TOKEN, WARM_ADDRESS_ACCESS_COST,
},
opcodes::Opcode,
vm::{EVMConfig, Substate, VM},
}, opcodes::Opcode, vm::{Substate, VM}, EVMConfig
};
use bytes::Bytes;
use ethrex_common::types::Account;
Expand Down Expand Up @@ -462,7 +457,7 @@ impl<'a> VM<'a> {
self.current_call_frame_mut()?.valid_jump_destinations =
get_valid_jump_destinations(&self.current_call_frame()?.bytecode).unwrap_or_default();

self.env.refunded_gas = refunded_gas;
self.accrued_substate.refunded_gas = refunded_gas;

Ok(())
}
Expand Down
Loading