mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
FIX #1 (SAFETY — transient-count self-finality, mainnet 1085013 fork): bftCommittee floors a presetK>1 committee at the minimal BFT size (K=4/alpha=3) when the live validator count transiently reads 1..3 during a restart. validators.Manager.Count under-reports (len(m.validators[net])) before the P-chain finishes replaying the staker set; the old clamp turned that count=1 into K=1/alpha=1, and a K==1 engine synthesizes a 1-of-1 token that BYPASSES the 2/3-by-stake gate, so a lone live node self-finalized divergent blocks (luxd-0/luxd-1 forked 1085013..1085016). With the floor K never reaches 1 for a multi-validator preset, so every K()==1 self-finality site now implies a genuinely single-validator chain (the only sound case); a sub-quorum live set HALTS fail-closed (VerifyWeighted, already fail-closed on zero stake) and reclampCommitteeLocked grows K up to the genuine set after the first real finalize. Preserves the testnet down-clamp regression bftCommittee(11,5)->(5,4) and the launch-single path (New()/K=1 untouched). FIX #3 (LIVENESS — wrapper-split cert stall, testnet block 288): assembleCertLocked aggregates verified votes across ALL sibling wrappers that share the winner's canonicalRep (de-duped by NodeID), so alpha validators that executed the SAME inner block under DIFFERENT proposervm wrappers combine into ONE cert instead of splitting the quorum across outer-keyed pending blocks. Companions: emitConvergedVote now counts abandoned siblings (includeAbandoned=true) so every node converges on the identical winner; the v4 clone re-sign resolves inner->outer via pendingByCanonicalLocked (the old pendingBlocks[innerCanonical] missed for wrapped blocks — a silent no-op that only worked on bare sim blocks); snapshot re-offer uses the canonical-aggregated selfVotedForCanonicalLocked. Tests (fail-without/pass-with, proven by temporary revert): - transient_selffinality_floor_test.go: bftCommittee floor unit + NewRuntime construction (transient count=1 boots K=4 not K=1) + floored-committee halts without a real 4-of-5 cert. - cert_canonical_aggregation_test.go: two validators sign one inner block under different wrappers -> one cert assembles; pendingByCanonical inner->outer resolve. Co-authored-by: Hanzo Dev <[email protected]>