diff --git a/crates/executor/guest/src/error.rs b/crates/executor/guest/src/error.rs index 557bd8e..8093320 100644 --- a/crates/executor/guest/src/error.rs +++ b/crates/executor/guest/src/error.rs @@ -2,6 +2,7 @@ use alloy_primitives::{Address, FixedBytes}; use mpt::Error as MptError; use reth_consensus::ConsensusError; use reth_evm::execute::BlockExecutionError; +use revm_primitives::U256; #[derive(Debug, thiserror::Error)] pub enum ClientError { @@ -31,4 +32,8 @@ pub enum ClientError { FailedToReadGenesisFile(#[from] std::io::Error), #[error("Failed to deserialize the genesis file: {}", .0)] FailedToDeserializeGenesisFile(#[from] serde_json::Error), + #[error("Failed to check slot and value: {}", .0)] + FailedToCheckSlotAndValue(U256), + #[error("Failed to fetch slot and value: {}", .0)] + FailedToFetchSlotAndValue(U256), } diff --git a/crates/executor/guest/src/executor.rs b/crates/executor/guest/src/executor.rs index 2520ab6..8affb26 100644 --- a/crates/executor/guest/src/executor.rs +++ b/crates/executor/guest/src/executor.rs @@ -13,9 +13,9 @@ use reth_evm::{ use reth_evm_ethereum::EthEvmConfig; use reth_execution_types::ExecutionOutcome; use reth_primitives_traits::Block; -use reth_trie::KeccakKeyHasher; -use revm::{database::WrapDatabaseRef, install_crypto}; -use revm_primitives::Address; +use reth_trie::{KeccakKeyHasher, TrieAccount, EMPTY_ROOT_HASH}; +use revm::{database::WrapDatabaseRef, install_crypto, DatabaseRef}; +use revm_primitives::{Address, HashMap, U256}; use crate::{ custom::{CustomCrypto, CustomEvmFactory}, @@ -33,6 +33,7 @@ pub const BLOCK_EXECUTION: &str = "block execution"; pub const VALIDATE_HEADER: &str = "validate header"; pub const VALIDATE_EXECUTION: &str = "validate block post-execution"; pub const COMPUTE_STATE_ROOT: &str = "compute state root"; +pub const CHECK_SLOT_AND_VALUE: &str = "check slot and value"; pub type EthClientExecutor = ClientExecutor, ChainSpec>; @@ -55,6 +56,7 @@ where pub fn execute( &self, mut input: ClientExecutorInput, + storage_info: Option>, ) -> Result<(Header, B256), ClientError> { let chain_id: u64 = (&input.genesis).try_into().expect("convert chain id err"); @@ -152,6 +154,69 @@ where requests_hash: input.current_block.header().requests_hash(), }; + if storage_info.is_some() { + let check_result: Result<(), ClientError> = profile_report!(CHECK_SLOT_AND_VALUE, { + let state = input.state(); + let db = { + for (hashed_address, storage_trie) in state.storage_tries.iter() { + let account = state + .state_trie + .get_rlp::(hashed_address.as_slice()) + .unwrap(); + let storage_root = account.map_or(EMPTY_ROOT_HASH, |a| a.storage_root); + if storage_root != storage_trie.hash() { + return Err(ClientError::MismatchedStorageRoot); + } + } + + let bytecodes_by_hash = input + .bytecodes() + .map(|code| (code.hash_slow(), code)) + .collect::>(); + + // Verify and build block hashes + let mut block_hashes: HashMap = + HashMap::with_hasher(Default::default()); + for (child_header, parent_header) in input.sealed_headers().tuple_windows() { + if parent_header.number() != child_header.number() - 1 { + return Err(ClientError::InvalidHeaderBlockNumber( + parent_header.number() + 1, + child_header.number(), + )); + } + + let parent_header_hash = parent_header.hash_slow(); + if parent_header_hash != child_header.parent_hash() { + return Err(ClientError::InvalidHeaderParentHash( + parent_header_hash, + child_header.parent_hash(), + )); + } + + block_hashes.insert(parent_header.number(), child_header.parent_hash()); + } + + TrieDB::new(state, block_hashes, bytecodes_by_hash) + }; + + for (contract_address, slot_id, expected_value) in storage_info.unwrap() { + match db.storage_ref(contract_address, slot_id) { + Ok(actual_value) => { + if actual_value != expected_value { + return Err(ClientError::FailedToCheckSlotAndValue(slot_id)); + } + } + _ => { + return Err(ClientError::FailedToFetchSlotAndValue(slot_id)); + } + } + } + Ok(()) + }); + if check_result.is_err() { + return Err(check_result.err().unwrap()); + } + } Ok((header, parent_state_root)) } } diff --git a/crates/executor/guest/src/lib.rs b/crates/executor/guest/src/lib.rs index bb754b7..85d8dbb 100644 --- a/crates/executor/guest/src/lib.rs +++ b/crates/executor/guest/src/lib.rs @@ -27,7 +27,8 @@ pub fn verify_block(input: &[u8]) -> (B256, B256, B256) { Arc::new((&input.genesis).try_into().unwrap()), input.custom_beneficiary, ); - let (header, prev_state_root) = executor.execute(input).expect("failed to execute client"); + let (header, prev_state_root) = + executor.execute(input, None).expect("failed to execute client"); let block_hash = header.hash_slow(); (block_hash, header.state_root, prev_state_root) } diff --git a/crates/executor/host/tests/integration.rs b/crates/executor/host/tests/integration.rs index b246c31..19bc1c2 100644 --- a/crates/executor/host/tests/integration.rs +++ b/crates/executor/host/tests/integration.rs @@ -123,7 +123,7 @@ async fn run_e2e( .expect("failed to execute host"); // Execute the client. - client_executor.execute(client_input.clone()).expect("failed to execute client"); + client_executor.execute(client_input.clone(), None).expect("failed to execute client"); // Save the client input to a buffer. let buffer = bincode::serialize(&client_input).unwrap();