mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
The four finality concepts as DISTINCT authorization tiers that never collapse, named in our astrophysics ontology (no generic BFT / Cosmos vocabulary): Photon in flight, being sampled authorizes nothing Wave the forming Nova preference authorizes nothing Nova beta-ignition, sampler accepts authorizes LOCAL execution (reorgable) Quasar 2/3-stake certificate authorizes bridges / DEX / cross-chain Horizon PQ-sealed, irreversible authorizes irreversible settlement finality.go: the Finality enum + AuthorizesLocalExecution/Export/IrreversibleSettlement boundary methods (the anti-collapse invariant as code: a block below Quasar can never be exported as deterministically final). quorum.go: reconciled to the Nova tier ONLY — NovaQuorum(n)=floor(n/2)+1 (majority, crash-tolerant, drives Nova ignition / local exec), CrashTolerance, NovaBeta. Deleted the node-count CertQuorum/ByzantineTolerance I had added: the Quasar tier has exactly ONE definition and it is config.TwoThirdsStakeFloor / EqualStakeSupermajorityThreshold (strict >2/3 stake, distinct signers via QuorumCert.VerifyWeighted) — never re-expressed as a count. TestNova_Below_Quasar proves the two tiers stay distinct (Nova majority < Quasar 2/3; n=5: 3 vs 4). Removed the dead config.Parameters.ViewChange field (Phase A deleted everything that read it) and its stale test assignments — no Tendermint vocabulary left in runtime config. Whole-repo build + ladder/quorum tests green. Co-authored-by: Hanzo Dev <[email protected]>
99 lines
4.1 KiB
Go
99 lines
4.1 KiB
Go
// Copyright (C) 2019-2026, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package chain
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/luxfi/consensus/config"
|
|
)
|
|
|
|
// TestNovaQuorum_Table pins the Nova-tier thresholds at n∈{1..5} — the "scale
|
|
// smoothly from 1" contract for β-ignition / local-execution acceptance.
|
|
func TestNovaQuorum_Table(t *testing.T) {
|
|
want := map[int][3]int{ // n → {NovaQuorum, CrashTolerance, NovaBeta}
|
|
1: {1, 0, 1}, // lone node self-ignites; no fault tolerance
|
|
2: {2, 0, 2}, // both must agree; any drop pauses Nova
|
|
3: {2, 1, 2}, // majority 2 → tolerate 1 crash
|
|
4: {3, 1, 2}, // majority 3 → tolerate 1 crash
|
|
5: {3, 2, 2}, // majority 3 → tolerate 2 crashes (the deploy)
|
|
}
|
|
for n, w := range want {
|
|
if got := NovaQuorum(n); got != w[0] {
|
|
t.Errorf("n=%d NovaQuorum=%d want %d", n, got, w[0])
|
|
}
|
|
if got := CrashTolerance(n); got != w[1] {
|
|
t.Errorf("n=%d CrashTolerance=%d want %d", n, got, w[1])
|
|
}
|
|
if got := NovaBeta(n); got != w[2] {
|
|
t.Errorf("n=%d NovaBeta=%d want %d", n, got, w[2])
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestNovaQuorum_MajorityOverlap_CrashSafe: two Nova majorities always intersect
|
|
// (2·NovaQuorum(n) > n), so — with no equivocation — no two conflicting blocks can
|
|
// both ignite to Nova. Holds for every n.
|
|
func TestNovaQuorum_MajorityOverlap_CrashSafe(t *testing.T) {
|
|
for n := 1; n <= 1000; n++ {
|
|
if 2*NovaQuorum(n) <= n {
|
|
t.Fatalf("n=%d: majority overlap broken (2·%d ≤ %d) — Nova is not crash-safe",
|
|
n, NovaQuorum(n), n)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestNova_Below_Quasar proves the two rungs are DISTINCT authorization tiers and are
|
|
// never collapsed: Nova (local, majority) never requires MORE than Quasar (export,
|
|
// ⅔-stake), and sits STRICTLY below it wherever majority < ⅔ (n=3, n=5, and generally).
|
|
// The Quasar threshold is read from its ONE definition (config), never re-expressed.
|
|
func TestNova_Below_Quasar(t *testing.T) {
|
|
strictlySeparatedAt := map[int]bool{3: true, 5: true} // deploy cases we assert are strict
|
|
for n := 1; n <= 1000; n++ {
|
|
quasar := config.EqualStakeSupermajorityThreshold(n) // ⌊2n/3⌋+1, the single ⅔ definition
|
|
nova := NovaQuorum(n)
|
|
if nova > quasar {
|
|
t.Fatalf("n=%d: Nova majority %d exceeds Quasar ⅔ %d — tiers collapsed", n, nova, quasar)
|
|
}
|
|
if strictlySeparatedAt[n] && nova >= quasar {
|
|
t.Fatalf("n=%d: Nova %d not strictly below Quasar %d — no distinct local vs export boundary",
|
|
n, nova, quasar)
|
|
}
|
|
}
|
|
// The deploy case, spelled out: n=5 tolerates 2 crashes at Nova (3-of-5) but the
|
|
// exportable Quasar cert still needs 4-of-5 — the whole reason to keep them apart.
|
|
if NovaQuorum(5) != 3 || config.EqualStakeSupermajorityThreshold(5) != 4 {
|
|
t.Fatalf("n=5 deploy: Nova=%d Quasar=%d want 3 and 4", NovaQuorum(5), config.EqualStakeSupermajorityThreshold(5))
|
|
}
|
|
}
|
|
|
|
// TestFinalityLadder: the ladder is strictly ordered and the authorization boundaries
|
|
// are exactly Nova (local) / Quasar (export) / Horizon (irreversible) — Photon and Wave
|
|
// authorize nothing. This is the anti-semantic-collapse contract as code.
|
|
func TestFinalityLadder(t *testing.T) {
|
|
if !(Photon < Wave && Wave < Nova && Nova < Quasar && Quasar < Horizon) {
|
|
t.Fatal("finality ladder is not strictly ordered Photon<Wave<Nova<Quasar<Horizon")
|
|
}
|
|
for _, f := range []Finality{Photon, Wave} {
|
|
if f.AuthorizesLocalExecution() || f.AuthorizesExport() || f.AuthorizesIrreversibleSettlement() {
|
|
t.Fatalf("%s must authorize nothing (pre-ignition)", f)
|
|
}
|
|
}
|
|
if !Nova.AuthorizesLocalExecution() || Nova.AuthorizesExport() {
|
|
t.Fatal("Nova must authorize LOCAL execution only — never export")
|
|
}
|
|
if !Quasar.AuthorizesExport() || Quasar.AuthorizesIrreversibleSettlement() {
|
|
t.Fatal("Quasar must authorize export — but not irreversible settlement")
|
|
}
|
|
if !Horizon.AuthorizesIrreversibleSettlement() {
|
|
t.Fatal("Horizon must authorize irreversible settlement")
|
|
}
|
|
// Names are the lowercase ontology verbatim (RPC/metrics contract).
|
|
for f, name := range map[Finality]string{Photon: "photon", Wave: "wave", Nova: "nova", Quasar: "quasar", Horizon: "horizon"} {
|
|
if f.String() != name {
|
|
t.Errorf("%d.String()=%q want %q", f, f.String(), name)
|
|
}
|
|
}
|
|
}
|