Files
zeekay f6753f3b53 quasar: complete Corona purge — Corona (production R-LWE) + Nasua (academic upstream)
Followup to the QuasarCert field rename (commit d1c8a2cc). This sweeps
the remaining Corona identifiers and prose out of the consensus repo
entirely:

  - Identifiers (Go code):
    coronaThreshold   → coronaThreshold      (import alias)
    coronaGroupKey    → coronaGroupKey
    coronaSigners     → coronaSigners
    coronaShares      → coronaShares
    coronaGobEncode   → coronaGobEncode
    coronaGobDecode   → coronaGobDecode
    decodeCoronaSig   → decodeCoronaSig
    EncodeCoronaSig   → EncodeCoronaSig
    CoronaRound1{,Data,State} → CoronaRound1{,Data,State}
    CoronaGroupKey/Pub/Shares → Corona*

  - File rename:
    protocol/quasar/corona_gob.go → corona_gob.go

  - PQMode constants:
    PQModeCorona            → PQModeNasua
    SigSchemeCoronaAcademic → SigSchemeNasua
    "corona" / "corona-academic" string forms → "nasua"

  - Prose & comments throughout the repo: "Corona" → "Corona" when
    naming the production Ring-LWE library, "Nasua" when naming the
    historical academic upstream (luxfi/nasua, formerly luxfi/corona).

Canonical Pulsar family taxonomy after this rename:

  Nasua  — academic R-LWE 2-round threshold sig (Boschini et al,
           ePrint 2024/1113); trusted-dealer DKG; kept as historical
           reference fork only. NOT for an open public chain.
  Corona — production Ring-LWE threshold ML-DSA (Pedersen DKG over R_q
           + proactive resharing); `github.com/luxfi/corona`. The
           Ring-LWE leg of QuasarCert.
  Pulsar — production Module-LWE threshold ML-DSA (FIPS 204
           byte-equal); `github.com/luxfi/pulsar`. The Module-LWE
           leg of QuasarCert. Pure-PQ pair = Corona + Pulsar.

Wire-format unchanged. Tests: 28/28 packages green (-short suite,
quasar 29s, integration 58s, no regressions).
2026-05-12 08:58:51 -07:00

245 lines
8.0 KiB
Go

// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package quasar
import (
"context"
"errors"
"testing"
"github.com/luxfi/consensus/config"
)
// testNonZeroDigest returns a fixed non-zero RoundDigest for tests
// that just need a valid (non-zero) subject. Production code MUST
// build the digest via ComputeRoundDigest; this helper bypasses that
// constructor only because the surrounding test isolates a different
// behaviour (witness floor, hash-suite propagation) and not the
// digest construction itself.
func testNonZeroDigest() RoundDigest {
return RoundDigest{
0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
}
}
// fakeP always succeeds (we want to isolate Q/Z floor behaviour).
type fakeP struct{}
func (fakeP) Witness(ctx context.Context, digest RoundDigest) ([]byte, []byte, error) {
return []byte("p-sig"), []byte("p-signers"), nil
}
// failQ always returns ErrWitnessUnavailable.
type failQ struct{}
func (failQ) Witness(ctx context.Context, digest RoundDigest) ([]byte, error) {
return nil, ErrWitnessUnavailable
}
// failZ always returns ErrWitnessUnavailable.
type failZ struct{}
func (failZ) Witness(ctx context.Context, digest RoundDigest, vk [][]byte) ([]byte, error) {
return nil, ErrWitnessUnavailable
}
// goodQ returns a non-nil signature.
type goodQ struct{}
func (goodQ) Witness(ctx context.Context, digest RoundDigest) ([]byte, error) {
return []byte("q-sig"), nil
}
// goodZ returns a non-nil proof.
type goodZ struct{}
func (goodZ) Witness(ctx context.Context, digest RoundDigest, vk [][]byte) ([]byte, error) {
return []byte("z-proof"), nil
}
// TestRun_LegacyBestEffort_NoFloor preserves the pre-floor behaviour for
// callers that haven't migrated. MinPolicy=0 → missing optional witnesses
// produce a downgraded bundle without error.
func TestRun_LegacyBestEffort_NoFloor(t *testing.T) {
ws := WitnessSet{P: fakeP{}, Q: failQ{}, Z: failZ{}}
rw, err := ws.Run(context.Background(), testNonZeroDigest(), nil)
if err != nil {
t.Fatalf("legacy mode: unexpected error: %v", err)
}
if rw.Q != nil || rw.Z != nil {
t.Fatalf("legacy mode: expected nil Q/Z, got Q=%v Z=%v", rw.Q != nil, rw.Z != nil)
}
if string(rw.PSig) != "p-sig" {
t.Fatalf("legacy mode: expected P=p-sig, got %s", rw.PSig)
}
}
// TestRun_QuasarFloor_RefusesWhenZFails proves the headline F2 fix:
// a Quasar-declared chain whose Z producer fails MUST NOT silently
// emit a PolicyPQ cert. It MUST refuse the round entirely.
func TestRun_QuasarFloor_RefusesWhenZFails(t *testing.T) {
ws := WitnessSet{
P: fakeP{},
Q: goodQ{},
Z: failZ{},
MinPolicy: 4, // PolicyQuantum
}
_, err := ws.Run(context.Background(), testNonZeroDigest(), nil)
if !errors.Is(err, ErrWitnessFloorBreached) {
t.Fatalf("expected ErrWitnessFloorBreached, got: %v", err)
}
}
// TestRun_QuasarFloor_RefusesWhenQFails — same protection in the other
// direction. Quasar requires BOTH lattice witnesses; either one missing
// → refused round.
func TestRun_QuasarFloor_RefusesWhenQFails(t *testing.T) {
ws := WitnessSet{
P: fakeP{},
Q: failQ{},
Z: goodZ{},
MinPolicy: 4, // PolicyQuantum
}
_, err := ws.Run(context.Background(), testNonZeroDigest(), nil)
if !errors.Is(err, ErrWitnessFloorBreached) {
t.Fatalf("expected ErrWitnessFloorBreached, got: %v", err)
}
}
// TestRun_QuasarFloor_AcceptsWhenBothPresent — happy path: both lattice
// witnesses available → cert finalises at PolicyQuantum.
func TestRun_QuasarFloor_AcceptsWhenBothPresent(t *testing.T) {
ws := WitnessSet{
P: fakeP{},
Q: goodQ{},
Z: goodZ{},
MinPolicy: 4, // PolicyQuantum
}
rw, err := ws.Run(context.Background(), testNonZeroDigest(), nil)
if err != nil {
t.Fatalf("expected success, got: %v", err)
}
if rw.Q == nil || rw.Z == nil {
t.Fatalf("expected both Q and Z, got Q=%v Z=%v", rw.Q != nil, rw.Z != nil)
}
}
// TestRun_PulsarFloor_AcceptsPQ — a chain that declared Pulsar mode
// (PolicyPQ floor) is happy with P+Q, even when Z is absent.
func TestRun_PulsarFloor_AcceptsPQ(t *testing.T) {
ws := WitnessSet{
P: fakeP{},
Q: goodQ{},
Z: nil, // explicitly unconfigured
MinPolicy: 5, // PolicyPQ
}
rw, err := ws.Run(context.Background(), testNonZeroDigest(), nil)
if err != nil {
t.Fatalf("expected success, got: %v", err)
}
if rw.Q == nil {
t.Fatalf("expected Q, got nil")
}
}
// TestRun_PulsarFloor_RefusesWhenQFails — Pulsar mode without Q can't
// downgrade silently to BLS-only.
func TestRun_PulsarFloor_RefusesWhenQFails(t *testing.T) {
ws := WitnessSet{
P: fakeP{},
Q: failQ{},
MinPolicy: 5, // PolicyPQ
}
_, err := ws.Run(context.Background(), testNonZeroDigest(), nil)
if !errors.Is(err, ErrWitnessFloorBreached) {
t.Fatalf("expected ErrWitnessFloorBreached, got: %v", err)
}
}
// TestRun_BLSFloor_AlwaysAccepts — chains that declared BLS-only mode
// finalise on P alone. (Lux mesh refuses this in production policy,
// but the consensus engine must support it for benchmarks/legacy.)
func TestRun_BLSFloor_AlwaysAccepts(t *testing.T) {
ws := WitnessSet{
P: fakeP{},
MinPolicy: 1, // PolicyQuorum
}
_, err := ws.Run(context.Background(), testNonZeroDigest(), nil)
if err != nil {
t.Fatalf("BLS floor: unexpected error: %v", err)
}
}
// TestRun_PropagatesHashSuiteID_FromMode is the F1 fix at the producer
// layer: every PQMode MUST emit a RoundWitnesses whose HashSuiteID matches
// config.PQMode.HashSuiteID(). The downstream cert assembler stamps the
// envelope's HashSuiteID byte from this field and binds it into the
// transcript.
//
// HIP-0077 §"Lux consensus PQ modes" red-review F1. Without this, a Pulsar
// (SHA-3) producer and a Corona (BLAKE3) producer at the same PolicyID 5
// emit indistinguishable certs and the receiver picks the wrong kernel.
func TestRun_PropagatesHashSuiteID_FromMode(t *testing.T) {
cases := []struct {
mode config.PQMode
want config.HashSuiteID
}{
{config.PQModeBLS, config.HashSuiteNone},
{config.PQModeNasua, config.HashSuiteBLAKE3Legacy},
{config.PQModePulsar, config.HashSuiteSHA3NIST},
{config.PQModeQuasar, config.HashSuiteSHA3NIST},
{config.PQModeMLDSA, config.HashSuiteSHA3NIST},
}
for _, c := range cases {
ws := WitnessSet{P: fakeP{}, Mode: c.mode}
rw, err := ws.Run(context.Background(), testNonZeroDigest(), nil)
if err != nil {
t.Fatalf("mode=%s: unexpected error: %v", c.mode, err)
}
if rw.HashSuiteID != c.want {
t.Errorf("mode=%s: RoundWitnesses.HashSuiteID = %d (%q), want %d (%q)",
c.mode, rw.HashSuiteID, rw.HashSuiteID.String(),
c.want, c.want.String())
}
}
}
// TestRun_HashSuiteID_DefaultsToNoneWhenModeUnset preserves legacy callers
// that haven't migrated yet: a zero-valued WitnessSet.Mode (PQModeBLS) maps
// to HashSuiteNone on the wire, matching the historical BLS-only cert
// shape. New code MUST set Mode explicitly.
func TestRun_HashSuiteID_DefaultsToNoneWhenModeUnset(t *testing.T) {
ws := WitnessSet{P: fakeP{}} // Mode zero-valued
rw, err := ws.Run(context.Background(), testNonZeroDigest(), nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if rw.HashSuiteID != config.HashSuiteNone {
t.Errorf("unset Mode: HashSuiteID = %d, want HashSuiteNone (0)", rw.HashSuiteID)
}
}
// TestEffectivePolicyID maps witness presence to wire policy ID. Direct
// inverse of config.PQMode.PolicyID().
func TestEffectivePolicyID(t *testing.T) {
cases := []struct {
name string
rw *RoundWitnesses
want uint16
}{
{"P only", &RoundWitnesses{}, 1},
{"P+Q", &RoundWitnesses{Q: []byte("x")}, 5},
{"P+Z", &RoundWitnesses{Z: []byte("x")}, 6},
{"P+Q+Z", &RoundWitnesses{Q: []byte("x"), Z: []byte("x")}, 4},
}
for _, c := range cases {
if got := effectivePolicyID(c.rw); got != c.want {
t.Errorf("%s: effectivePolicyID = %d, want %d", c.name, got, c.want)
}
}
}