Skip to content

fix(network): follow the verified body tip when Zakura header sync falls behind#459

Merged
p0mvn merged 1 commit into
roman/zakura-header-anchor-root-validationfrom
roman/hs-follow-verified-tip
Jul 4, 2026
Merged

fix(network): follow the verified body tip when Zakura header sync falls behind#459
p0mvn merged 1 commit into
roman/zakura-header-anchor-root-validationfrom
roman/hs-follow-verified-tip

Conversation

@p0mvn

@p0mvn p0mvn commented Jul 4, 2026

Copy link
Copy Markdown

Motivation

Stacked on top of #458 (Zakura header-sync anchor-root validation).

After a restart, Zakura header sync resumes at the confirmed roots frontier. If 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 (refresh_forward_range schedules nothing when best_peer_tip <= best_header_tip). Those blocks are already locally verified: their headers and roots are the trusted committed rows, so header sync has no reason to sit behind them.

Found while validating #458 on a live mainnet archive node: the node was healthy and at the tip via legacy sync, but Zakura header sync stayed ~300 blocks behind the verified tip because all its overlay peers advertised a lower height.

Solution

handle_state_frontiers_changed now advances the header frontier up to the verified body tip once the verified tip leads it by more than HEADER_SYNC_FOLLOW_VERIFIED_TIP_GAP (256), reusing the existing reanchor_to_verified_block_tip path (repositions the tip and reloads the frontier history tree at the verified tip).

The gap gate keeps the re-anchor's status broadcast well under the 5s inbound status-spam interval (DEFAULT_HS_INBOUND_STATUS_MIN_INTERVAL) even during a full-speed legacy catch-up: a 256-block gap spaces re-anchors seconds apart in the worst case. On a node that is keeping up (header sync at/ahead of the verified tip) the branch never fires.

Tests

  • New unit test verified_tip_beyond_gap_reanchors_header_sync_to_follow_it covers both directions: a verified lead at the gap boundary leaves the frontier put (strictly-greater gate), and a lead one block beyond the gap re-anchors up to the verified tip and dispatches the frontier-tree rebuild there.
  • All 100 zakura::header_sync unit tests pass; cargo fmt --all -- --check and cargo clippy -p zebra-network --all-targets are clean.
  • Validated on a live mainnet archive node: the fix is dormant while the node is at the tip (0 re-anchors), and the node stays healthy.

AI Disclosure

  • AI tools were used: Claude (Claude Code) — diagnosed the header-sync-behind-verified-tip behavior on a live node, implemented the fix and its unit test.

…lls behind

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.
@v12-auditor

v12-auditor Bot commented Jul 4, 2026

Copy link
Copy Markdown

Note

Complete: Audit complete. V12 found two issues worth reviewing.

Open the full results here.

FindingSeverityDetails
F-95792 🟠 High
Stale commits drop frontier tree

The new verified-tip follow path can race with a forward header commit that was already dispatched before the reanchor. A peer can return a valid ahead-of-tip Headers response, causing the reactor to validate auxiliary roots, store a PendingHeaderCommit, and emit CommitHeaderRange; if body sync then advances the verified tip by more than HEADER_SYNC_FOLLOW_VERIFIED_TIP_GAP, reanchor_to_verified_block_tip() removes that pending commit metadata. The already-dispatched driver action is not cancelled and still sends HeaderRangeCommitted on state success. When that stale completion arrives, handle_header_range_committed() can publish the stale range tip without installing the matching history tree because the pending commit was removed. Subsequent forward validation requires a cached history tree at the overlapped tip parent, but the only automatic reload path is gated to the case where the missing height equals the current best header tip and the verified tip equals the best header tip, so the range is retried without repair.

F-95793 🟠 High
Same-height resets stale anchors

The new verified-tip follow logic is height-only, so it does not realign header sync after a valid same-height body-chain reset changes the verified hash. A peer can send a consensus-valid NewBlock, the driver commits it through the block verifier, and Zebra's chain-tip mirror publishes a VerifiedReset when the best-chain switch does not increase height. The reactor consumes that reset by updating verified_block_tip and verified_block_hash, but it only calls the new reanchor when the verified height is more than 256 blocks above best_header_tip. Equal-height full-block paths also only publish a new best header tip when height > best_header_tip, leaving best_header_hash on the old fork while verified_block_hash points at the new fork. Future forward requests use the stale best_header_hash as their anchor, so honest headers on the new fork fail the first-header link check and the stale-anchor recovery path refuses to reanchor because best_header_tip <= verified_block_tip.

Analyzed three files, diff 4072cb7...7e9855a.

@p0mvn
p0mvn merged commit 2d5ca5d into roman/zakura-header-anchor-root-validation Jul 4, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant