Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/optimism/payload/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ impl<Txs> OpBuilder<'_, Txs> {
ctx.execute_sequencer_transactions(&mut builder)?;
builder.into_executor().apply_post_execution_changes()?;

let ExecutionWitnessRecord { hashed_state, codes, keys } =
let ExecutionWitnessRecord { hashed_state, codes, keys, block_ids_for_blockhash_opcode: _ } =
ExecutionWitnessRecord::from_executed_state(&db);
let state = state_provider.witness(Default::default(), hashed_state)?;
Ok(ExecutionWitness { state: state.into_iter().collect(), codes, keys })
Expand Down
6 changes: 6 additions & 0 deletions crates/revm/src/witness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ pub struct ExecutionWitnessRecord {
///
/// `keccak(address|slot) => address|slot`
pub keys: Vec<Bytes>,
/// List of block ids for blocks whose hashes were requested during execution
/// by the BLOCKHASH opcode.
pub block_ids_for_blockhash_opcode: Vec<u64>,
}

impl ExecutionWitnessRecord {
Expand Down Expand Up @@ -62,6 +65,9 @@ impl ExecutionWitnessRecord {
}
}
}
// Save only the block numbers, since the block hashes are redundant.
// The block numbers moreover, give us the ordering of the blocks.
self.block_ids_for_blockhash_opcode = statedb.block_hashes.keys().copied().collect();
}

/// Creates the record from the state after execution.
Expand Down
55 changes: 45 additions & 10 deletions crates/rpc/rpc/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ where
pub async fn debug_execution_witness_by_block_hash(
&self,
hash: B256,
) -> Result<ExecutionWitness, Eth::Error> {
) -> Result<(ExecutionWitness, Vec<Bytes>), Eth::Error> {
let this = self.clone();
let block = this
.eth_api()
Expand All @@ -614,7 +614,7 @@ where
pub async fn debug_execution_witness(
&self,
block_id: BlockNumberOrTag,
) -> Result<ExecutionWitness, Eth::Error> {
) -> Result<(ExecutionWitness, Vec<Bytes>), Eth::Error> {
let this = self.clone();
let block = this
.eth_api()
Expand All @@ -629,9 +629,12 @@ where
pub async fn debug_execution_witness_for_block(
&self,
block: Arc<RecoveredBlock<ProviderBlock<Eth::Provider>>>,
) -> Result<ExecutionWitness, Eth::Error> {
) -> Result<(ExecutionWitness, Vec<Bytes>), Eth::Error> {
let this = self.clone();
self.eth_api()
let block_number = block.header().number();

let (exec_witness, blocks_ids_for_blockhash_opcode) = self
.eth_api()
.spawn_with_state_at_block(block.parent_hash().into(), move |state_provider| {
let db = StateProviderDatabase::new(&state_provider);
let block_executor = this.inner.block_executor.executor(db);
Expand All @@ -644,14 +647,43 @@ where
})
.map_err(|err| EthApiError::Internal(err.into()))?;

let ExecutionWitnessRecord { hashed_state, codes, keys } = witness_record;
let ExecutionWitnessRecord {
hashed_state,
codes,
keys,
block_ids_for_blockhash_opcode,
} = witness_record;

let state = state_provider
.witness(Default::default(), hashed_state)
.map_err(EthApiError::from)?;
Ok(ExecutionWitness { state, codes, keys })
Ok((ExecutionWitness { state, codes, keys }, block_ids_for_blockhash_opcode))
})
.await
.await?;

// For zkVMs, they need a contiguous set of block headers in order to prove that the block
// hashes are correct.
//
// Fetch the smallest block and return an empty vector if there were no block_ids
let Some(smallest) = blocks_ids_for_blockhash_opcode.iter().min().copied() else {
return Ok((exec_witness, Vec::new()));
};

let range = smallest..block_number;
// TODO: Check if headers_range errors when one of the headers in the range is missing
let headers: Vec<Bytes> = self
.provider()
.headers_range(range)
.map_err(EthApiError::from)?
.into_iter()
.map(|header| {
let mut serialized_header = Vec::new();
header.encode(&mut serialized_header);
serialized_header.into()
})
.collect();

Ok((exec_witness, headers))
}

/// Returns the code associated with a given hash at the specified block ID. If no code is
Expand Down Expand Up @@ -1004,9 +1036,9 @@ where
async fn debug_execution_witness(
&self,
block: BlockNumberOrTag,
) -> RpcResult<ExecutionWitness> {
) -> RpcResult<(ExecutionWitness)> {
let _permit = self.acquire_trace_permit().await;
Self::debug_execution_witness(self, block).await.map_err(Into::into)
Self::debug_execution_witness(self, block).await.map_err(Into::into).map(|p| p.0)
}

/// Handler for `debug_executionWitnessByBlockHash`
Expand All @@ -1015,7 +1047,10 @@ where
hash: B256,
) -> RpcResult<ExecutionWitness> {
let _permit = self.acquire_trace_permit().await;
Self::debug_execution_witness_by_block_hash(self, hash).await.map_err(Into::into)
Self::debug_execution_witness_by_block_hash(self, hash)
.await
.map_err(Into::into)
.map(|p| p.0)
}

async fn debug_backtrace_at(&self, _location: &str) -> RpcResult<()> {
Expand Down
Loading