Skip to content

Commit 33e34ef

Browse files
authored
refactor(levm): use ethrex account types in LEVM (#2629)
**Motivation** - Stop using the Account and AccountInfo types defined in LEVM and start using the ones defined in the L1 client. - Biggest changes are that AccountInfo no longer has code, so we can't use it with that purpose and also we don't have our struct StorageSlot anymore, so we have to keep track of original values somewhere else. **Description** - Now we use the structs of the L1 client but they are different from the ones that we used so I had to make changes: - `get_account_info` is now `get_account` because we also need the code of the account and `AccountInfo` has the `code_hash` only. This makes changes on every structure that implements `LevmDatabase` trait. - Now that we don't have `StorageSlot` that had the `current_value` and `original_value` of a storage slot (`original_value` being the value pre-tx) I had to make some changes to logic and store those original values into an auxiliary `HashMap` on `VM`. - Added new function `get_original_storage()` for getting the original storage value. - Make some tiny changes in SSTORE, mostly organize it better. Storage changes deep description: - Now every time we want to get the `original_value` we will look up in the original values stored in the VM struct. These intends to store the storage values previous to starting the execution of a particular transaction. For efficiency and performance, we only update this new field when actually getting the original value. - Let me clarify: At the beginning of the transaction the `CacheDB` could have a lot of accounts with their storage but the `VM.storage_original_values`will start empty on every transaction. When `SSTORE` opcode is executed and we actually care for the original value of a storage slot we will look at `storage_original_values` and it won’t find it (the first time), so then it will see what the value in the `CacheDB` is, and if it’s not there it will finally check on the actual `Database`. After retrieving the value, it will be added to `storage_original_values` , but ONLY the FIRST time. That means that if the value keeps on changing the `original_value` won’t change because once it’s added it’s not modified. <!-- Link to issues: Resolves #111, Resolves #222 --> Closes #issue_number
1 parent 240886b commit 33e34ef

File tree

23 files changed

+257
-372
lines changed

23 files changed

+257
-372
lines changed

cmd/ef_tests/state/report.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use crate::runner::{EFTestRunnerError, InternalError};
22
use colored::Colorize;
3-
use ethrex_common::{types::Fork, Address, H256};
4-
use ethrex_levm::{
5-
errors::{ExecutionReport, TxResult, VMError},
6-
Account,
3+
use ethrex_common::{
4+
types::{Account, Fork},
5+
Address, H256,
76
};
7+
use ethrex_levm::errors::{ExecutionReport, TxResult, VMError};
88
use ethrex_storage::{error::StoreError, AccountUpdate};
99
use itertools::Itertools;
1010
use revm::primitives::{EVMError, ExecutionResult as RevmExecutionResult};
@@ -664,14 +664,14 @@ impl fmt::Display for ComparisonReport {
664664
base_account.info.balance, new_info.balance
665665
)?;
666666

667-
if base_account.info.bytecode_hash() != new_info.code_hash {
667+
if base_account.info.code_hash != new_info.code_hash {
668668
writeln!(
669669
f,
670670
"\t\t\t\t\tCode: {} -> {}",
671-
if base_account.info.bytecode.is_empty() {
671+
if base_account.code.is_empty() {
672672
"empty".to_string()
673673
} else {
674-
hex::encode(&base_account.info.bytecode)
674+
hex::encode(&base_account.code)
675675
},
676676
account_update
677677
.code
@@ -691,7 +691,7 @@ impl fmt::Display for ComparisonReport {
691691
writeln!(
692692
f,
693693
"\t\t\t\t\tStorage slot: {key:#x}: {} -> {}",
694-
initial_value.original_value, value
694+
initial_value, value
695695
)?;
696696
}
697697
}

cmd/ef_tests/state/runner/levm_runner.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -198,29 +198,29 @@ pub fn prepare_vm_for_tx<'a>(
198198
pub fn ensure_pre_state(evm: &VM, test: &EFTest) -> Result<(), EFTestRunnerError> {
199199
let world_state = &evm.db.store;
200200
for (address, pre_value) in &test.pre.0 {
201-
let account = world_state.get_account_info(*address).map_err(|e| {
201+
let account = world_state.get_account(*address).map_err(|e| {
202202
EFTestRunnerError::Internal(InternalError::Custom(format!(
203203
"Failed to get account info when ensuring pre state: {}",
204204
e
205205
)))
206206
})?;
207207
ensure_pre_state_condition(
208-
account.nonce == pre_value.nonce,
208+
account.info.nonce == pre_value.nonce,
209209
format!(
210210
"Nonce mismatch for account {:#x}: expected {}, got {}",
211-
address, pre_value.nonce, account.nonce
211+
address, pre_value.nonce, account.info.nonce
212212
),
213213
)?;
214214
ensure_pre_state_condition(
215-
account.balance == pre_value.balance,
215+
account.info.balance == pre_value.balance,
216216
format!(
217217
"Balance mismatch for account {:#x}: expected {}, got {}",
218-
address, pre_value.balance, account.balance
218+
address, pre_value.balance, account.info.balance
219219
),
220220
)?;
221221
for (k, v) in &pre_value.storage {
222222
let storage_slot = world_state
223-
.get_storage_slot(*address, H256::from_slice(&k.to_big_endian()))
223+
.get_storage_value(*address, H256::from_slice(&k.to_big_endian()))
224224
.unwrap();
225225
ensure_pre_state_condition(
226226
&storage_slot == v,
@@ -231,12 +231,12 @@ pub fn ensure_pre_state(evm: &VM, test: &EFTest) -> Result<(), EFTestRunnerError
231231
)?;
232232
}
233233
ensure_pre_state_condition(
234-
keccak(account.bytecode.clone()) == keccak(pre_value.code.as_ref()),
234+
account.info.code_hash == keccak(pre_value.code.as_ref()),
235235
format!(
236236
"Code hash mismatch for account {:#x}: expected {}, got {}",
237237
address,
238238
keccak(pre_value.code.as_ref()),
239-
keccak(account.bytecode)
239+
account.info.code_hash
240240
),
241241
)?;
242242
}

cmd/ef_tests/state/runner/mod.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
};
77
use clap::Parser;
88
use colored::Colorize;
9-
use ethrex_common::Address;
9+
use ethrex_common::{types::Account, Address};
1010
use ethrex_levm::errors::{ExecutionReport, VMError};
1111
use ethrex_vm::SpecId;
1212
use serde::{Deserialize, Serialize};
@@ -24,11 +24,7 @@ pub enum EFTestRunnerError {
2424
#[error("Failed to ensure pre-state: {0}")]
2525
FailedToEnsurePreState(String),
2626
#[error("Failed to ensure post-state: {1}")]
27-
FailedToEnsurePostState(
28-
ExecutionReport,
29-
String,
30-
HashMap<Address, ethrex_levm::Account>,
31-
),
27+
FailedToEnsurePostState(ExecutionReport, String, HashMap<Address, Account>),
3228
#[error("VM run mismatch: {0}")]
3329
VMExecutionMismatch(String),
3430
#[error("Exception does not match the expected: {0}")]

cmd/ef_tests/state/runner/revm_runner.rs

+3-12
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,10 @@ use crate::{
66
};
77
use bytes::Bytes;
88
use ethrex_common::{
9-
types::{Fork, TxKind},
9+
types::{Account, Fork, TxKind},
1010
Address, H256,
1111
};
12-
use ethrex_levm::{
13-
errors::{ExecutionReport, TxResult},
14-
Account, StorageSlot,
15-
};
12+
use ethrex_levm::errors::{ExecutionReport, TxResult};
1613
use ethrex_storage::{error::StoreError, AccountUpdate};
1714
use ethrex_vm::{
1815
self,
@@ -362,13 +359,7 @@ pub async fn compare_levm_revm_account_updates(
362359
let account_storage = pre_state_value
363360
.storage
364361
.iter()
365-
.map(|(key, value)| {
366-
let storage_slot = StorageSlot {
367-
original_value: *value,
368-
current_value: *value,
369-
};
370-
(H256::from_slice(&key.to_big_endian()), storage_slot)
371-
})
362+
.map(|(key, value)| (H256::from_slice(&key.to_big_endian()), *value))
372363
.collect();
373364
let account = Account::new(
374365
pre_state_value.balance,

crates/common/types/account.rs

+38
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::collections::HashMap;
33
use bytes::Bytes;
44
use ethereum_types::{H256, U256};
55
use ethrex_trie::Trie;
6+
use keccak_hash::keccak;
67
use serde::{Deserialize, Serialize};
78
use sha3::{Digest as _, Keccak256};
89

@@ -169,6 +170,43 @@ impl From<&GenesisAccount> for AccountState {
169170
}
170171
}
171172

173+
impl Account {
174+
pub fn new(balance: U256, code: Bytes, nonce: u64, storage: HashMap<H256, U256>) -> Self {
175+
Self {
176+
info: AccountInfo {
177+
balance,
178+
code_hash: keccak(code.as_ref()).0.into(),
179+
nonce,
180+
},
181+
code,
182+
storage,
183+
}
184+
}
185+
186+
pub fn has_nonce(&self) -> bool {
187+
self.info.nonce != 0
188+
}
189+
190+
pub fn has_code(&self) -> bool {
191+
self.info.code_hash != *EMPTY_KECCACK_HASH
192+
}
193+
194+
pub fn has_code_or_nonce(&self) -> bool {
195+
self.has_code() || self.has_nonce()
196+
}
197+
198+
pub fn is_empty(&self) -> bool {
199+
self.info.balance.is_zero()
200+
&& self.info.nonce == 0
201+
&& self.info.code_hash == *EMPTY_KECCACK_HASH
202+
}
203+
204+
pub fn set_code(&mut self, code: Bytes) {
205+
self.info.code_hash = keccak(code.as_ref()).0.into();
206+
self.code = code;
207+
}
208+
}
209+
172210
#[cfg(test)]
173211
mod test {
174212
use std::str::FromStr;

crates/l2/prover/bench/src/rpc/db.rs

+12-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use std::cell::RefCell;
21
use std::collections::HashMap;
32

43
use crate::constants::{CANCUN_CONFIG, RPC_RATE_LIMIT};
@@ -9,8 +8,8 @@ use ethrex_common::{
98
types::{AccountInfo, AccountState, Block, Fork, TxKind},
109
Address, H256, U256,
1110
};
11+
use ethrex_levm::db::gen_db::GeneralizedDatabase;
1212
use ethrex_levm::db::Database as LevmDatabase;
13-
use ethrex_levm::vm::GeneralizedDatabase;
1413
use ethrex_storage::{hash_address, hash_key};
1514
use ethrex_trie::{Node, PathRLP, Trie};
1615
use ethrex_vm::backends::levm::{CacheDB, LEVM};
@@ -445,10 +444,11 @@ impl LevmDatabase for RpcDB {
445444
Ok(None) // code is stored in account info
446445
}
447446

448-
fn get_account_info(
447+
fn get_account(
449448
&self,
450449
address: Address,
451-
) -> std::result::Result<ethrex_levm::AccountInfo, ethrex_levm::db::error::DatabaseError> {
450+
) -> std::result::Result<ethrex_common::types::Account, ethrex_levm::db::error::DatabaseError>
451+
{
452452
let cache = self.cache.lock().unwrap();
453453
let account = if let Some(account) = cache.get(&address).cloned() {
454454
account
@@ -466,17 +466,18 @@ impl LevmDatabase for RpcDB {
466466
..
467467
} = account
468468
{
469-
Ok(ethrex_levm::AccountInfo {
470-
bytecode: code.clone().unwrap_or_default(),
471-
balance: account_state.balance,
472-
nonce: account_state.nonce,
473-
})
469+
Ok(ethrex_common::types::Account::new(
470+
account_state.balance,
471+
code.clone().unwrap_or_default(),
472+
account_state.nonce,
473+
HashMap::new(),
474+
))
474475
} else {
475-
Ok(ethrex_levm::AccountInfo::default())
476+
Ok(ethrex_common::types::Account::default())
476477
}
477478
}
478479

479-
fn get_storage_slot(&self, address: Address, key: H256) -> Result<U256, DatabaseError> {
480+
fn get_storage_value(&self, address: Address, key: H256) -> Result<U256, DatabaseError> {
480481
let account = self
481482
.fetch_accounts_blocking(&[(address, vec![key])], false)
482483
.map_err(|e| DatabaseError::Custom(format!("Failed to fetch account info: {e}")))?

crates/vm/backends/levm/db.rs

+22-28
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use ethrex_common::types::Account;
12
use ethrex_common::U256 as CoreU256;
23
use ethrex_common::{Address as CoreAddress, H256 as CoreH256};
34
use ethrex_levm::constants::EMPTY_CODE_HASH;
@@ -30,10 +31,7 @@ impl DatabaseLogger {
3031
}
3132

3233
impl LevmDatabase for DatabaseLogger {
33-
fn get_account_info(
34-
&self,
35-
address: CoreAddress,
36-
) -> Result<ethrex_levm::AccountInfo, DatabaseError> {
34+
fn get_account(&self, address: CoreAddress) -> Result<Account, DatabaseError> {
3735
self.state_accessed
3836
.lock()
3937
.map_err(|_| DatabaseError::Custom("Could not lock mutex".to_string()))?
@@ -42,14 +40,14 @@ impl LevmDatabase for DatabaseLogger {
4240
self.store
4341
.lock()
4442
.map_err(|_| DatabaseError::Custom("Could not lock mutex".to_string()))?
45-
.get_account_info(address)
43+
.get_account(address)
4644
}
4745

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

52-
fn get_storage_slot(
50+
fn get_storage_value(
5351
&self,
5452
address: CoreAddress,
5553
key: CoreH256,
@@ -63,7 +61,7 @@ impl LevmDatabase for DatabaseLogger {
6361
self.store
6462
.lock()
6563
.map_err(|_| DatabaseError::Custom("Could not lock mutex".to_string()))?
66-
.get_storage_slot(address, key)
64+
.get_storage_value(address, key)
6765
}
6866

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

103101
impl LevmDatabase for StoreWrapper {
104-
fn get_account_info(
105-
&self,
106-
address: CoreAddress,
107-
) -> Result<ethrex_levm::AccountInfo, DatabaseError> {
102+
fn get_account(&self, address: CoreAddress) -> Result<Account, DatabaseError> {
108103
let acc_info = self
109104
.store
110105
.get_account_info_by_hash(self.block_hash, address)
@@ -117,11 +112,12 @@ impl LevmDatabase for StoreWrapper {
117112
.map_err(|e| DatabaseError::Custom(e.to_string()))?
118113
.unwrap_or_default();
119114

120-
Ok(ethrex_levm::account::AccountInfo {
121-
balance: acc_info.balance,
122-
nonce: acc_info.nonce,
123-
bytecode: acc_code,
124-
})
115+
Ok(Account::new(
116+
acc_info.balance,
117+
acc_code,
118+
acc_info.nonce,
119+
HashMap::new(),
120+
))
125121
}
126122

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

136-
fn get_storage_slot(
132+
fn get_storage_value(
137133
&self,
138134
address: CoreAddress,
139135
key: CoreH256,
@@ -165,12 +161,9 @@ impl LevmDatabase for StoreWrapper {
165161
}
166162

167163
impl LevmDatabase for ExecutionDB {
168-
fn get_account_info(
169-
&self,
170-
address: CoreAddress,
171-
) -> Result<ethrex_levm::AccountInfo, DatabaseError> {
164+
fn get_account(&self, address: CoreAddress) -> Result<Account, DatabaseError> {
172165
let Some(acc_info) = self.accounts.get(&address) else {
173-
return Ok(ethrex_levm::AccountInfo::default());
166+
return Ok(Account::default());
174167
};
175168

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

187-
Ok(ethrex_levm::AccountInfo {
188-
balance: acc_info.balance,
189-
bytecode: acc_code.clone(),
190-
nonce: acc_info.nonce,
191-
})
180+
Ok(Account::new(
181+
acc_info.balance,
182+
acc_code.clone(),
183+
acc_info.nonce,
184+
HashMap::new(),
185+
))
192186
}
193187

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

202-
fn get_storage_slot(
196+
fn get_storage_value(
203197
&self,
204198
address: CoreAddress,
205199
key: CoreH256,

0 commit comments

Comments
 (0)