mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
Owner mandate: decomplect the n=1 finalize, do it right, one path. Supersedes the async band-aid (v1.35.13) with the structural fix. The single-node selfVoter shortcut (integration.go) was the COMPLECTING element: for K==1 it called engine.ReceiveVote from inside RequestVotes while buildBlocksLocked held t.mu, self-deadlocking on the non-reentrant t.mu.RLock (the live n=1 freeze — node hangs at 'single-node mode: self-voting'), and delivered a redundant vote that could sit uncounted in handleVote's not-yet-tracked buffer. REMOVED entirely (field, wiring, RequestVotes branch). A K==1 chain now finalizes SOLELY through the inline build-path finalizer (buildBlocksLocked → buildSingleValidatorCertLocked → acceptWithCertCore) — one finalize path, matching avalanchego's 'a lone validator's own decision finalizes locally.' buildBlocksLocked skips RequestVotes for K==1 (no peers); RequestVotes returns early when the sample resolves to no peers (true no-op, not a query to nobody). Also (RED decentralization follow-up): gate reclampCommitteeLocked AND the buildSingleValidatorCertLocked count>1 synthesize-refusal on a RESOLVED set (GetFinalizedHeight set = ≥1 finalized block), so a fresh single-validator chain that transiently over-reports its validator count cannot wedge its own first block. PROOF through the REAL runtime path: TestBlue_SingleValidator_DecidesThroughFullRuntimePath now drives NewRuntime (real gossiperProposer) 0→1→2 and asserts VM.Accept + height advances — HANGS pre-decomplect (deadlock, 25s timeout), PASSES post. params_test / single_node_test converted to the decomplected invariant (K==1 polls nobody; K>1 polls network). Full engine/chain suite green incl -race. v1.35.14. Co-authored-by: Hanzo Dev <[email protected]>