From 81b5a4129f92e14661d2a2d8a06cc0b1a322bdd3 Mon Sep 17 00:00:00 2001 From: Jonathan LEI Date: Wed, 11 Sep 2024 10:34:07 +0800 Subject: [PATCH] fix: unconstrained block hashes --- .github/workflows/pr.yml | 2 +- Cargo.lock | 1 - crates/executor/client/src/io.rs | 35 ++++++++++++++++++++++++------- crates/executor/client/src/lib.rs | 2 +- crates/executor/host/src/lib.rs | 20 +++++++++++------- crates/storage/rpc-db/Cargo.toml | 1 - crates/storage/rpc-db/src/lib.rs | 28 +++++++------------------ 7 files changed, 51 insertions(+), 38 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index da75b9b..e0daf89 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -55,7 +55,7 @@ jobs: - name: "Set up test fixture" run: | - git clone https://github.com/succinctlabs/rsp-tests --branch 2024-09-09 --depth 1 ../rsp-tests + git clone https://github.com/succinctlabs/rsp-tests --branch 2024-09-11 --depth 1 ../rsp-tests cd ../rsp-tests/ docker compose up -d diff --git a/Cargo.lock b/Cargo.lock index 396f05b..3c0f16f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5421,7 +5421,6 @@ dependencies = [ "reth-trie", "revm-primitives", "rsp-primitives", - "rsp-witness-db", "thiserror", "tokio", "tracing", diff --git a/crates/executor/client/src/io.rs b/crates/executor/client/src/io.rs index 2354e90..acb0dd3 100644 --- a/crates/executor/client/src/io.rs +++ b/crates/executor/client/src/io.rs @@ -1,6 +1,7 @@ -use std::collections::HashMap; +use std::{collections::HashMap, iter::once}; use eyre::Result; +use itertools::Itertools; use reth_primitives::{revm_primitives::AccountInfo, Address, Block, Header, B256, U256}; use reth_trie::TrieAccount; use revm_primitives::{keccak256, Bytecode}; @@ -17,26 +18,30 @@ use serde::{Deserialize, Serialize}; pub struct ClientExecutorInput { /// The current block (which will be executed inside the client). pub current_block: Block, - /// The previous block header. - pub previous_block: Header, + /// The previous block headers starting from the most recent. There must be at least one header + /// to provide the parent state root. + pub ancestor_headers: Vec
, /// Network state as of the parent block. pub parent_state: EthereumState, /// Requests to account state and storage slots. pub state_requests: HashMap>, /// Account bytecodes. pub bytecodes: Vec, - /// The block hashes. - pub block_hashes: HashMap, } impl ClientExecutorInput { + /// Gets the immediate parent block's header. + pub fn parent_header(&self) -> &Header { + &self.ancestor_headers[0] + } + /// Creates a [WitnessDb] from a [ClientExecutorInput]. To do so, it verifies the used storage /// proofs and constructs the account and storage values. /// /// Note: This mutates the input and takes ownership of used storage proofs and block hashes /// to avoid unnecessary cloning. pub fn witness_db(&mut self) -> Result { - let state_root: B256 = self.previous_block.state_root; + let state_root: B256 = self.parent_header().state_root; if state_root != self.parent_state.state_root() { eyre::bail!("parent state root mismatch"); } @@ -93,6 +98,22 @@ impl ClientExecutorInput { } } - Ok(WitnessDb { accounts, storage, block_hashes: std::mem::take(&mut self.block_hashes) }) + // Verify and build block hashes + let mut block_hashes: HashMap = HashMap::new(); + for (child_header, parent_header) in + once(&self.current_block.header).chain(self.ancestor_headers.iter()).tuple_windows() + { + if parent_header.number != child_header.number - 1 { + eyre::bail!("non-consecutive blocks"); + } + + if parent_header.hash_slow() != child_header.parent_hash { + eyre::bail!("parent hash mismatch"); + } + + block_hashes.insert(parent_header.number, child_header.parent_hash); + } + + Ok(WitnessDb { accounts, storage, block_hashes }) } } diff --git a/crates/executor/client/src/lib.rs b/crates/executor/client/src/lib.rs index 113cddb..cdb117c 100644 --- a/crates/executor/client/src/lib.rs +++ b/crates/executor/client/src/lib.rs @@ -158,7 +158,7 @@ impl ClientExecutor { // // Note: the receipts root and gas used are verified by `validate_block_post_execution`. let mut header = input.current_block.header.clone(); - header.parent_hash = input.previous_block.hash_slow(); + header.parent_hash = input.parent_header().hash_slow(); header.ommers_hash = proofs::calculate_ommers_root(&input.current_block.ommers); header.state_root = input.current_block.state_root; header.transactions_root = proofs::calculate_transaction_root(&input.current_block.body); diff --git a/crates/executor/host/src/lib.rs b/crates/executor/host/src/lib.rs index faf0b46..7e190f5 100644 --- a/crates/executor/host/src/lib.rs +++ b/crates/executor/host/src/lib.rs @@ -68,11 +68,7 @@ impl + Clone> HostExecutor + Clone> HostExecutor { pub provider: P, /// The block to fetch data from. pub block: BlockId, - /// The state root of the block. - pub state_root: B256, /// The cached accounts. pub accounts: RefCell>, /// The cached storage values. pub storage: RefCell>>, - /// The cached block hashes. - pub block_hashes: RefCell>, + /// The oldest block whose header/hash has been requested. + pub oldest_ancestor: RefCell, /// A phantom type to make the struct generic over the transport. pub _phantom: PhantomData, } @@ -48,14 +45,13 @@ pub enum RpcDbError { impl + Clone> RpcDb { /// Create a new [`RpcDb`]. - pub fn new(provider: P, block: BlockId, state_root: B256) -> Self { + pub fn new(provider: P, block: u64) -> Self { RpcDb { provider, - block, - state_root, + block: block.into(), accounts: RefCell::new(HashMap::new()), storage: RefCell::new(HashMap::new()), - block_hashes: RefCell::new(HashMap::new()), + oldest_ancestor: RefCell::new(block), _phantom: PhantomData, } } @@ -133,7 +129,9 @@ impl + Clone> RpcDb { // Record the block hash to the state. let block = block.ok_or(RpcDbError::BlockNotFound)?; let hash = block.header.hash; - self.block_hashes.borrow_mut().insert(number, hash); + + let mut oldest_ancestor = self.oldest_ancestor.borrow_mut(); + *oldest_ancestor = number.min(*oldest_ancestor); Ok(hash) } @@ -210,13 +208,3 @@ impl + Clone> DatabaseRef for R Ok(value) } } - -impl> From> for WitnessDb { - fn from(value: RpcDb) -> Self { - Self { - accounts: value.accounts.borrow().clone(), - storage: value.storage.borrow().clone(), - block_hashes: value.block_hashes.borrow().clone(), - } - } -}