mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
THE BUG (fresh-net deadlock). The engine forked avalanchego's Snowman model by adding a hard per-height finality gate (markFinalizedLocked invariant (c): a finalized block's parent MUST equal the current finalized tip) AND never reorged. When a cert finalized one child of a parent while production/preference had explored a SIBLING and built on it, the winner finalized but the losing sibling subtree was NEVER pruned and preference never reorged — so production ran away on the losing branch and every cert for it hit "parent != finalized tip" forever (devnet: finalized stuck at height 1, blocks past 10, every cert REFUSED). A refusal cannot reorg. THE FIX (avalanchego-shaped). Siblings are TRACKED (admission already tolerated them); when a cert SELECTS one, FinalizeBranch walks the certified branch from the finalized tip, finalizes the whole contiguous path, and PRUNES the losing-sibling subtrees — exactly topological.go's acceptPreferredChild (accept the path) + rejectTransitively (prune the losers). The single-non-branching-chain property is now the OUTPUT of the reorg, not a precondition refused at admission. The reorg IS the recovery. - consensus.go: markFinalizedLocked → FinalizeBranch (branch walk + reorg/prune plan). KEPT as α-of-K-cert safety: one-finalized-block-per-height (ErrHeightAlreadyFinalized, equivocation evidence) and the conflicting/losing-branch refusal (renamed ErrParentNotFinalizedTip → ErrConflictsWithFinalizedBranch; now a branch check, not a blanket non-tip-parent refusal). Added ErrAncestorNotTracked (behind-node DEFER). - Decomplected the count path: ProcessVote/Poll set block.accepted as a pure LIVENESS trigger; the per-height ledger is advanced ONLY by FinalizeBranch (the cert path). - engine.go: acceptWithCertCore is the SOLE finalizer — calls FinalizeBranch and applies the plan (VM.Accept the path ascending, VM.Reject the pruned losers, SetPreference to the certified tip). New applyBranchFinalization helper. - Removed dead one-off entrypoints AcceptViaCert / ForceAccept / consensus.AcceptBootstrapBlock; HandleIncomingCert + the K==1 path + Runtime.AcceptBootstrapBlock all route through the one finalize. Simplified ForcePreference (the gate-(c) desync defense is now moot). Regression (RED→GREEN): TestSiblingReorg_CertPrunesLoserBranchAndContinues — two children at one height from a finalized parent, cert one, winner descendants accepted, loser subtree rejected, no permanent refusal, chain continues. Plus FinalizeBranch unit tests (multi-step finalize, conflicting-branch refusal, missing-ancestor defer) and the decomplected count path. Full engine/chain suite green incl -race; CGO_ENABLED=0.