test(integration): state-sync fixture — conventional 4-validator topology, witness-freshness gate#488
Conversation
…logy, witness-freshness gate The fixture was the harness's only single-validator chain. A solo validator is gossip-free (it self-votes to quorum), so it produces blocks at the unthrottled ~15/s execution rate, while a follower replays consensus at the gossip-paced ~2-3/s ceiling (100ms block-part pacing + 100ms vote pacing + 50ms commit timeout + per-height WAL fsync). The witness follower therefore diverged from head immediately after its caught-up gate, and the state-sync bootstrap's light client failed its cross-check at the snapshot height with "witnesses don't have the blocks". Nothing switches a diverged follower back to blocksync in this fork (SwitchToBlockSync is statesync-only; self-remediation defaults off and is outside the sei-config surface), and catching_up is a one-way latch, so the divergence is silent and permanent. Four validators, the harness convention (release/chaos/bench run the same), dissolves the class: consensus among four paces production to the same gossip cadence followers replay at, so the witness tracks head. The follower count returns to one (the validator set clears the blocksync peer floor), the follower still produces no snapshots (witness role only), and a new witness-freshness gate asserts the follower sits within one snapshot interval of the validator head before the bootstrap starts, since catching_up cannot certify at-head and a future pacing change would otherwise regress silently. Root-caused live on harbor with source-cited mechanism (consensus/reactor gossip pacing, state.go commit path, pool.IsCaughtUp, WaitSync latch). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PR SummaryLow Risk Overview Before bootstrapping the state-sync follower, Comments in Reviewed by Cursor Bugbot for commit 1ef5181. Bugbot is set up for automated code reviews on this repo. Configure here. |
Summary
The state-sync fixture was the harness's only single-validator chain, and that topology is the root of the divergence found during the live validation run: a solo validator is gossip-free (it self-votes to quorum), producing at the unthrottled ~15 blocks/s execution rate, while a follower replays consensus at the gossip-paced ~2-3 blocks/s ceiling (100ms block-part pacing + 100ms vote pacing + 50ms commit timeout + per-height WAL fsync). The witness follower diverged from head immediately after its caught-up gate — silently, since
catching_upis a one-way latch and this fork has no consensus→blocksync switchback — and the state-sync bootstrap's light client failed its snapshot-height cross-check with "witnesses don't have the blocks."The fix is the conventional topology, not a custom pacing knob:
SEI_VALIDATORSdefaults to 4 in both suites — the harness convention (release/chaos/bench hardcode 4). Consensus among four paces production to the same gossip cadence followers replay at, so the witness tracks head structurally.rpcNodesreturns to 1 — the validator set clears blocksync's >1-peer caught-up floor natively; the interim sibling follower is no longer needed.catching_upcannot certify at-head, so a future change that makes production outpace replay fails here by name instead of wedging silently.snapshot_interval=0(witness role only; chunks come from the validators over p2p).A closing detail from review: the old comment's "2-validator genesis blocksync-bootstrap deadlock" — the reason the fixture went single-validator — was the same
pool.IsCaughtUppeer floor all along (two validators = one peer each). Four validators clears it, as the sibling suites prove nightly.Review
xreview ledger:
bdchatham-designs/designs/seinode-task/xreview/workflow-complete-at-release.mdRounds 12-13 — blinded sei-network-specialist (assigned dissenter) + idiomatic-reviewer, both RATIFY. The dissenter's outpacing hypothesis (parallel validator gossip vs serial follower pull) was refuted from source: a consensus-mode follower receives parts and votes from all four validators in parallel, one gossip hop behind the quorum. Non-blocking notes on record: the freshness gate is a single-shot read (consistent with the file's point-read idiom), and its 50-block bound is a regime separator relying on co-pacing persistence.Mechanism citations:
sei-tendermint/internal/consensus/reactor.go(gossip pacing, WaitSync latch),internal/blocksync/pool.go:211-214(peer floor),config/config.go:1269(100ms gossip sleep),internal/consensus/state.go(commit timeout, WAL fsync).A gated validation run on harbor from the rebuilt harness image follows the merge.
🤖 Generated with Claude Code