Skip to content

Conversation

@JereSalo
Copy link
Contributor

@JereSalo JereSalo commented Nov 6, 2025

Motivation

  • Sometimes when we want a witness ethrex returns an error, this aims to fix it.

Description

  1. There's a bug that assumes that we have some data in a map that we don't necessarily have. This is usually triggered when there's a call to the BLOCKHASH opcode during a block. The idea is to requests all the headers of intermediate blocks so that we can generate the witness correctly.
  2. Now the Guest Program accepts blocks with hash already computed, it just recomputes it and compares the hash with the already set one (if any), returning an error in case they differ. This was suggested in ethrex: remove lost keccak calculation in guest program eth-act/zkevm-benchmark-workload#177 and we liked the idea.

Both changes are in the same PR because the goal was just to solve 1 but as I was hashing the blocks when generating the witness I decided to fix 2 elegantly so we can pass the Blockchain tests.

Closes #5250

@github-actions github-actions bot added L1 Ethereum client L2 Rollup client labels Nov 6, 2025
@github-actions
Copy link

github-actions bot commented Nov 6, 2025

Lines of code report

Total lines added: 6
Total lines removed: 16
Total lines changed: 22

Detailed view
+-------------------------------------------------------+-------+------+
| File                                                  | Lines | Diff |
+-------------------------------------------------------+-------+------+
| ethrex/crates/blockchain/blockchain.rs                | 1298  | -16  |
+-------------------------------------------------------+-------+------+
| ethrex/crates/common/types/block_execution_witness.rs | 428   | +6   |
+-------------------------------------------------------+-------+------+

@JereSalo JereSalo marked this pull request as ready for review November 10, 2025 19:32
@JereSalo JereSalo requested a review from a team as a code owner November 10, 2025 19:32
Copilot AI review requested due to automatic review settings November 10, 2025 19:32
@ethrex-project-sync ethrex-project-sync bot moved this to In Review in ethrex_l1 Nov 10, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes issues with the debug_executionWitness RPC method by addressing two main problems:

  1. BLOCKHASH opcode handling: Previously, the code assumed block hash data was always available in a map, but this failed when blocks referenced via the BLOCKHASH opcode weren't included. The fix properly fetches all intermediate block headers needed for witness generation.

  2. Pre-computed block hashes: The guest program now accepts blocks with hashes already computed, validating them against recomputed values instead of requiring them to be uninitialized.

Key Changes

  • Modified initialize_block_header_hashes to validate pre-set hashes instead of rejecting them
  • Replaced block_hashes_map with direct block.header.hash() calls using the OnceCell caching mechanism
  • Refactored header fetching logic to walk backwards from the last block to the first needed block, fetching headers by hash rather than by number

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
crates/common/types/block_execution_witness.rs Updated hash initialization logic to validate pre-set hashes and avoid double-hashing headers that appear in both the witness and blocks array
crates/common/types/block.rs Changed compute_block_hash visibility from private to public to support external hash computation
crates/blockchain/blockchain.rs Removed block_hashes_map construction and refactored header fetching to properly handle BLOCKHASH opcode references by walking backwards through parent hashes

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 493 to 507
let mut block_numbers_in_common = vec![];
for block in blocks {
// Verify each block's header hash is uninitialized
if block.header.hash.get().is_some() {
return Err(GuestProgramStateError::Custom(format!(
"Block header hash is already set for {}",
block.header.number
)));
let hash = block.header.compute_block_hash();
set_hash_or_validate(&block.header, hash)?;

let number = block.header.number;
if let Some(header) = self.block_headers.get(&number) {
block_numbers_in_common.push(number);
set_hash_or_validate(header, hash)?;
}
let header = self
.block_headers
.get(&block.header.number)
.unwrap_or(&block.header);

let hash = header.hash();
// this returns err if it's already set, so we drop the Result as we don't
// care if it was already initialized.
let _ = block.header.hash.set(hash);
}

for header in self.block_headers.values() {
if block_numbers_in_common.contains(&header.number) {
// We have already set this hash in the previous step
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Using Vec::contains in the loop makes this O(n*m) where n is the number of headers and m is the number of blocks in common. Consider using a HashSet<u64> instead of Vec<u64> for block_numbers_in_common to make the lookup O(1), improving overall performance to O(n+m).

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I will use a BTreeSet because HashSet is not zkvm friendly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JereSalo JereSalo enabled auto-merge November 10, 2025 22:21
@JereSalo JereSalo added this pull request to the merge queue Nov 10, 2025
Merged via the queue into main with commit 80be189 Nov 10, 2025
40 checks passed
@JereSalo JereSalo deleted the fix_rpc_witness_error branch November 10, 2025 22:35
@github-project-automation github-project-automation bot moved this from In Review to Done in ethrex_l1 Nov 10, 2025
xqft pushed a commit that referenced this pull request Nov 11, 2025
**Motivation**

<!-- Why does this pull request exist? What are its goals? -->
- Sometimes when we want a witness ethrex returns an error, this aims to
fix it.

**Description**

<!-- A clear and concise general description of the changes this PR
introduces -->
1. There's a bug that assumes that we have some data in a map that we
don't necessarily have. This is usually triggered when there's a call to
the BLOCKHASH opcode during a block. The idea is to requests all the
headers of intermediate blocks so that we can generate the witness
correctly.
2. Now the Guest Program accepts blocks with hash already computed, it
just recomputes it and compares the hash with the already set one (if
any), returning an error in case they differ. This was suggested in
eth-act/zkevm-benchmark-workload#177 and we
liked the idea.

Both changes are in the same PR because the goal was just to solve `1`
but as I was hashing the blocks when generating the witness I decided to
fix `2` elegantly so we can pass the Blockchain tests.

Closes #5250

---------

Co-authored-by: Ivan Litteri <67517699+ilitteri@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

L1 Ethereum client L2 Rollup client

Projects

Status: Done
Status: Done

Development

Successfully merging this pull request may close these issues.

Accept blocks with computed hash in guest program but recompute it anyway

5 participants