Skip to content

feat(levm): add in memory db for AccountUpdates #2638

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

Closed
wants to merge 19 commits into from
Closed
19 changes: 12 additions & 7 deletions crates/vm/backends/levm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,11 @@ impl LEVM {
) -> Result<Vec<AccountUpdate>, EvmError> {
let mut account_updates: Vec<AccountUpdate> = 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;
Expand Down Expand Up @@ -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;
}
}
}

Expand All @@ -233,6 +237,7 @@ impl LEVM {

account_updates.push(account_update);
}
db.in_memory_db.clear();
Ok(account_updates)
}

Expand Down
45 changes: 41 additions & 4 deletions crates/vm/levm/src/db/gen_db.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::collections::HashMap;
use std::sync::Arc;

use bytes::Bytes;
Expand All @@ -21,11 +22,16 @@ use super::Database;
pub struct GeneralizedDatabase {
pub store: Arc<dyn Database>,
pub cache: CacheDB,
pub in_memory_db: HashMap<Address, Account>,
}

impl GeneralizedDatabase {
pub fn new(store: Arc<dyn Database>, cache: CacheDB) -> Self {
Self { store, cache }
Self {
store,
cache,
in_memory_db: HashMap::new(),
}
}

// ================== Account related functions =====================
Expand All @@ -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<Account, DatabaseError> {
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<U256, DatabaseError> {
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<Account, DatabaseError> {
pub fn get_account_no_push_cache(
&mut self,
address: Address,
) -> Result<Account, DatabaseError> {
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),
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/vm/levm/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
Loading