Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(l2): use levm for sp1 prover using a trait #2250

Merged
merged 25 commits into from
Mar 26, 2025
Merged
Changes from 1 commit
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
Prev Previous commit
Next Next commit
make compile
tomip01 committed Mar 18, 2025

Verified

This commit was signed with the committer’s verified signature.
tomip01 Tomás Paradelo
commit 6b144b205f55fc13b291c01c9617c432b2c645a4
8 changes: 7 additions & 1 deletion cmd/ef_tests/state/runner/levm_runner.rs
Original file line number Diff line number Diff line change
@@ -309,9 +309,15 @@ pub fn ensure_post_state(
// Execution result was successful and no exception was expected.
None => {
let store_wrapper = utils::load_initial_state_levm(test);
let block_header = store_wrapper
.store
.get_block_header_by_hash(store_wrapper.block_hash)
.unwrap()
.unwrap();
let levm_account_updates = backends::levm::LEVM::get_state_transitions(
Some(*fork),
&store_wrapper,
Arc::new(store_wrapper.clone()),
&block_header,
&execution_report.new_state,
)
.map_err(|_| {
13 changes: 11 additions & 2 deletions cmd/ef_tests/state/runner/revm_runner.rs
Original file line number Diff line number Diff line change
@@ -29,7 +29,10 @@ use revm::{
},
Evm as Revm,
};
use std::collections::{HashMap, HashSet};
use std::{
collections::{HashMap, HashSet},
sync::Arc,
};

pub fn re_run_failed_ef_test(
test: &EFTest,
@@ -328,9 +331,15 @@ pub fn ensure_post_state(
// We only want to compare account updates when no exception is expected.
None => {
let store_wrapper = load_initial_state_levm(test);
let block_header = store_wrapper
.store
.get_block_header_by_hash(store_wrapper.block_hash)
.unwrap()
.unwrap();
let levm_account_updates = backends::levm::LEVM::get_state_transitions(
Some(*fork),
&store_wrapper,
Arc::new(store_wrapper.clone()),
&block_header,
&levm_execution_report.new_state,
)
.map_err(|_| {
116 changes: 47 additions & 69 deletions crates/vm/backends/levm/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
pub(crate) mod db;

use super::BlockExecutionResult;
use crate::backends::revm::execution_db::ExecutionDB;
use crate::constants::{
BEACON_ROOTS_ADDRESS, CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, HISTORY_STORAGE_ADDRESS,
SYSTEM_ADDRESS, WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
};
use crate::db::StoreWrapper;
use crate::EvmError;
use bytes::Bytes;
use ethrex_common::types::requests::Requests;
use ethrex_common::types::Fork;
use ethrex_common::{
@@ -23,9 +22,7 @@ use ethrex_levm::{
vm::{EVMConfig, VM},
Account, AccountInfo as LevmAccountInfo, Environment,
};
use ethrex_storage::{error::StoreError, AccountUpdate, Store};
use revm_primitives::Bytes;
use std::ops::Deref;
use ethrex_storage::{AccountUpdate, Store};
use std::{collections::HashMap, sync::Arc};

// Export needed types
@@ -49,31 +46,27 @@ impl LEVM {
if #[cfg(not(feature = "l2"))] {
let fork = config.fork(block_header.timestamp);
if block_header.parent_beacon_block_root.is_some() && fork >= Fork::Cancun {
Self::beacon_root_contract_call(block_header, &db, &mut block_cache)?;
Self::beacon_root_contract_call(block_header, db.clone(), &mut block_cache)?;
}

if fork >= Fork::Prague {
//eip 2935: stores parent block hash in system contract
Self::process_block_hash_history(block_header, &db, &mut block_cache)?;
Self::process_block_hash_history(block_header, db.clone(), &mut block_cache)?;
}
}
}

// Account updates are initialized like this because of the beacon_root_contract_call, it is going to be empty if it wasn't called.
// Here we get the state_transitions from the db and then we get the state_transitions from the cache_db.
let mut account_updates = Self::get_state_transitions(None, &store_wrapper, &block_cache)?;
let mut account_updates =
Self::get_state_transitions(None, db.clone(), &block.header, &block_cache)?;
let mut receipts = Vec::new();
let mut cumulative_gas_used = 0;

for tx in block.body.transactions.iter() {
let report = Self::execute_tx(
tx,
block_header,
Arc::new(store_wrapper.clone()),
block_cache.clone(),
&config,
)
.map_err(EvmError::from)?;
let report =
Self::execute_tx(tx, &block.header, db.clone(), block_cache.clone(), &config)
.map_err(EvmError::from)?;

let mut new_state = report.new_state.clone();
// Now original_value is going to be the same as the current_value, for the next transaction.
@@ -109,7 +102,7 @@ impl LEVM {
{
// We check if it was in block_cache, if not, we get it from DB.
let mut account = block_cache.get(&address).cloned().unwrap_or({
let acc_info = store_wrapper.get_account_info(address);
let acc_info = db.get_account_info(address);
Account::from(acc_info)
});

@@ -119,11 +112,13 @@ impl LEVM {
}
}

let requests = extract_all_requests_levm(&receipts, &db, &block.header, &mut block_cache)?;
let requests =
extract_all_requests_levm(&receipts, db.clone(), &block.header, &mut block_cache)?;

account_updates.extend(Self::get_state_transitions(
None,
&store_wrapper,
db.clone(),
&block.header,
&block_cache,
)?);

@@ -181,7 +176,7 @@ impl LEVM {
env,
tx.value(),
tx.data().clone(),
db,
db.clone(),
block_cache.clone(),
tx.access_list(),
tx.authorization_list(),
@@ -194,16 +189,13 @@ impl LEVM {
// Warning only pass the fork if running the ef-tests.
// ISSUE #2021: https://github.com/lambdaclass/ethrex/issues/2021
ef_tests: Option<Fork>,
store_wrapper: &StoreWrapper,
db: Arc<dyn LevmDatabase>,
block_header: &BlockHeader,
new_state: &CacheDB,
) -> Result<Vec<AccountUpdate>, EvmError> {
let mut account_updates: Vec<AccountUpdate> = vec![];
let store = store_wrapper.store.clone();
let block_hash = store_wrapper.block_hash;
for (new_state_account_address, new_state_account) in new_state {
let initial_account_state = store
.get_account_info_by_hash(block_hash, *new_state_account_address)?
.unwrap_or_default();
let initial_account_state = db.get_account_info(*new_state_account_address);
let mut updates = 0;
if initial_account_state.balance != new_state_account.info.balance {
updates += 1;
@@ -215,15 +207,11 @@ impl LEVM {
// The new state account has no code
None
} else {
// Get the code hash of the new state account bytecode
let potential_new_bytecode_hash = code_hash(&new_state_account.info.bytecode);
// Look into the current database to see if the bytecode hash is already present
let current_bytecode = store
.get_account_code(potential_new_bytecode_hash)
.expect("Error getting account code by hash");
let current_bytecode = db.get_account_info(*new_state_account_address).bytecode;
let code = new_state_account.info.bytecode.clone();
// The code is present in the current database
if let Some(current_bytecode) = current_bytecode {
if current_bytecode != Bytes::new() {
if current_bytecode != code {
// The code has changed
Some(code)
@@ -262,25 +250,19 @@ impl LEVM {
added_storage,
};

let block_header = store
.get_block_header_by_hash(block_hash)?
.ok_or(StoreError::MissingStore)?;
let fork_from_config = store.get_chain_config()?.fork(block_header.timestamp);
let fork_from_config = db.get_chain_config().fork(block_header.timestamp);
// Here we take the passed fork through the ef_tests variable, or we set it to the fork based on the timestamp.
let fork = ef_tests.unwrap_or(fork_from_config);
if let Some(old_info) =
store.get_account_info_by_hash(block_hash, account_update.address)?
let old_info = db.get_account_info(account_update.address);
// https://eips.ethereum.org/EIPS/eip-161
// if an account was empty and is now empty, after spurious dragon, it should be removed
if account_update.removed
&& old_info.balance.is_zero()
&& old_info.nonce == 0
&& old_info.bytecode_hash() == code_hash(&Bytes::new())
&& fork < Fork::SpuriousDragon
{
// https://eips.ethereum.org/EIPS/eip-161
// if an account was empty and is now empty, after spurious dragon, it should be removed
if account_update.removed
&& old_info.balance.is_zero()
&& old_info.nonce == 0
&& old_info.code_hash == code_hash(&Bytes::new())
&& fork < Fork::SpuriousDragon
{
continue;
}
continue;
}

account_updates.push(account_update);
@@ -331,7 +313,7 @@ impl LEVM {
/// `new_state` is being modified inside [generic_system_contract_levm].
pub fn beacon_root_contract_call(
block_header: &BlockHeader,
store: &Store,
db: Arc<dyn LevmDatabase>,
new_state: &mut CacheDB,
) -> Result<(), EvmError> {
let beacon_root = match block_header.parent_beacon_block_root {
@@ -346,7 +328,7 @@ impl LEVM {
generic_system_contract_levm(
block_header,
Bytes::copy_from_slice(beacon_root.as_bytes()),
store,
db,
new_state,
*BEACON_ROOTS_ADDRESS,
*SYSTEM_ADDRESS,
@@ -356,13 +338,13 @@ impl LEVM {
/// `new_state` is being modified inside [generic_system_contract_levm].
pub fn process_block_hash_history(
block_header: &BlockHeader,
store: &Store,
db: Arc<dyn LevmDatabase>,
new_state: &mut CacheDB,
) -> Result<(), EvmError> {
generic_system_contract_levm(
block_header,
Bytes::copy_from_slice(block_header.parent_hash.as_bytes()),
store,
db.clone(),
new_state,
*HISTORY_STORAGE_ADDRESS,
*SYSTEM_ADDRESS,
@@ -371,13 +353,13 @@ impl LEVM {
}
pub(crate) fn read_withdrawal_requests(
block_header: &BlockHeader,
store: &Store,
db: Arc<dyn LevmDatabase>,
new_state: &mut CacheDB,
) -> Option<ExecutionReport> {
let report = generic_system_contract_levm(
block_header,
Bytes::new(),
store,
db.clone(),
new_state,
*WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
*SYSTEM_ADDRESS,
@@ -391,13 +373,13 @@ impl LEVM {
}
pub(crate) fn dequeue_consolidation_requests(
block_header: &BlockHeader,
store: &Store,
db: Arc<dyn LevmDatabase>,
new_state: &mut CacheDB,
) -> Option<ExecutionReport> {
let report = generic_system_contract_levm(
block_header,
Bytes::new(),
store,
db.clone(),
new_state,
*CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS,
*SYSTEM_ADDRESS,
@@ -415,17 +397,12 @@ impl LEVM {
pub fn generic_system_contract_levm(
block_header: &BlockHeader,
calldata: Bytes,
store: &Store,
db: Arc<dyn LevmDatabase>,
new_state: &mut CacheDB,
contract_address: Address,
system_address: Address,
) -> Result<ExecutionReport, EvmError> {
let store_wrapper = Arc::new(StoreWrapper {
store: store.clone(),
block_hash: block_header.parent_hash,
});

let chain_config = store.get_chain_config()?;
let chain_config = db.get_chain_config();
let config = EVMConfig::new_from_chain_config(&chain_config, block_header);
let env = Environment {
origin: system_address,
@@ -448,8 +425,8 @@ pub fn generic_system_contract_levm(
TxKind::Call(contract_address),
env,
U256::zero(),
calldata.into(),
store_wrapper,
calldata,
db.clone(),
new_state.clone(),
vec![],
None,
@@ -487,11 +464,11 @@ pub fn generic_system_contract_levm(
#[allow(unused_variables)]
pub fn extract_all_requests_levm(
receipts: &[Receipt],
store: &Store,
db: Arc<dyn LevmDatabase>,
header: &BlockHeader,
cache: &mut CacheDB,
) -> Result<Vec<Requests>, EvmError> {
let config = store.get_chain_config()?;
let config = db.get_chain_config();
let fork = config.fork(header.timestamp);

if fork < Fork::Prague {
@@ -508,7 +485,8 @@ pub fn extract_all_requests_levm(
"deposit_contract_address config is missing".to_string(),
))?;

let withdrawals_data: Vec<u8> = match LEVM::read_withdrawal_requests(header, store, cache) {
let withdrawals_data: Vec<u8> = match LEVM::read_withdrawal_requests(header, db.clone(), cache)
{
Some(report) => {
// the cache is updated inside the generic_system_call
report.output.into()
@@ -517,7 +495,7 @@ pub fn extract_all_requests_levm(
};

let consolidation_data: Vec<u8> =
match LEVM::dequeue_consolidation_requests(header, store, cache) {
match LEVM::dequeue_consolidation_requests(header, db.clone(), cache) {
Some(report) => {
// the cache is updated inside the generic_system_call
report.output.into()
37 changes: 28 additions & 9 deletions crates/vm/backends/mod.rs
Original file line number Diff line number Diff line change
@@ -87,7 +87,7 @@ impl Evm {
REVM::execute_block(block, &mut state)
}
Evm::LEVM { store_wrapper, .. } => {
LEVM::execute_block(block, store_wrapper.store.clone())
LEVM::execute_block(block, Arc::new(store_wrapper.clone()))
}
}
}
@@ -185,17 +185,24 @@ impl Evm {
store_wrapper,
block_cache,
} => {
let store = store_wrapper.store.clone();
let chain_config = store.get_chain_config()?;
let chain_config = store_wrapper.store.get_chain_config()?;
let fork = chain_config.fork(block_header.timestamp);
let mut new_state = CacheDB::new();

if block_header.parent_beacon_block_root.is_some() && fork >= Fork::Cancun {
LEVM::beacon_root_contract_call(block_header, &store, &mut new_state)?;
LEVM::beacon_root_contract_call(
block_header,
Arc::new(store_wrapper.clone()),
&mut new_state,
)?;
}

if fork >= Fork::Prague {
LEVM::process_block_hash_history(block_header, &store, &mut new_state)?;
LEVM::process_block_hash_history(
block_header,
Arc::new(store_wrapper.clone()),
&mut new_state,
)?;
}

// Now original_value is going to be the same as the current_value, for the next transaction.
@@ -231,7 +238,16 @@ impl Evm {
block_cache,
} => {
store_wrapper.block_hash = parent_hash;
LEVM::get_state_transitions(None, store_wrapper, block_cache)
let block_header = store_wrapper
.store
.get_block_header_by_hash(parent_hash)?
.unwrap();
LEVM::get_state_transitions(
None,
Arc::new(store_wrapper.clone()),
&block_header,
block_cache,
)
}
}
}
@@ -272,9 +288,12 @@ impl Evm {
Evm::LEVM {
store_wrapper,
block_cache,
} => {
levm::extract_all_requests_levm(receipts, &store_wrapper.store, header, block_cache)
}
} => levm::extract_all_requests_levm(
receipts,
Arc::new(store_wrapper.clone()),
header,
block_cache,
),
Evm::REVM { state } => revm::extract_all_requests(receipts, state, header),
}
}