feat(state): verify VCT roots during header sync (move verification off the committer)#447
feat(state): verify VCT roots during header sync (move verification off the committer)#447p0mvn wants to merge 8 commits into
Conversation
And two more auto-invalidated findings. Analyzed 12 files, diff |
0a15345 to
edbba53
Compare
0d7efd3 to
9080aa8
Compare
9080aa8 to
4ca5e09
Compare
…isting Peer-supplied commitment (tree-aux) roots delivered during Zakura header sync are now verified against block header commitments before they are written to state. A block's commitment binds the history tree as of its parent, so a root at height H is confirmed when H+1's header is checked; each committed range persists only its header-authenticated confirmed prefix, and forward ranges overlap by one block so the next range confirms the previous tip. On startup the ZIP-221 history tree is reconstructed from the durable confirmed roots up to the highest contiguous frontier, and header sync resumes from that frontier with an overlapping range that re-validates the next root. Unauthenticated peer data is therefore never folded into the header-sync history tree, and a restart never trusts an unconfirmed tip root.
…struction Add §6.4 covering the header-sync-layer verify-before-persist gate, the confirmed-prefix persistence (the range tip's root is confirmed by the next overlapping range), and the startup reconstruction that resumes from the highest contiguous confirmed frontier. Update §5.4, §11, and the §12 roadmap (increment 6d) to match.
…rg, reanchor) Add integration tests derisking the verified note-commitment-tree-root header-sync flows that previously had no automated cross-component coverage: - restart-resume: driver startup reconstructs the history tree at the confirmed frontier and resumes header sync at frontier+1 (zebrad) - block reorg below the header frontier drops conflicting provisional headers/roots so reconstruction folds nothing stale (zebra-state) - reanchor dispatches the frontier-tree rebuild and forward sync resumes after the reload (zebra-network) - append_confirmed_roots resumes from a non-empty post-Heartwood base tree (zebra-chain) - CommitHeaderRange rejects a full-length roots vector through the real request path, enforcing the confirmed-prefix boundary (zebra-state) Also import NetworkKind in the Zakura handler to fix an unrelated build break.
…lidation Header-sync commit routed every range through the forward-only header-auxiliary commitment validation. Backward checkpoint-backfill ranges fold onto the previous checkpoint's tree, not the forward frontier the reactor caches, so they returned MissingHeaderHistoryTree and retried forever, never committing their headers. Forward ranges (finalized or not) are still ZIP-221 aux-validated. Backward ranges are authenticated by the checkpoint hash and now commit with no provisional roots: CommitHeaderRange.verified_roots is Option, None for the checkpoint-authenticated path, and prepare_header_range_batch_with_roots accepts an empty roots vector while still rejecting a full-length (tip-included) vector, so the trust boundary is unchanged. Fixes the two regressed backfill-commit tests; fixes a stale forward-genesis fixture that supplied empty roots for a Sapling-active range.
…lls behind (#459) When this node is ahead of its Zakura header-sync overlay peers (they are behind) and legacy body sync keeps committing, the verified body tip can outrun the header-sync frontier, leaving header sync stuck below the local verified state with no peer to fetch from. Those blocks are locally verified, so once the verified tip leads the frontier by more than HEADER_SYNC_FOLLOW_VERIFIED_TIP_GAP, header sync now re-anchors up to the verified tip (reusing the existing re-anchor path, which rebuilds the frontier history tree there). The gap gate keeps the re-anchor's status broadcast well under the inbound status-spam interval even while the verified tip climbs fast.
4ca5e09 to
473eaac
Compare
473eaac to
04554bd
Compare
5f44263 to
46f41c6
Compare
Reapplication of #366 onto
ironwood-main(rebased over the #444 squash-merge ofroman/vct-config). The original PR was written against the oldfeat/pre-release-mainfeature branch; this ports the same change to the current branch structure.Goal
Make header-sync commit the single authoritative verify+fold for peer-supplied commitment roots, so a lying peer is caught early and attributably at the network boundary (scored + refetched), and the finalized committer no longer needs the successor at all (no successor look-ahead, no await-successor park, no deadlock).
What changed vs the original #366
The base branch evolved, so the port adapts rather than replays:
HistoryTreeBlockParts). The verifier folds viapush_from_parts(network, HistoryTreeBlockParts { .. })instead of the original's many-argument signature.commitment_roots_by_heightcolumn family on this branch (with per-height tx counts andauth_data_rootalready carried), so the original's separatezakura_header_commitment_roots_by_heightCF does not exist here. Only the newzakura_header_frontier_treeCF is added.Nu6_3activation (verify_supplied_ironwood_root_below_nu6_3/InvalidPreNu6_3IronwoodRoot), not the original'sNu7variant.VctWriteManagerlook-ahead and a prevalidation/dedup cache; both are removed along withNextVctBlockand thecommit_finalized(_direct)successor threading.Commits
header_commitment_is_valid_for_chain_historycore; addverify_supplied_roots_from_parts+ the sub-Heartwood/sub-NU5/sub-Nu6_3 direct checks; persist a running header-frontier ZIP-221 MMR (newzakura_header_frontier_treeCF) that each committed range's roots are folded into and verified against the header commitments; reject a bad range withCommitHeaderRangeError::InvalidCommitmentRoots.InvalidCommitmentRoots→InvalidRangepeer scoring in the header-sync driver; drop the committer's successor look-ahead and the write-worker await-successor park; remove the successor threading fromcommit_finalized/commit_finalized_direct. The committer re-checks its own header commitment and folds the (already header-sync-verified) roots.VctState/VctCommitState/VctWriteManager; regenerate CF snapshots.Test evidence
cargo check --workspace --all-targetscleancargo fmt --all -- --checkcleancargo clippy -p zebra-state -p zebrad --all-targetscleancargo test -p zebra-state: 289 passed, 0 failed, 2 ignoredAI disclosure
Reapplied and adapted with Claude Code (Fable); the contributor is the responsible author.