Skip to content

refactor(levm): use ethrex account types in LEVM #2629

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

Merged
merged 12 commits into from
Apr 29, 2025
16 changes: 8 additions & 8 deletions cmd/ef_tests/state/report.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use crate::runner::{EFTestRunnerError, InternalError};
use colored::Colorize;
use ethrex_common::{types::Fork, Address, H256};
use ethrex_levm::{
errors::{ExecutionReport, TxResult, VMError},
Account,
use ethrex_common::{
types::{Account, Fork},
Address, H256,
};
use ethrex_levm::errors::{ExecutionReport, TxResult, VMError};
use ethrex_storage::{error::StoreError, AccountUpdate};
use itertools::Itertools;
use revm::primitives::{EVMError, ExecutionResult as RevmExecutionResult};
Expand Down Expand Up @@ -664,14 +664,14 @@ impl fmt::Display for ComparisonReport {
base_account.info.balance, new_info.balance
)?;

if base_account.info.bytecode_hash() != new_info.code_hash {
if base_account.info.code_hash != new_info.code_hash {
writeln!(
f,
"\t\t\t\t\tCode: {} -> {}",
if base_account.info.bytecode.is_empty() {
if base_account.code.is_empty() {
"empty".to_string()
} else {
hex::encode(&base_account.info.bytecode)
hex::encode(&base_account.code)
},
account_update
.code
Expand All @@ -691,7 +691,7 @@ impl fmt::Display for ComparisonReport {
writeln!(
f,
"\t\t\t\t\tStorage slot: {key:#x}: {} -> {}",
initial_value.original_value, value
initial_value, value
)?;
}
}
Expand Down
16 changes: 8 additions & 8 deletions cmd/ef_tests/state/runner/levm_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,29 +198,29 @@ pub fn prepare_vm_for_tx<'a>(
pub fn ensure_pre_state(evm: &VM, test: &EFTest) -> Result<(), EFTestRunnerError> {
let world_state = &evm.db.store;
for (address, pre_value) in &test.pre.0 {
let account = world_state.get_account_info(*address).map_err(|e| {
let account = world_state.get_account(*address).map_err(|e| {
EFTestRunnerError::Internal(InternalError::Custom(format!(
"Failed to get account info when ensuring pre state: {}",
e
)))
})?;
ensure_pre_state_condition(
account.nonce == pre_value.nonce,
account.info.nonce == pre_value.nonce,
format!(
"Nonce mismatch for account {:#x}: expected {}, got {}",
address, pre_value.nonce, account.nonce
address, pre_value.nonce, account.info.nonce
),
)?;
ensure_pre_state_condition(
account.balance == pre_value.balance,
account.info.balance == pre_value.balance,
format!(
"Balance mismatch for account {:#x}: expected {}, got {}",
address, pre_value.balance, account.balance
address, pre_value.balance, account.info.balance
),
)?;
for (k, v) in &pre_value.storage {
let storage_slot = world_state
.get_storage_slot(*address, H256::from_slice(&k.to_big_endian()))
.get_storage_value(*address, H256::from_slice(&k.to_big_endian()))
.unwrap();
ensure_pre_state_condition(
&storage_slot == v,
Expand All @@ -231,12 +231,12 @@ pub fn ensure_pre_state(evm: &VM, test: &EFTest) -> Result<(), EFTestRunnerError
)?;
}
ensure_pre_state_condition(
keccak(account.bytecode.clone()) == keccak(pre_value.code.as_ref()),
account.info.code_hash == keccak(pre_value.code.as_ref()),
format!(
"Code hash mismatch for account {:#x}: expected {}, got {}",
address,
keccak(pre_value.code.as_ref()),
keccak(account.bytecode)
account.info.code_hash
),
)?;
}
Expand Down
8 changes: 2 additions & 6 deletions cmd/ef_tests/state/runner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
};
use clap::Parser;
use colored::Colorize;
use ethrex_common::Address;
use ethrex_common::{types::Account, Address};
use ethrex_levm::errors::{ExecutionReport, VMError};
use ethrex_vm::SpecId;
use serde::{Deserialize, Serialize};
Expand All @@ -24,11 +24,7 @@ pub enum EFTestRunnerError {
#[error("Failed to ensure pre-state: {0}")]
FailedToEnsurePreState(String),
#[error("Failed to ensure post-state: {1}")]
FailedToEnsurePostState(
ExecutionReport,
String,
HashMap<Address, ethrex_levm::Account>,
),
FailedToEnsurePostState(ExecutionReport, String, HashMap<Address, Account>),
#[error("VM run mismatch: {0}")]
VMExecutionMismatch(String),
#[error("Exception does not match the expected: {0}")]
Expand Down
15 changes: 3 additions & 12 deletions cmd/ef_tests/state/runner/revm_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,10 @@ use crate::{
};
use bytes::Bytes;
use ethrex_common::{
types::{Fork, TxKind},
types::{Account, Fork, TxKind},
Address, H256,
};
use ethrex_levm::{
errors::{ExecutionReport, TxResult},
Account, StorageSlot,
};
use ethrex_levm::errors::{ExecutionReport, TxResult};
use ethrex_storage::{error::StoreError, AccountUpdate};
use ethrex_vm::{
self,
Expand Down Expand Up @@ -362,13 +359,7 @@ pub async fn compare_levm_revm_account_updates(
let account_storage = pre_state_value
.storage
.iter()
.map(|(key, value)| {
let storage_slot = StorageSlot {
original_value: *value,
current_value: *value,
};
(H256::from_slice(&key.to_big_endian()), storage_slot)
})
.map(|(key, value)| (H256::from_slice(&key.to_big_endian()), *value))
.collect();
let account = Account::new(
pre_state_value.balance,
Expand Down
38 changes: 38 additions & 0 deletions crates/common/types/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::collections::HashMap;
use bytes::Bytes;
use ethereum_types::{H256, U256};
use ethrex_trie::Trie;
use keccak_hash::keccak;
use serde::{Deserialize, Serialize};
use sha3::{Digest as _, Keccak256};

Expand Down Expand Up @@ -169,6 +170,43 @@ impl From<&GenesisAccount> for AccountState {
}
}

impl Account {
pub fn new(balance: U256, code: Bytes, nonce: u64, storage: HashMap<H256, U256>) -> Self {
Self {
info: AccountInfo {
balance,
code_hash: keccak(code.as_ref()).0.into(),
nonce,
},
code,
storage,
}
}

pub fn has_nonce(&self) -> bool {
self.info.nonce != 0
}

pub fn has_code(&self) -> bool {
self.info.code_hash != *EMPTY_KECCACK_HASH
}

pub fn has_code_or_nonce(&self) -> bool {
self.has_code() || self.has_nonce()
}

pub fn is_empty(&self) -> bool {
self.info.balance.is_zero()
&& self.info.nonce == 0
&& self.info.code_hash == *EMPTY_KECCACK_HASH
}

pub fn set_code(&mut self, code: Bytes) {
self.info.code_hash = keccak(code.as_ref()).0.into();
self.code = code;
}
}

#[cfg(test)]
mod test {
use std::str::FromStr;
Expand Down
23 changes: 12 additions & 11 deletions crates/l2/prover/bench/src/rpc/db.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::cell::RefCell;
use std::collections::HashMap;

use crate::constants::{CANCUN_CONFIG, RPC_RATE_LIMIT};
Expand All @@ -9,8 +8,8 @@ use ethrex_common::{
types::{AccountInfo, AccountState, Block, Fork, TxKind},
Address, H256, U256,
};
use ethrex_levm::db::gen_db::GeneralizedDatabase;
use ethrex_levm::db::Database as LevmDatabase;
use ethrex_levm::vm::GeneralizedDatabase;
use ethrex_storage::{hash_address, hash_key};
use ethrex_trie::{Node, PathRLP, Trie};
use ethrex_vm::backends::levm::{CacheDB, LEVM};
Expand Down Expand Up @@ -445,10 +444,11 @@ impl LevmDatabase for RpcDB {
Ok(None) // code is stored in account info
}

fn get_account_info(
fn get_account(
&self,
address: Address,
) -> std::result::Result<ethrex_levm::AccountInfo, ethrex_levm::db::error::DatabaseError> {
) -> std::result::Result<ethrex_common::types::Account, ethrex_levm::db::error::DatabaseError>
{
let cache = self.cache.lock().unwrap();
let account = if let Some(account) = cache.get(&address).cloned() {
account
Expand All @@ -466,17 +466,18 @@ impl LevmDatabase for RpcDB {
..
} = account
{
Ok(ethrex_levm::AccountInfo {
bytecode: code.clone().unwrap_or_default(),
balance: account_state.balance,
nonce: account_state.nonce,
})
Ok(ethrex_common::types::Account::new(
account_state.balance,
code.clone().unwrap_or_default(),
account_state.nonce,
HashMap::new(),
))
} else {
Ok(ethrex_levm::AccountInfo::default())
Ok(ethrex_common::types::Account::default())
}
}

fn get_storage_slot(&self, address: Address, key: H256) -> Result<U256, DatabaseError> {
fn get_storage_value(&self, address: Address, key: H256) -> Result<U256, DatabaseError> {
let account = self
.fetch_accounts_blocking(&[(address, vec![key])], false)
.map_err(|e| DatabaseError::Custom(format!("Failed to fetch account info: {e}")))?
Expand Down
50 changes: 22 additions & 28 deletions crates/vm/backends/levm/db.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use ethrex_common::types::Account;
use ethrex_common::U256 as CoreU256;
use ethrex_common::{Address as CoreAddress, H256 as CoreH256};
use ethrex_levm::constants::EMPTY_CODE_HASH;
Expand Down Expand Up @@ -30,10 +31,7 @@ impl DatabaseLogger {
}

impl LevmDatabase for DatabaseLogger {
fn get_account_info(
&self,
address: CoreAddress,
) -> Result<ethrex_levm::AccountInfo, DatabaseError> {
fn get_account(&self, address: CoreAddress) -> Result<Account, DatabaseError> {
self.state_accessed
.lock()
.map_err(|_| DatabaseError::Custom("Could not lock mutex".to_string()))?
Expand All @@ -42,14 +40,14 @@ impl LevmDatabase for DatabaseLogger {
self.store
.lock()
.map_err(|_| DatabaseError::Custom("Could not lock mutex".to_string()))?
.get_account_info(address)
.get_account(address)
}

fn account_exists(&self, address: CoreAddress) -> bool {
self.store.lock().unwrap().account_exists(address)
}

fn get_storage_slot(
fn get_storage_value(
&self,
address: CoreAddress,
key: CoreH256,
Expand All @@ -63,7 +61,7 @@ impl LevmDatabase for DatabaseLogger {
self.store
.lock()
.map_err(|_| DatabaseError::Custom("Could not lock mutex".to_string()))?
.get_storage_slot(address, key)
.get_storage_value(address, key)
}

fn get_block_hash(&self, block_number: u64) -> Result<Option<CoreH256>, DatabaseError> {
Expand Down Expand Up @@ -101,10 +99,7 @@ impl LevmDatabase for DatabaseLogger {
}

impl LevmDatabase for StoreWrapper {
fn get_account_info(
&self,
address: CoreAddress,
) -> Result<ethrex_levm::AccountInfo, DatabaseError> {
fn get_account(&self, address: CoreAddress) -> Result<Account, DatabaseError> {
let acc_info = self
.store
.get_account_info_by_hash(self.block_hash, address)
Expand All @@ -117,11 +112,12 @@ impl LevmDatabase for StoreWrapper {
.map_err(|e| DatabaseError::Custom(e.to_string()))?
.unwrap_or_default();

Ok(ethrex_levm::account::AccountInfo {
balance: acc_info.balance,
nonce: acc_info.nonce,
bytecode: acc_code,
})
Ok(Account::new(
acc_info.balance,
acc_code,
acc_info.nonce,
HashMap::new(),
))
}

fn account_exists(&self, address: CoreAddress) -> bool {
Expand All @@ -133,7 +129,7 @@ impl LevmDatabase for StoreWrapper {
acc_info.is_some()
}

fn get_storage_slot(
fn get_storage_value(
&self,
address: CoreAddress,
key: CoreH256,
Expand Down Expand Up @@ -165,12 +161,9 @@ impl LevmDatabase for StoreWrapper {
}

impl LevmDatabase for ExecutionDB {
fn get_account_info(
&self,
address: CoreAddress,
) -> Result<ethrex_levm::AccountInfo, DatabaseError> {
fn get_account(&self, address: CoreAddress) -> Result<Account, DatabaseError> {
let Some(acc_info) = self.accounts.get(&address) else {
return Ok(ethrex_levm::AccountInfo::default());
return Ok(Account::default());
};

let acc_code = if acc_info.code_hash != EMPTY_CODE_HASH {
Expand All @@ -184,11 +177,12 @@ impl LevmDatabase for ExecutionDB {
&bytes::Bytes::new()
};

Ok(ethrex_levm::AccountInfo {
balance: acc_info.balance,
bytecode: acc_code.clone(),
nonce: acc_info.nonce,
})
Ok(Account::new(
acc_info.balance,
acc_code.clone(),
acc_info.nonce,
HashMap::new(),
))
}

fn account_exists(&self, address: CoreAddress) -> bool {
Expand All @@ -199,7 +193,7 @@ impl LevmDatabase for ExecutionDB {
Ok(self.block_hashes.get(&block_number).cloned())
}

fn get_storage_slot(
fn get_storage_value(
&self,
address: CoreAddress,
key: CoreH256,
Expand Down
Loading
Loading