mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 03:39:39 +00:00
Publishes the consensus quorum-finality cascade into node: - consensus v1.25.19 -> v1.25.20 (chain quorum-cert finality, LocalBFTParams) - crypto v1.19.21 -> v1.19.22 (BLS deserialization/aggregation hardening) - chains v1.3.17 -> v1.3.18, runtime v1.1.1 -> v1.1.3 (value-path tags) selectConsensusParams now routes explicitly-local dev networks (devnet 3 / localnet 1337, EXACT IDs not a range) to consensus.LocalBFTParams (K=4/α=3, f=1) — the minimal real-BFT committee. Default K=20 is unsatisfiable on a few localhost validators (α=14 unreachable -> P-Chain freezes at height 0). K=4 makes quorum reachable while still clearing ValidateForValueNetwork and the CRITICAL-2 multi-node-is-BFT regression. A custom value L1 (high networkID) keeps the large Default set. precompile resolves to v0.5.56 (MVS-correct: chains v1.3.18's own requirement; node has no evm/dex dep). go mod verify: all modules verified; chains quorum params tests green.
133 lines
5.8 KiB
Go
133 lines
5.8 KiB
Go
// Copyright (C) 2019-2026, Lux Industries, Inc. All rights reserved.
|
||
// See the file LICENSE for licensing terms.
|
||
|
||
// quorum_params_test.go — CRITICAL-2 node-layer regression: the node must NEVER
|
||
// wire a non-BFT consensus param set for a multi-validator (sybil-protected)
|
||
// chain. The round-1 hole was manager.go selecting LocalParams() (K=3/α=2 → f=0,
|
||
// CFT) for ALL multi-node nets — a single Byzantine validator forks K=3/α=2.
|
||
// These tests pin selectConsensusParams to a BFT-safe set for every multi-node
|
||
// network and prove the value-network backstop (ValidateForValueNetwork) accepts
|
||
// the selected params.
|
||
package chains
|
||
|
||
import (
|
||
"testing"
|
||
|
||
consensusconfig "github.com/luxfi/consensus/config"
|
||
"github.com/luxfi/constants"
|
||
)
|
||
|
||
// TestSelectConsensusParams_MultiNodeIsBFT proves that for EVERY multi-validator
|
||
// (sybilProtection==true) network the node selects a Byzantine-fault-tolerant
|
||
// param set (f≥1, i.e. K≥4) that also clears the value-network validator — and
|
||
// that it is NEVER LocalParams (K=3) or any K<4 set. This is the node half of
|
||
// CRITICAL-2 (the consensus half is config.ValidateForValueNetwork, tested in
|
||
// the consensus module).
|
||
func TestSelectConsensusParams_MultiNodeIsBFT(t *testing.T) {
|
||
local := consensusconfig.LocalParams()
|
||
|
||
cases := []struct {
|
||
name string
|
||
networkID uint32
|
||
}{
|
||
{"mainnet", constants.MainnetID},
|
||
{"testnet", constants.TestnetID},
|
||
{"localnet-multinode", constants.LocalID},
|
||
{"unittest-multinode", constants.UnitTestID},
|
||
{"arbitrary-multinode", 424242},
|
||
}
|
||
|
||
for _, tc := range cases {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
p := selectConsensusParams(true /* sybilProtection */, tc.networkID)
|
||
|
||
// MUST be Byzantine-fault-tolerant: f≥1 ⟹ K≥4.
|
||
if p.ByzantineFaultTolerance() < 1 {
|
||
t.Fatalf("multi-node net %q got non-BFT params K=%d f=%d (CRITICAL-2: a single faulty validator forks)",
|
||
tc.name, p.K, p.ByzantineFaultTolerance())
|
||
}
|
||
// MUST NOT be the CFT LocalParams (K=3/α=2) that was the round-1 hole.
|
||
if p.K == local.K && p.AlphaPreference == local.AlphaPreference && p.K == 3 {
|
||
t.Fatalf("multi-node net %q selected LocalParams (K=3/α=2) — the CRITICAL-2 fork config", tc.name)
|
||
}
|
||
// The selected params MUST themselves pass Valid() (the BFT α-floor:
|
||
// 2·AlphaPreference − K ≥ f+1).
|
||
if err := p.Valid(); err != nil {
|
||
t.Fatalf("multi-node net %q selected params fail Valid(): %v (K=%d α=%d)",
|
||
tc.name, err, p.K, p.AlphaPreference)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestSelectConsensusParams_LocalDevIsSatisfiableBFT proves that an explicitly-
|
||
// local dev network (devnet 3 / localnet 1337) selects the MINIMAL real-BFT set
|
||
// (LocalBFTParams: K=4/α=3, f=1) — satisfiable by a handful of localhost
|
||
// validators — and NOT the large Default (K=20/α=14), whose α=14 quorum is
|
||
// unreachable with 3-4 validators (the freeze-at-height-0 bug). It must still be
|
||
// genuinely BFT (f≥1) and pass the value backstop (K=4 ≥ 4), so production
|
||
// safety / CRITICAL-2 is untouched: a custom VALUE L1 (high networkID) still gets
|
||
// the large Default set, never the local one.
|
||
func TestSelectConsensusParams_LocalDevIsSatisfiableBFT(t *testing.T) {
|
||
for _, networkID := range []uint32{constants.DevnetID, constants.LocalID} {
|
||
p := selectConsensusParams(true /* sybilProtection */, networkID)
|
||
|
||
// Satisfiable on a small committee: α must be small (K=4 → α=3), NOT 14.
|
||
if p.K != 4 || p.AlphaPreference != 3 {
|
||
t.Fatalf("local dev net %d: want minimal-BFT K=4/α=3, got K=%d/α=%d (Default K=20 is unsatisfiable on localhost)",
|
||
networkID, p.K, p.AlphaPreference)
|
||
}
|
||
// Still genuinely BFT (f≥1) — does NOT regress CRITICAL-2.
|
||
if p.ByzantineFaultTolerance() < 1 {
|
||
t.Fatalf("local dev net %d: K=%d is not BFT (f=%d)", networkID, p.K, p.ByzantineFaultTolerance())
|
||
}
|
||
// Must clear the value backstop the manager asserts before engine start.
|
||
if err := p.ValidateForValueNetwork(networkID); err != nil {
|
||
t.Fatalf("local dev net %d: minimal-BFT params must pass the value backstop, got %v", networkID, err)
|
||
}
|
||
}
|
||
|
||
// A custom value L1 with a high networkID is NOT a local dev network: it must
|
||
// keep the large Default set (the isLocalDevNetwork predicate is EXACT, not a
|
||
// >=1337 range that would wrongly catch value L1s).
|
||
const customValueL1 = uint32(909090)
|
||
if p := selectConsensusParams(true, customValueL1); p.K != consensusconfig.DefaultParams().K {
|
||
t.Fatalf("custom value L1 %d must get Default K=%d, got K=%d (must NOT match the local-dev path)",
|
||
customValueL1, consensusconfig.DefaultParams().K, p.K)
|
||
}
|
||
}
|
||
|
||
// TestSelectConsensusParams_SingleNodeIsK1 proves --dev / sybil-disabled selects
|
||
// the K=1 single-validator regime (the sole validator's accept is the 1-of-1
|
||
// quorum) — and NOT a multi-node BFT set.
|
||
func TestSelectConsensusParams_SingleNodeIsK1(t *testing.T) {
|
||
p := selectConsensusParams(false /* sybilProtection */, constants.LocalID)
|
||
if p.K != 1 {
|
||
t.Fatalf("sybil-disabled (single-node) must select K=1, got K=%d", p.K)
|
||
}
|
||
}
|
||
|
||
// TestSelectConsensusParams_ValueBackstop proves the params selected for a
|
||
// multi-node net pass the STRICTER value-network validator for that net — the
|
||
// fail-closed backstop asserted at the manager call site before starting the
|
||
// engine. (Mainnet enforces K≥11, so MainnetParams K=21 passes; Default K=20
|
||
// passes for an arbitrary value net.)
|
||
func TestSelectConsensusParams_ValueBackstop(t *testing.T) {
|
||
cases := []struct {
|
||
name string
|
||
networkID uint32
|
||
}{
|
||
{"mainnet", constants.MainnetID},
|
||
{"arbitrary-value-net", 909090},
|
||
}
|
||
for _, tc := range cases {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
p := selectConsensusParams(true, tc.networkID)
|
||
if err := p.ValidateForValueNetwork(tc.networkID); err != nil {
|
||
t.Fatalf("selected params for value net %q must pass the value backstop, got %v (K=%d)",
|
||
tc.name, err, p.K)
|
||
}
|
||
})
|
||
}
|
||
}
|