fix(world-state): make block 0 a first-class historical block#22711
Merged
spalladino merged 2 commits intomerge-train/spartanfrom Apr 22, 2026
Merged
fix(world-state): make block 0 a first-class historical block#22711spalladino merged 2 commits intomerge-train/spartanfrom
spalladino merged 2 commits intomerge-train/spartanfrom
Conversation
Persist a BlockPayload for block 0 at tree genesis so historical queries pinned to the initial state resolve against the block-0 snapshot instead of throwing or silently returning the latest committed tip. - cached_content_addressed_tree_store.hpp: write a block-0 payload (root=initialRoot, size=initialSize) in commit_genesis_state so get_block_data(0) succeeds. - append_only_tree / indexed_tree: drop the blockNumber==0 throw guards on read paths (get_sibling_path, get_leaf, find_leaf_indices_from, find_leaf_sibling_paths, find_low_leaf) now that genesis reads have a valid payload to resolve against. - aztec-node server.getWorldState: stop short-circuiting to getCommitted when the anchor matches the genesis header. Resolve to BlockNumber.ZERO and fall through to the standard snapshot + archive-root double-check so proving against the genesis anchor returns genesis state even after the node tip has advanced. - e2e_genesis_timestamp: unskip the regression test exercising the bug (prove a genesis-anchored tx after the chain has advanced with public data tree changes). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bb08d96 to
0ecbf04
Compare
48236ce to
bb08d96
Compare
PhilWindle
reviewed
Apr 22, 2026
| @@ -0,0 +1 @@ | |||
| {"sessionId":"085655bd-c5a7-430d-b256-9581219d8ab1","pid":14351,"acquiredAt":1776775336648} No newline at end of file | |||
Collaborator
There was a problem hiding this comment.
Did you mean to push this?
alexghr
approved these changes
Apr 22, 2026
Contributor
alexghr
left a comment
There was a problem hiding this comment.
Looks good, just an extra file that needs to be gitignore
PhilWindle
reviewed
Apr 22, 2026
| BlockPayload genesisBlock{ .size = meta.initialSize, .blockNumber = 0, .root = meta.initialRoot }; | ||
| dataStore_->write_block_data(0, genesisBlock, *tx); | ||
| if (meta.initialSize > 0) { | ||
| dataStore_->write_block_index_data(0, meta.initialSize, *tx); |
Collaborator
There was a problem hiding this comment.
The world state has an API that allows querying for the earliest block number where the tree was at a certain size. This conditional means it would never return block number 0 for index 0. This may be fine, I just wanted to flag it.
PhilWindle
pushed a commit
that referenced
this pull request
Apr 22, 2026
…e size (#22724) ## Motivation Follow-up to #22711 addressing a review flag from PhilWindle: `commit_genesis_state` only wrote a block-index entry for block 0 when `initialSize > 0`. This created a minor asymmetry with the regular commit path, which writes `write_block_index_data` unconditionally for every block (including zero-size blocks). ## Approach Drop the `if (meta.initialSize > 0)` guard so genesis state is indexed the same way as any other block. The read API (`find_block_for_index`) uses `get_value_or_greater(index + 1)`, so a `sizeAtBlock=0` entry is unreachable and the extra write is harmless — it just restores the invariant that every committed block appears in the index-to-block database. ## Changes - **barretenberg (cached_content_addressed_tree_store)**: remove the `initialSize > 0` guard around the block-0 `write_block_index_data` call so the genesis commit matches the regular commit path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
PXE proves a tx anchored to the genesis block (block 0). When the node has already advanced past block 0, the node's
getWorldState(genesisHeaderHash)short-circuited togetCommitted(), which returns the latest committed tip instead of genesis state. The public-data-tree witnesses computed against the tip diverge from the genesis root the kernel circuit checks against, firingassert(is_leaf_in_tree, "Proving public value inclusion failed").The deeper cause is that block 0 was never a first-class historical block in world state: trees persisted a
BlockPayloadonly from block 1 onwards, and low-level tree ops explicitly threw onblockNumber == 0.Approach
Persist a
BlockPayloadfor block 0 at tree genesis (captured frommeta.initialRoot/meta.initialSizeincommit_genesis_state). Remove the read-pathblockNumber == 0throw guards now thatget_block_data(0)succeeds and returns the correct initial state. On the aztec-node, resolve the genesis anchor hash toBlockNumber.ZEROand fall through to the standard snapshot + archive-root double-check path instead of short-circuiting to committed.Changes
commit_genesis_state, plus a block-index entry for trees with non-empty initial state (NULLIFIER, PUBLIC_DATA, ARCHIVE).blockNumber == 0throw guards on read paths (get_sibling_path,get_leaf,find_leaf_indices_from,find_leaf_sibling_paths,find_low_leaf). Destructive guards (unwind/remove/finalize at block 0) are preserved.BlockNumber.ZEROand reuse the standard snapshot path so genesis-anchored queries see genesis state even after the tip advances.initial header hashtest now assertsgetSnapshot(BlockNumber.ZERO)is called and the archive double-check passes.