Skip to content

Commit

Permalink
perf: ignore all storage of empty accounts (#127)
Browse files Browse the repository at this point in the history
* Make MutDB generic

* Use code_by_hash to load contract code

* improve logging

* do not proof storage of empty accounts

---------

Co-authored-by: Rami <2418646+hashcashier@users.noreply.github.com>
  • Loading branch information
Wollac and hashcashier authored Dec 12, 2024
1 parent 4466ac7 commit 9ae3488
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 31 deletions.
69 changes: 38 additions & 31 deletions crates/core/src/stateless/initialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ use crate::driver::CoreDriver;
use crate::keccak::keccak;
use crate::mpt::MptNode;
use crate::stateless::data::StorageEntry;
use alloy_consensus::constants::EMPTY_ROOT_HASH;
use alloy_consensus::Account;
use alloy_primitives::map::HashMap;
use alloy_primitives::{Address, Bytes, B256, U256};
use anyhow::bail;
use anyhow::{bail, ensure};
use core::mem::take;
use reth_primitives::revm_primitives::Bytecode;
use reth_revm::db::{AccountState, DbAccount};
Expand Down Expand Up @@ -76,38 +77,44 @@ impl<Driver: CoreDriver> InitializationStrategy<Driver, MemoryDB> for MemoryDbSt
// consume the slots, as they are no longer needed afterward
let slots = take(slots);

// load the account from the state trie or empty if it does not exist
let state_account = state_trie
.get_rlp::<Account>(&keccak(address))?
.unwrap_or_default();
// Verify storage trie root
if storage_trie.hash() != state_account.storage_root {
bail!(
"Invalid storage trie for {:?}: expected {}, got {}",
address,
state_account.storage_root,
storage_trie.hash()
);
}
// load the account from the state trie
let state_account = state_trie.get_rlp::<Account>(&keccak(address))?;

// load storage reads
let mut storage = HashMap::with_capacity_and_hasher(slots.len(), Default::default());
for slot in slots {
let value: U256 = storage_trie
.get_rlp(&keccak(slot.to_be_bytes::<32>()))?
.unwrap_or_default();
storage.insert(slot, value);
}
// check that the account storage root matches the storage trie root of the input
let storage_root = state_account.map_or(EMPTY_ROOT_HASH, |a| a.storage_root);
ensure!(
storage_root == storage_trie.hash(),
"Invalid storage trie for {}: expected {}, got {}",
address,
storage_root,
storage_trie.hash()
);

// load the account into memory
let mem_account = match state_account {
None => DbAccount::new_not_existing(),
Some(state_account) => {
// load storage reads
let mut storage =
HashMap::with_capacity_and_hasher(slots.len(), Default::default());
for slot in slots {
let value: U256 = storage_trie
.get_rlp(&keccak(slot.to_be_bytes::<32>()))?
.unwrap_or_default();
storage.insert(slot, value);
}

let mem_account = DbAccount {
info: AccountInfo {
balance: state_account.balance,
nonce: state_account.nonce,
code_hash: state_account.code_hash,
code: None,
},
account_state: AccountState::None,
storage,
DbAccount {
info: AccountInfo {
balance: state_account.balance,
nonce: state_account.nonce,
code_hash: state_account.code_hash,
code: None,
},
account_state: AccountState::None,
storage,
}
}
};

accounts.insert(*address, mem_account);
Expand Down
6 changes: 6 additions & 0 deletions crates/preflight/src/provider/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ impl<N: Network, R: CoreDriver, P: PreflightDriver<R, N>> Database for ProviderD
.map_err(db_error)?;
let bytecode = Bytecode::new_raw(code);

// if the account is empty return None
// in the EVM, emptiness is treated as equivalent to nonexistence
if nonce.is_zero() && balance.is_zero() && bytecode.is_empty() {
return Ok(None);
}

// index the code by its hash, so that we can later use code_by_hash
let code_hash = bytecode.hash_slow();
self.contracts.insert(code_hash, bytecode);
Expand Down

0 comments on commit 9ae3488

Please sign in to comment.