Skip to content

Commit

Permalink
fix: unconstrained block hashes (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
xJonathanLEI authored Sep 11, 2024
1 parent 4d57234 commit 2850e25
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 38 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 28 additions & 7 deletions crates/executor/client/src/io.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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<Header>,
/// Network state as of the parent block.
pub parent_state: EthereumState,
/// Requests to account state and storage slots.
pub state_requests: HashMap<Address, Vec<U256>>,
/// Account bytecodes.
pub bytecodes: Vec<Bytecode>,
/// The block hashes.
pub block_hashes: HashMap<u64, B256>,
}

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<WitnessDb> {
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");
}
Expand Down Expand Up @@ -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<u64, B256> = 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 })
}
}
2 changes: 1 addition & 1 deletion crates/executor/client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
20 changes: 13 additions & 7 deletions crates/executor/host/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,7 @@ impl<T: Transport + Clone, P: Provider<T, AnyNetwork> + Clone> HostExecutor<T, P

// Setup the database for the block executor.
tracing::info!("setting up the database for the block executor");
let rpc_db = RpcDb::new(
self.provider.clone(),
(block_number - 1).into(),
previous_block.header.state_root,
);
let rpc_db = RpcDb::new(self.provider.clone(), block_number - 1);
let cache_db = CacheDB::new(&rpc_db);

// Execute the block and fetch all the necessary data along the way.
Expand Down Expand Up @@ -199,15 +195,25 @@ impl<T: Transport + Clone, P: Provider<T, AnyNetwork> + Clone> HostExecutor<T, P
state_root
);

// Fetch the parent headers needed to constrain the BLOCKHASH opcode.
let oldest_ancestor = *rpc_db.oldest_ancestor.borrow();
let mut ancestor_headers = vec![];
tracing::info!("fetching {} ancestor headers", block_number - oldest_ancestor);
for height in (oldest_ancestor..=(block_number - 1)).rev() {
let block = self.provider.get_block_by_number(height.into(), false).await?.unwrap();
ancestor_headers.push(block.inner.header.try_into()?);
}

// Create the client input.
let client_input = ClientExecutorInput {
current_block: V::pre_process_block(&current_block),
previous_block: previous_block.header,
ancestor_headers,
parent_state: state,
state_requests,
bytecodes: rpc_db.get_bytecodes(),
block_hashes: rpc_db.block_hashes.borrow().clone(),
};
tracing::info!("successfully generated client input");

Ok(client_input)
}
}
1 change: 0 additions & 1 deletion crates/storage/rpc-db/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ tracing.workspace = true
rayon.workspace = true

# workspace
rsp-witness-db.workspace = true
rsp-primitives.workspace = true

# reth
Expand Down
28 changes: 8 additions & 20 deletions crates/storage/rpc-db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use reth_primitives::{
use reth_revm::DatabaseRef;
use reth_storage_errors::{db::DatabaseError, provider::ProviderError};
use revm_primitives::HashMap;
use rsp_witness_db::WitnessDb;

/// A database that fetches data from a [Provider] over a [Transport].
#[derive(Debug, Clone)]
Expand All @@ -23,14 +22,12 @@ pub struct RpcDb<T, P> {
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<HashMap<Address, AccountInfo>>,
/// The cached storage values.
pub storage: RefCell<HashMap<Address, HashMap<U256, U256>>>,
/// The cached block hashes.
pub block_hashes: RefCell<HashMap<u64, B256>>,
/// The oldest block whose header/hash has been requested.
pub oldest_ancestor: RefCell<u64>,
/// A phantom type to make the struct generic over the transport.
pub _phantom: PhantomData<T>,
}
Expand All @@ -48,14 +45,13 @@ pub enum RpcDbError {

impl<T: Transport + Clone, P: Provider<T, AnyNetwork> + Clone> RpcDb<T, P> {
/// 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,
}
}
Expand Down Expand Up @@ -133,7 +129,9 @@ impl<T: Transport + Clone, P: Provider<T, AnyNetwork> + Clone> RpcDb<T, P> {
// 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)
}
Expand Down Expand Up @@ -210,13 +208,3 @@ impl<T: Transport + Clone, P: Provider<T, AnyNetwork> + Clone> DatabaseRef for R
Ok(value)
}
}

impl<T: Transport + Clone, P: Provider<T, AnyNetwork>> From<RpcDb<T, P>> for WitnessDb {
fn from(value: RpcDb<T, P>) -> Self {
Self {
accounts: value.accounts.borrow().clone(),
storage: value.storage.borrow().clone(),
block_hashes: value.block_hashes.borrow().clone(),
}
}
}

0 comments on commit 2850e25

Please sign in to comment.