diff --git a/crates/l2/proposer/prover_server.rs b/crates/l2/proposer/prover_server.rs index ebb4473508..235ec5e382 100644 --- a/crates/l2/proposer/prover_server.rs +++ b/crates/l2/proposer/prover_server.rs @@ -405,7 +405,8 @@ impl ProverServer { let block = Block::new(header, body); - let db = ExecutionDB::from_exec(&block, &self.store).map_err(EvmError::ExecutionDB)?; + let db = + ExecutionDB::from_store(&block, self.store.clone()).map_err(EvmError::ExecutionDB)?; let parent_block_header = self .store diff --git a/crates/l2/prover/tests/perf_zkvm.rs b/crates/l2/prover/tests/perf_zkvm.rs index 8ee9ae0ea3..db3d80679e 100644 --- a/crates/l2/prover/tests/perf_zkvm.rs +++ b/crates/l2/prover/tests/perf_zkvm.rs @@ -80,7 +80,7 @@ async fn setup() -> (ProgramInput, Block) { } let block_to_prove = blocks.last().unwrap(); - let db = ExecutionDB::from_exec(block_to_prove, &store).unwrap(); + let db = ExecutionDB::from_store(block_to_prove, store.clone()).unwrap(); let parent_block_header = store .get_block_header_by_hash(block_to_prove.header.parent_hash) diff --git a/crates/l2/prover/zkvm/interface/risc0/src/main.rs b/crates/l2/prover/zkvm/interface/risc0/src/main.rs index 85ecec50a7..5350183c13 100644 --- a/crates/l2/prover/zkvm/interface/risc0/src/main.rs +++ b/crates/l2/prover/zkvm/interface/risc0/src/main.rs @@ -4,7 +4,7 @@ use ethrex_blockchain::{validate_block, validate_gas_used}; use ethrex_vm::{execute_block, get_state_transitions, EvmState}; use zkvm_interface::{ io::{ProgramInput, ProgramOutput}, - trie::update_tries, + trie::{update_tries, verify_db}, }; fn main() { @@ -15,27 +15,31 @@ fn main() { } = env::read(); let mut state = EvmState::from(db.clone()); - // Validate the block pre-execution + // Validate the block validate_block(&block, &parent_block_header, &state).expect("invalid block"); - // Validate the initial state + // Tries used for validating initial and final state root let (mut state_trie, mut storage_tries) = db - .build_tries() + .get_tries() .expect("failed to build state and storage tries or state is not valid"); + // Validate the initial state let initial_state_hash = state_trie.hash_no_commit(); if initial_state_hash != parent_block_header.state_root { panic!("invalid initial state trie"); } + if !verify_db(&db, &state_trie, &storage_tries).expect("failed to validate database") { + panic!("invalid database") + }; let receipts = execute_block(&block, &mut state).expect("failed to execute block"); validate_gas_used(&receipts, &block.header).expect("invalid gas used"); + // Output gas for measurement purposes let cumulative_gas_used = receipts .last() .map(|last_receipt| last_receipt.cumulative_gas_used) .unwrap_or_default(); - env::write(&cumulative_gas_used); let account_updates = get_state_transitions(&mut state); @@ -43,8 +47,8 @@ fn main() { // Update tries and calculate final state root hash update_tries(&mut state_trie, &mut storage_tries, &account_updates) .expect("failed to update state and storage tries"); - let final_state_hash = state_trie.hash_no_commit(); + let final_state_hash = state_trie.hash_no_commit(); if final_state_hash != block.header.state_root { panic!("invalid final state trie"); } diff --git a/crates/l2/prover/zkvm/interface/sp1/elf/riscv32im-succinct-zkvm-elf b/crates/l2/prover/zkvm/interface/sp1/elf/riscv32im-succinct-zkvm-elf index 01f51eafeb..51d206dee4 100755 Binary files a/crates/l2/prover/zkvm/interface/sp1/elf/riscv32im-succinct-zkvm-elf and b/crates/l2/prover/zkvm/interface/sp1/elf/riscv32im-succinct-zkvm-elf differ diff --git a/crates/l2/prover/zkvm/interface/sp1/src/main.rs b/crates/l2/prover/zkvm/interface/sp1/src/main.rs index 6ea6f9e3c5..3e582b5e76 100644 --- a/crates/l2/prover/zkvm/interface/sp1/src/main.rs +++ b/crates/l2/prover/zkvm/interface/sp1/src/main.rs @@ -4,7 +4,7 @@ use ethrex_blockchain::{validate_block, validate_gas_used}; use ethrex_vm::{execute_block, get_state_transitions, EvmState}; use zkvm_interface::{ io::{ProgramInput, ProgramOutput}, - trie::update_tries, + trie::{update_tries, verify_db}, }; sp1_zkvm::entrypoint!(main); @@ -15,30 +15,33 @@ pub fn main() { parent_block_header, db, } = sp1_zkvm::io::read::(); - let mut state = EvmState::from(db.clone()); - // Validate the block pre-execution + // Validate the block validate_block(&block, &parent_block_header, &state).expect("invalid block"); - // Validate the initial state + // Tries used for validating initial and final state root let (mut state_trie, mut storage_tries) = db - .build_tries() + .get_tries() .expect("failed to build state and storage tries or state is not valid"); + // Validate the initial state let initial_state_hash = state_trie.hash_no_commit(); if initial_state_hash != parent_block_header.state_root { panic!("invalid initial state trie"); } + if !verify_db(&db, &state_trie, &storage_tries).expect("failed to validate database") { + panic!("invalid database") + }; let receipts = execute_block(&block, &mut state).expect("failed to execute block"); validate_gas_used(&receipts, &block.header).expect("invalid gas used"); + // Output gas for measurement purposes let cumulative_gas_used = receipts .last() .map(|last_receipt| last_receipt.cumulative_gas_used) .unwrap_or_default(); - sp1_zkvm::io::commit(&cumulative_gas_used); let account_updates = get_state_transitions(&mut state); @@ -46,8 +49,8 @@ pub fn main() { // Update tries and calculate final state root hash update_tries(&mut state_trie, &mut storage_tries, &account_updates) .expect("failed to update state and storage tries"); - let final_state_hash = state_trie.hash_no_commit(); + let final_state_hash = state_trie.hash_no_commit(); if final_state_hash != block.header.state_root { panic!("invalid final state trie"); } diff --git a/crates/l2/prover/zkvm/interface/src/lib.rs b/crates/l2/prover/zkvm/interface/src/lib.rs index 078ef26d87..189e926eb8 100644 --- a/crates/l2/prover/zkvm/interface/src/lib.rs +++ b/crates/l2/prover/zkvm/interface/src/lib.rs @@ -78,10 +78,11 @@ pub mod io { pub mod trie { use std::collections::HashMap; - use ethrex_core::{types::AccountState, H160}; + use ethrex_core::{types::AccountState, H160, U256}; use ethrex_rlp::{decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError}; use ethrex_storage::{hash_address, hash_key, AccountUpdate}; use ethrex_trie::{Trie, TrieError}; + use ethrex_vm::execution_db::ExecutionDB; use thiserror::Error; #[derive(Debug, Error)] @@ -91,9 +92,58 @@ pub mod trie { #[error(transparent)] RLPDecode(#[from] RLPDecodeError), #[error("Missing storage trie for address {0}")] + MissingStorageTrie(H160), + #[error("Missing storage for address {0}")] StorageNotFound(H160), } + pub fn verify_db( + db: &ExecutionDB, + state_trie: &Trie, + storage_tries: &HashMap, + ) -> Result { + for (address, account_info) in &db.accounts { + let storage_trie = storage_tries + .get(address) + .ok_or(Error::MissingStorageTrie(*address))?; + let storage_root = storage_trie.hash_no_commit(); + + // verify account and storage trie are valid + let trie_account_state = match state_trie.get(&hash_address(address)) { + Ok(Some(encoded_state)) => AccountState::decode(&encoded_state)?, + Ok(None) | Err(TrieError::InconsistentTree) => return Ok(false), // account not in trie + Err(err) => return Err(err.into()), + }; + let db_account_state = AccountState { + nonce: account_info.nonce, + balance: account_info.balance, + code_hash: account_info.code_hash, + storage_root, + }; + if db_account_state != trie_account_state { + return Ok(false); + } + + // verify storage + for (key, db_value) in db + .storage + .get(address) + .ok_or(Error::StorageNotFound(*address))? + { + let trie_value = match storage_trie.get(&hash_key(key)) { + Ok(Some(encoded)) => U256::decode(&encoded)?, + Ok(None) | Err(TrieError::InconsistentTree) => return Ok(false), // value not in trie + Err(err) => return Err(err.into()), + }; + if *db_value != trie_value { + return Ok(false); + } + } + } + + Ok(true) + } + pub fn update_tries( state_trie: &mut Trie, storage_tries: &mut HashMap, @@ -133,7 +183,7 @@ pub mod trie { } else { storage_tries .get_mut(&update.address) - .ok_or(Error::StorageNotFound(update.address))? + .ok_or(Error::MissingStorageTrie(update.address))? }; for (storage_key, storage_value) in &update.added_storage { let hashed_key = hash_key(storage_key); diff --git a/crates/l2/utils/test_data_io.rs b/crates/l2/utils/test_data_io.rs index 9773a2de66..34d9c8949a 100644 --- a/crates/l2/utils/test_data_io.rs +++ b/crates/l2/utils/test_data_io.rs @@ -81,7 +81,7 @@ pub fn generate_program_input( .ok_or(ProverInputError::InvalidParentBlock( block.header.parent_hash, ))?; - let db = ExecutionDB::from_exec(&block, &store)?; + let db = ExecutionDB::from_store(&block, store)?; Ok(ProgramInput { db, diff --git a/crates/vm/db.rs b/crates/vm/db.rs index cb9024673a..5528ee6442 100644 --- a/crates/vm/db.rs +++ b/crates/vm/db.rs @@ -111,3 +111,54 @@ impl revm::Database for StoreWrapper { .ok_or_else(|| StoreError::Custom(format!("Block {number} not found"))) } } + +impl revm::DatabaseRef for StoreWrapper { + type Error = StoreError; + + fn basic_ref(&self, address: RevmAddress) -> Result, Self::Error> { + let acc_info = match self + .store + .get_account_info_by_hash(self.block_hash, CoreAddress::from(address.0.as_ref()))? + { + None => return Ok(None), + Some(acc_info) => acc_info, + }; + let code = self + .store + .get_account_code(acc_info.code_hash)? + .map(|b| RevmBytecode::new_raw(RevmBytes(b))); + + Ok(Some(RevmAccountInfo { + balance: RevmU256::from_limbs(acc_info.balance.0), + nonce: acc_info.nonce, + code_hash: RevmB256::from(acc_info.code_hash.0), + code, + })) + } + + fn code_by_hash_ref(&self, code_hash: RevmB256) -> Result { + self.store + .get_account_code(CoreH256::from(code_hash.as_ref()))? + .map(|b| RevmBytecode::new_raw(RevmBytes(b))) + .ok_or_else(|| StoreError::Custom(format!("No code for hash {code_hash}"))) + } + + fn storage_ref(&self, address: RevmAddress, index: RevmU256) -> Result { + Ok(self + .store + .get_storage_at_hash( + self.block_hash, + CoreAddress::from(address.0.as_ref()), + CoreH256::from(index.to_be_bytes()), + )? + .map(|value| RevmU256::from_limbs(value.0)) + .unwrap_or_else(|| RevmU256::ZERO)) + } + + fn block_hash_ref(&self, number: u64) -> Result { + self.store + .get_block_header(number)? + .map(|header| RevmB256::from_slice(&header.compute_block_hash().0)) + .ok_or_else(|| StoreError::Custom(format!("Block {number} not found"))) + } +} diff --git a/crates/vm/errors.rs b/crates/vm/errors.rs index 58dea2e02d..a80ae2a787 100644 --- a/crates/vm/errors.rs +++ b/crates/vm/errors.rs @@ -1,5 +1,5 @@ use ethereum_types::{H160, H256}; -use ethrex_core::types::BlockHash; +use ethrex_core::{types::BlockHash, Address}; use ethrex_storage::error::StoreError; use ethrex_trie::TrieError; use revm::primitives::{ @@ -43,14 +43,18 @@ pub enum ExecutionDBError { StorageValueNotFound(RevmAddress, RevmU256), #[error("Hash of block with number {0} not found")] BlockHashNotFound(u64), - #[error("Missing account {0} info while trying to create ExecutionDB")] - NewMissingAccountInfo(RevmAddress), #[error("Missing state trie of block {0} while trying to create ExecutionDB")] NewMissingStateTrie(BlockHash), #[error( "Missing storage trie of block {0} and address {1} while trying to create ExecutionDB" )] - NewMissingStorageTrie(BlockHash, H160), + NewMissingStorageTrie(BlockHash, Address), + #[error("Missing account {0} info while trying to create ExecutionDB")] + NewMissingAccountInfo(Address), + #[error("Missing storage of address {0} and key {1} while trying to create ExecutionDB")] + NewMissingStorage(Address, H256), + #[error("Missing code of hash {0} while trying to create ExecutionDB")] + NewMissingCode(H256), #[error("The account {0} is not included in the stored pruned state trie")] MissingAccountInStateTrie(H160), #[error("Missing storage trie of account {0}")] diff --git a/crates/vm/execution_db.rs b/crates/vm/execution_db.rs index 5a43fce1bd..2bf2b8cafc 100644 --- a/crates/vm/execution_db.rs +++ b/crates/vm/execution_db.rs @@ -1,131 +1,165 @@ use std::collections::HashMap; +use bytes::Bytes; use ethereum_types::H160; use ethrex_core::{ - types::{AccountState, Block, ChainConfig}, - H256, + types::{AccountInfo, Block, ChainConfig}, + Address, H256, U256, }; -use ethrex_rlp::encode::RLPEncode; use ethrex_storage::{hash_address, hash_key, AccountUpdate, Store}; -use ethrex_trie::{NodeRLP, Trie}; +use ethrex_trie::{NodeRLP, Trie, TrieError}; use revm::{ + db::CacheDB, + inspectors::TracerEip3155, primitives::{ - AccountInfo as RevmAccountInfo, Address as RevmAddress, Bytecode as RevmBytecode, - B256 as RevmB256, U256 as RevmU256, + result::EVMError as RevmError, AccountInfo as RevmAccountInfo, Address as RevmAddress, + Bytecode as RevmBytecode, Bytes as RevmBytes, B256 as RevmB256, U256 as RevmU256, }, - DatabaseRef, + Database, DatabaseRef, Evm, }; +use revm_primitives::SpecId; use serde::{Deserialize, Serialize}; -use crate::{errors::ExecutionDBError, evm_state, execute_block, get_state_transitions}; +use crate::{ + block_env, db::StoreWrapper, errors::ExecutionDBError, evm_state, execute_block, + get_state_transitions, spec_id, tx_env, EvmError, +}; -/// In-memory EVM database for caching execution data. +/// In-memory EVM database for single execution data. /// -/// This is mainly used to store the relevant state data for executing a particular block and then +/// This is mainly used to store the relevant state data for executing a single block and then /// feeding the DB into a zkVM program to prove the execution. #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct ExecutionDB { /// indexed by account address - pub accounts: HashMap, + pub accounts: HashMap, /// indexed by code hash - pub code: HashMap, + pub code: HashMap, /// indexed by account address and storage key - pub storage: HashMap>, + pub storage: HashMap>, /// indexed by block number - pub block_hashes: HashMap, + pub block_hashes: HashMap, /// stored chain config pub chain_config: ChainConfig, - /// encoded nodes to reconstruct a state trie, but only including relevant data (pruned). - /// root node is stored separately from the rest. - pub pruned_state_trie: (Option, Vec), - /// encoded nodes to reconstruct every storage trie, but only including relevant data (pruned) - /// root nodes are stored separately from the rest. - pub pruned_storage_tries: HashMap, Vec)>, + /// Encoded nodes to reconstruct a state trie, but only including relevant data ("pruned trie"). + /// + /// Root node is stored separately from the rest as the first tuple member. + pub state_proofs: (Option, Vec), + /// Encoded nodes to reconstruct every storage trie, but only including relevant data ("pruned + /// trie"). + /// + /// Root node is stored separately from the rest as the first tuple member. + pub storage_proofs: HashMap, Vec)>, } impl ExecutionDB { - /// Creates a database and returns the ExecutionDB by executing a block, - /// without performing any validation. - pub fn from_exec(block: &Block, store: &Store) -> Result { - // TODO: perform validation to exit early - let account_updates = Self::get_account_updates(block, store)?; - Self::from_account_updates(account_updates, block, store) - } - - /// Creates a database and returns the ExecutionDB from a Vec<[AccountUpdate]>, - /// without performing any validation. - pub fn from_account_updates( - account_updates: Vec, - block: &Block, - store: &Store, - ) -> Result { - // TODO: perform validation to exit early + /// Creates a database and returns the ExecutionDB by "pre-executing" a block, + /// without performing any validation, and retrieving data from a [Store]. + pub fn from_store(block: &Block, store: Store) -> Result { + let parent_hash = block.header.parent_hash; let chain_config = store.get_chain_config()?; + let store_wrapper = StoreWrapper { + store: store.clone(), + block_hash: parent_hash, + }; - // Store data touched by updates and get all touched storage keys for each account - let mut accounts = HashMap::new(); - let code = HashMap::new(); // TODO: `code` remains empty for now - let mut storage = HashMap::new(); - let block_hashes = HashMap::new(); // TODO: `block_hashes` remains empty for now - - let mut address_storage_keys = HashMap::new(); - - for account_update in account_updates.iter() { - let address = RevmAddress::from_slice(account_update.address.as_bytes()); - let account_state = match store.get_account_state_by_hash( - block.header.parent_hash, - H160::from_slice(address.as_slice()), - )? { - Some(state) => state, - None => continue, - }; - accounts.insert(address, account_state); - - let account_storage = account_update - .added_storage - .iter() - .map(|(key, value)| { - ( - RevmU256::from_be_bytes(key.to_fixed_bytes()), - RevmU256::from_be_slice(&value.to_big_endian()), - ) - }) - .collect(); - storage.insert(address, account_storage); - address_storage_keys.insert( - account_update.address, - account_update - .added_storage - .keys() - .cloned() - .collect::>(), - ); - } - - // Get pruned state and storage tries. For this we get the "state" (all relevant nodes) of every trie. - // "Pruned" because we're only getting the nodes that make paths to the relevant - // key-values. - let state_trie = store.state_trie(block.header.parent_hash)?.ok_or( - ExecutionDBError::NewMissingStateTrie(block.header.parent_hash), - )?; - - // Get pruned state trie - let state_paths: Vec<_> = address_storage_keys.keys().map(hash_address).collect(); - let pruned_state_trie = state_trie.get_proofs(&state_paths)?; - - // Get pruned storage tries for every account - let mut pruned_storage_tries = HashMap::new(); - for (address, keys) in address_storage_keys { - let storage_trie = store - .storage_trie(block.header.parent_hash, address)? - .ok_or(ExecutionDBError::NewMissingStorageTrie( - block.header.parent_hash, + // pre-execute and get all state changes + let cache = Self::pre_execute( + block, + chain_config.chain_id, + spec_id(&chain_config, block.header.timestamp), + store_wrapper, + ) + .map_err(|err| Box::new(EvmError::from(err)))?; // TODO: must be a better way + let store_wrapper = cache.db; + + // fetch all read/written values from store + let already_existing_accounts = cache + .accounts + .iter() + // filter out new accounts, we're only interested in already existing accounts. + // new accounts are storage cleared, self-destructed accounts too but they're marked with "not + // existing" status instead. + .filter_map(|(address, account)| { + if !account.account_state.is_storage_cleared() { + Some((Address::from(address.0.as_ref()), account)) + } else { + None + } + }); + let accounts = already_existing_accounts + .clone() + .map(|(address, _)| { + // return error if account is missing + let account = match store_wrapper + .store + .get_account_info_by_hash(parent_hash, address) + { + Ok(None) => Err(ExecutionDBError::NewMissingAccountInfo(address)), + Ok(Some(some)) => Ok(some), + Err(err) => Err(ExecutionDBError::Store(err)), + }; + Ok((address, account?)) + }) + .collect::, ExecutionDBError>>()?; + let code = already_existing_accounts + .clone() + .map(|(_, account)| { + // return error if code is missing + let hash = H256::from(account.info.code_hash.0); + Ok(( + hash, + store_wrapper + .store + .get_account_code(hash)? + .ok_or(ExecutionDBError::NewMissingCode(hash))?, + )) + }) + .collect::>()?; + let storage = already_existing_accounts + .map(|(address, account)| { + // return error if storage is missing + Ok(( address, - ))?; - let storage_paths: Vec<_> = keys.iter().map(hash_key).collect(); - let (storage_trie_root, storage_trie_nodes) = - storage_trie.get_proofs(&storage_paths)?; - pruned_storage_tries.insert(address, (storage_trie_root, storage_trie_nodes)); + account + .storage + .keys() + .map(|key| { + let key = H256::from(key.to_be_bytes()); + let value = store_wrapper + .store + .get_storage_at_hash(parent_hash, address, key) + .map_err(ExecutionDBError::Store)? + .ok_or(ExecutionDBError::NewMissingStorage(address, key))?; + Ok((key, value)) + }) + .collect::, ExecutionDBError>>()?, + )) + }) + .collect::, ExecutionDBError>>()?; + let block_hashes = cache + .block_hashes + .into_iter() + .map(|(num, hash)| (num.try_into().unwrap(), H256::from(hash.0))) + .collect(); + // WARN: unwrapping because revm wraps a u64 as a U256 + + // get proofs + let state_trie = store + .state_trie(parent_hash)? + .ok_or(ExecutionDBError::NewMissingStateTrie(parent_hash))?; + + let state_proofs = + state_trie.get_proofs(&accounts.keys().map(hash_address).collect::>())?; + + let mut storage_proofs = HashMap::new(); + for (address, storages) in &storage { + let storage_trie = store.storage_trie(parent_hash, *address)?.ok_or( + ExecutionDBError::NewMissingStorageTrie(parent_hash, *address), + )?; + + let paths = storages.keys().map(hash_key).collect::>(); + storage_proofs.insert(*address, storage_trie.get_proofs(&paths)?); } Ok(Self { @@ -134,8 +168,8 @@ impl ExecutionDB { storage, block_hashes, chain_config, - pruned_state_trie, - pruned_storage_tries, + state_proofs, + storage_proofs, }) } @@ -158,52 +192,61 @@ impl ExecutionDB { self.chain_config } - /// Verifies that all data in [self] is included in the stored tries, and then builds the - /// pruned tries from the stored nodes. - pub fn build_tries(&self) -> Result<(Trie, HashMap), ExecutionDBError> { - let (state_trie_root, state_trie_nodes) = &self.pruned_state_trie; + /// Recreates the state trie and storage tries from the encoded nodes. + pub fn get_tries(&self) -> Result<(Trie, HashMap), ExecutionDBError> { + let (state_trie_root, state_trie_nodes) = &self.state_proofs; let state_trie = Trie::from_nodes(state_trie_root.as_ref(), state_trie_nodes)?; - let mut storage_tries = HashMap::new(); - - for (revm_address, account) in &self.accounts { - let address = H160::from_slice(revm_address.as_slice()); - - // check account is in state trie - if state_trie.get(&hash_address(&address))?.is_none() { - return Err(ExecutionDBError::MissingAccountInStateTrie(address)); - } - let (storage_trie_root, storage_trie_nodes) = - self.pruned_storage_tries - .get(&address) - .ok_or(ExecutionDBError::MissingStorageTrie(address))?; + let storage_trie = self + .storage_proofs + .iter() + .map(|(address, nodes)| { + let (storage_trie_root, storage_trie_nodes) = nodes; + let trie = Trie::from_nodes(storage_trie_root.as_ref(), storage_trie_nodes)?; + Ok((*address, trie)) + }) + .collect::>()?; + + Ok((state_trie, storage_trie)) + } - // compare account storage root with storage trie root - let storage_trie = Trie::from_nodes(storage_trie_root.as_ref(), storage_trie_nodes)?; - if storage_trie.hash_no_commit() != account.storage_root { - return Err(ExecutionDBError::InvalidStorageTrieRoot(address)); - } + /// Execute a block and cache all state changes, returns the cache + fn pre_execute( + block: &Block, + chain_id: u64, + spec_id: SpecId, + db: ExtDB, + ) -> Result, RevmError> { + let block_env = block_env(&block.header); + let mut db = CacheDB::new(db); + + for transaction in &block.body.transactions { + let tx_env = tx_env(transaction); + + // execute tx + let evm_builder = Evm::builder() + .with_block_env(block_env.clone()) + .with_tx_env(tx_env) + .modify_cfg_env(|cfg| { + cfg.chain_id = chain_id; + }) + .with_spec_id(spec_id) + .with_external_context( + TracerEip3155::new(Box::new(std::io::stderr())).without_summary(), + ); + let mut evm = evm_builder.with_db(&mut db).build(); + evm.transact_commit()?; + } - // check all storage keys are in storage trie and compare values - let storage = self - .storage - .get(revm_address) - .ok_or(ExecutionDBError::StorageNotFound(*revm_address))?; - for (key, value) in storage { - let key = H256::from_slice(&key.to_be_bytes_vec()); - let value = H256::from_slice(&value.to_be_bytes_vec()); - let retrieved_value = storage_trie - .get(&hash_key(&key))? - .ok_or(ExecutionDBError::MissingKeyInStorageTrie(address, key))?; - if value.encode_to_vec() != retrieved_value { - return Err(ExecutionDBError::InvalidStorageTrieValue(address, key)); - } + // add withdrawal accounts + if let Some(ref withdrawals) = block.body.withdrawals { + for withdrawal in withdrawals { + db.basic(RevmAddress::from_slice(withdrawal.address.as_bytes())) + .map_err(RevmError::Database)?; } - - storage_tries.insert(address, storage_trie); } - Ok((state_trie, storage_tries)) + Ok(db) } } @@ -213,14 +256,14 @@ impl DatabaseRef for ExecutionDB { /// Get basic account information. fn basic_ref(&self, address: RevmAddress) -> Result, Self::Error> { - let Some(account_state) = self.accounts.get(&address) else { + let Some(account_info) = self.accounts.get(&Address::from(address.0.as_ref())) else { return Ok(None); }; Ok(Some(RevmAccountInfo { - balance: RevmU256::from_be_bytes(account_state.balance.to_big_endian()), - nonce: account_state.nonce, - code_hash: RevmB256::from_slice(account_state.code_hash.as_bytes()), + balance: RevmU256::from_limbs(account_info.balance.0), + nonce: account_info.nonce, + code_hash: RevmB256::from_slice(&account_info.code_hash.0), code: None, })) } @@ -228,18 +271,18 @@ impl DatabaseRef for ExecutionDB { /// Get account code by its hash. fn code_by_hash_ref(&self, code_hash: RevmB256) -> Result { self.code - .get(&code_hash) - .cloned() + .get(&H256::from(code_hash.as_ref())) + .map(|b| RevmBytecode::new_raw(RevmBytes(b.clone()))) .ok_or(ExecutionDBError::CodeNotFound(code_hash)) } /// Get storage value of address at index. fn storage_ref(&self, address: RevmAddress, index: RevmU256) -> Result { self.storage - .get(&address) + .get(&Address::from(address.0.as_ref())) .ok_or(ExecutionDBError::AccountNotFound(address))? - .get(&index) - .cloned() + .get(&H256::from(index.to_be_bytes())) + .map(|v| RevmU256::from_limbs(v.0)) .ok_or(ExecutionDBError::StorageValueNotFound(address, index)) } @@ -247,7 +290,7 @@ impl DatabaseRef for ExecutionDB { fn block_hash_ref(&self, number: u64) -> Result { self.block_hashes .get(&number) - .cloned() + .map(|h| RevmB256::from_slice(&h.0)) .ok_or(ExecutionDBError::BlockHashNotFound(number)) } } diff --git a/crates/vm/vm.rs b/crates/vm/vm.rs index f6649ea6a6..a52b3b86e6 100644 --- a/crates/vm/vm.rs +++ b/crates/vm/vm.rs @@ -670,10 +670,8 @@ pub fn get_state_transitions(state: &mut EvmState) -> Vec { let mut account_update = AccountUpdate::new(address); // Update account info in DB if let Some(new_acc_info) = account.info() { - let code_hash = H256::from_slice(new_acc_info.code_hash.as_slice()); - // If code changed, update - if matches!(db.db.accounts.get(revm_address), Some(account) if account.code_hash != code_hash) + if matches!(db.db.accounts.get(&address), Some(account) if B256::from(account.code_hash.0) != new_acc_info.code_hash) { account_update.code = new_acc_info .code @@ -681,7 +679,7 @@ pub fn get_state_transitions(state: &mut EvmState) -> Vec { } let account_info = AccountInfo { - code_hash, + code_hash: H256::from_slice(new_acc_info.code_hash.as_slice()), balance: U256::from_little_endian(new_acc_info.balance.as_le_slice()), nonce: new_acc_info.nonce, };