diff --git a/crates/vm/backends/levm/mod.rs b/crates/vm/backends/levm/mod.rs index d30eba2ded..d3902b7950 100644 --- a/crates/vm/backends/levm/mod.rs +++ b/crates/vm/backends/levm/mod.rs @@ -164,8 +164,11 @@ impl LEVM { ) -> Result, EvmError> { let mut account_updates: Vec = vec![]; for (address, new_state_account) in db.cache.drain() { - let initial_state_account = db.store.get_account(address)?; - let account_existed = db.store.account_exists(address); + let initial_state_account = db + .in_memory_db + .get(&address) + .cloned() + .unwrap_or(db.store.get_account(address)?); let mut acc_info_updated = false; let mut storage_updated = false; @@ -206,15 +209,16 @@ impl LEVM { // https://eips.ethereum.org/EIPS/eip-161 if fork >= Fork::SpuriousDragon { - // "No account may change state from non-existent to existent-but-_empty_. If an operation would do this, the account SHALL instead remain non-existent." - if !account_existed && new_state_account.is_empty() { - continue; - } - // "At the end of the transaction, any account touched by the execution of that transaction which is now empty SHALL instead become non-existent (i.e. deleted)." // Note: An account can be empty but still exist in the trie (if that's the case we remove it) if new_state_account.is_empty() { removed = true; + + // "No account may change state from non-existent to existent-but-_empty_. If an operation would do this, the account SHALL instead remain non-existent." + let account_existed = db.store.account_exists(address); + if !account_existed { + continue; + } } } @@ -233,6 +237,7 @@ impl LEVM { account_updates.push(account_update); } + db.in_memory_db.clear(); Ok(account_updates) } diff --git a/crates/vm/levm/src/db/gen_db.rs b/crates/vm/levm/src/db/gen_db.rs index 3aa2b35a16..d42bef3c74 100644 --- a/crates/vm/levm/src/db/gen_db.rs +++ b/crates/vm/levm/src/db/gen_db.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::sync::Arc; use bytes::Bytes; @@ -21,11 +22,16 @@ use super::Database; pub struct GeneralizedDatabase { pub store: Arc, pub cache: CacheDB, + pub in_memory_db: HashMap, } impl GeneralizedDatabase { pub fn new(store: Arc, cache: CacheDB) -> Self { - Self { store, cache } + Self { + store, + cache, + in_memory_db: HashMap::new(), + } } // ================== Account related functions ===================== @@ -35,18 +41,49 @@ impl GeneralizedDatabase { match cache::get_account(&self.cache, &address) { Some(acc) => Ok(acc.clone()), None => { - let account = self.store.get_account(address)?; + let account = self.get_account_from_database(address)?; cache::insert_account(&mut self.cache, address, account.clone()); Ok(account) } } } + /// Gets account from storage, storing in InMemoryDB for efficiency when getting AccountUpdates. + pub fn get_account_from_database( + &mut self, + address: Address, + ) -> Result { + let account = self.store.get_account(address)?; + self.in_memory_db.insert(address, account.clone()); + Ok(account) + } + + /// Gets storage slot from Database, storing in InMemoryDB for efficiency when getting AccountUpdates. + pub fn get_value_from_database( + &mut self, + address: Address, + key: H256, + ) -> Result { + let value = self.store.get_storage_value(address, key)?; + // Account must be already in in_memory_db + if let Some(account) = self.in_memory_db.get_mut(&address) { + account.storage.insert(key, value); + } else { + return Err(DatabaseError::Custom( + "Account not found in InMemoryDB".to_string(), + )); + } + Ok(value) + } + /// Gets account without pushing it to the cache - pub fn get_account_no_push_cache(&self, address: Address) -> Result { + pub fn get_account_no_push_cache( + &mut self, + address: Address, + ) -> Result { match cache::get_account(&self.cache, &address) { Some(acc) => Ok(acc.clone()), - None => self.store.get_account(address), + None => self.get_account_from_database(address), } } diff --git a/crates/vm/levm/src/utils.rs b/crates/vm/levm/src/utils.rs index 4835af78f4..dbff02577f 100644 --- a/crates/vm/levm/src/utils.rs +++ b/crates/vm/levm/src/utils.rs @@ -331,7 +331,7 @@ pub fn eip7702_recover_address( /// The idea of this function comes from ethereum/execution-specs: /// https://github.com/ethereum/execution-specs/blob/951fc43a709b493f27418a8e57d2d6f3608cef84/src/ethereum/prague/vm/eoa_delegation.py#L115 pub fn eip7702_get_code( - db: &GeneralizedDatabase, + db: &mut GeneralizedDatabase, accrued_substate: &mut Substate, address: Address, ) -> Result<(bool, u64, Address, Bytes), VMError> {