Files
zeekayandHanzo Dev 4abf37c952 config: K is a number of draws, not a headcount
The live guard refused to start a chain whose K was below the number of
validators that existed. But sampling is stake-weighted: a validator is drawn in
proportion to what it staked, however many validators there are, and K only
sets how many draws a poll makes. Tying K to the headcount gave a validator
holding a billionth of the stake the same standing as one holding a fifth, and
refused to boot a producing chain because a small staker had joined.

The guard keeps what makes a committee safe — the overlap bound and at least
single-fault tolerance — and stops counting heads. The tier floor stays a
reported target: refusing to start does not add validators, it pins every node
on whatever it last booted.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-16 11:42:58 -07:00

656 lines
20 KiB
Go

// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package config
import "testing"
// TestPQMode_String pins the canonical wire name for every mode.
// Anyone who renames a constant without updating the env-var docs / HIP
// breaks this test.
func TestPQMode_String(t *testing.T) {
cases := []struct {
mode PQMode
name string
}{
{PQModeBLS, "bls"},
{PQModeNasua, "nasua"},
{PQModePulsar, "pulsar"},
{PQModeQuasar, "quasar"},
{PQModeMLDSA, "mldsa"},
}
for _, c := range cases {
if got := c.mode.String(); got != c.name {
t.Errorf("PQMode(%d).String() = %q, want %q", c.mode, got, c.name)
}
}
}
// TestPQMode_PolicyID pins the wire policy ID per mode. Anyone changing
// the wire layer must update this table at the same time.
func TestPQMode_PolicyID(t *testing.T) {
cases := []struct {
mode PQMode
want uint16
}{
{PQModeBLS, 1}, // PolicyQuorum
{PQModeNasua, 5}, // PolicyPQ
{PQModePulsar, 5}, // PolicyPQ (same wire shape, SHA-3 negotiated out-of-band)
{PQModeQuasar, 4}, // PolicyQuantum
{PQModeMLDSA, 6}, // PolicyPZ
}
for _, c := range cases {
if got := c.mode.PolicyID(); got != c.want {
t.Errorf("PQMode(%d).PolicyID() = %d, want %d", c.mode, got, c.want)
}
}
}
// TestPQMode_HashProfile asserts the canonical hash family per mode.
// Strings are the human-readable form of HashSuiteID and must stay in
// lockstep with TestPQMode_HashSuiteID below.
//
// NIST-aligned mapping (HIP-0077 §"Lux consensus PQ modes"):
//
// bls -> "none" no hash-family commitment
// corona -> "blake3-legacy" academic kernel, non-normative
// pulsar -> "sha3-nist" FIPS 202 + SP 800-185 (cSHAKE256 family)
// quasar -> "sha3-nist" same kernel as Pulsar
// mldsa -> "sha3-nist" ML-DSA-65 uses SHAKE256 (FIPS 202)
func TestPQMode_HashProfile(t *testing.T) {
cases := []struct {
mode PQMode
want string
}{
{PQModeBLS, "none"},
{PQModeNasua, "blake3-legacy"},
{PQModePulsar, "sha3-nist"},
{PQModeQuasar, "sha3-nist"},
{PQModeMLDSA, "sha3-nist"},
}
for _, c := range cases {
if got := c.mode.HashProfile(); got != c.want {
t.Errorf("PQMode(%d).HashProfile() = %q, want %q", c.mode, got, c.want)
}
}
}
// TestPQMode_HashSuiteID pins the wire HashSuiteID byte for every mode.
// Closes HIP-0077 red-review F1 (silent finality forks between hash-profile
// islands sharing the same PolicyID).
//
// Numbering per the NIST submission prescription:
//
// 0x00 HashSuiteNone BLS-only, no hash commitment
// 0x01 HashSuiteSHA3NIST FIPS 202 family (normative)
// 0x02 HashSuiteBLAKE3Legacy academic / federation, non-normative
//
// Renumbering an existing entry breaks every cert ever signed under
// that mode; ADDING a new entry only requires a new free integer.
func TestPQMode_HashSuiteID(t *testing.T) {
cases := []struct {
mode PQMode
want HashSuiteID
}{
{PQModeBLS, HashSuiteNone}, // 0x00
{PQModeNasua, HashSuiteBLAKE3Legacy}, // 0x02
{PQModePulsar, HashSuiteSHA3NIST}, // 0x01
{PQModeQuasar, HashSuiteSHA3NIST}, // 0x01
{PQModeMLDSA, HashSuiteSHA3NIST}, // 0x01 (SHAKE256 ∈ FIPS 202)
}
for _, c := range cases {
if got := c.mode.HashSuiteID(); got != c.want {
t.Errorf("PQMode(%d).HashSuiteID() = %d (%q), want %d (%q)",
c.mode, got, got.String(), c.want, c.want.String())
}
}
}
// TestHashSuiteID_StableIntegers locks the integer values themselves.
// The HashSuiteID byte is on the wire of every Quasar finality cert;
// renumbering breaks forward and backward compatibility simultaneously.
// New suites MUST claim the next free integer.
func TestHashSuiteID_StableIntegers(t *testing.T) {
cases := []struct {
suite HashSuiteID
want uint8
}{
{HashSuiteNone, 0x00},
{HashSuiteSHA3NIST, 0x01},
{HashSuiteBLAKE3Legacy, 0x02},
}
for _, c := range cases {
if got := uint8(c.suite); got != c.want {
t.Errorf("HashSuiteID %q = 0x%02x, want 0x%02x", c.suite.String(), got, c.want)
}
}
}
// TestHashSuiteID_String pins the human-readable name for every defined
// suite. The names appear in logs and error messages.
func TestHashSuiteID_String(t *testing.T) {
cases := []struct {
suite HashSuiteID
want string
}{
{HashSuiteNone, "none"},
{HashSuiteSHA3NIST, "sha3-nist"},
{HashSuiteBLAKE3Legacy, "blake3-legacy"},
}
for _, c := range cases {
if got := c.suite.String(); got != c.want {
t.Errorf("HashSuiteID(0x%02x).String() = %q, want %q", uint8(c.suite), got, c.want)
}
}
}
// TestHashSuiteID_IsNormative — production Lux meshes accept only
// FIPS-aligned suites. Non-normative suites may emit certs but not on
// any NIST-track production network.
func TestHashSuiteID_IsNormative(t *testing.T) {
if !HashSuiteSHA3NIST.IsNormative() {
t.Errorf("HashSuiteSHA3NIST must be normative")
}
if HashSuiteBLAKE3Legacy.IsNormative() {
t.Errorf("HashSuiteBLAKE3Legacy must be non-normative")
}
if HashSuiteNone.IsNormative() {
t.Errorf("HashSuiteNone (bls-only) must be non-normative")
}
}
// TestPQMode_SigSchemeID pins the default signature scheme the consensus
// layer will request for each mode. Mainnet declares PQModeMLDSA →
// raw ML-DSA-65 (audit-grade); operators that want Pulsar-M-65 threshold
// must opt in explicitly via the cert assembler.
func TestPQMode_SigSchemeID(t *testing.T) {
cases := []struct {
mode PQMode
want SigSchemeID
}{
{PQModeBLS, SigSchemeNone},
{PQModeNasua, SigSchemeNasua},
{PQModePulsar, SigSchemePulsarR},
{PQModeQuasar, SigSchemePulsarR},
{PQModeMLDSA, SigSchemeMLDSA65}, // raw ML-DSA-65 default; ops opt-in to Pulsar-M-65
}
for _, c := range cases {
if got := c.mode.SigSchemeID(); got != c.want {
t.Errorf("PQMode(%d).SigSchemeID() = 0x%02x (%q), want 0x%02x (%q)",
c.mode, uint8(got), got.String(), uint8(c.want), c.want.String())
}
}
}
// TestSigSchemeID_StableIntegers locks the byte values per HIP-0077
// numbering blocks. Renumbering breaks every cert ever emitted; new
// schemes claim the next free integer in their block:
//
// 0x10 BLS / 0x20 Corona / 0x30 Pulsar.R / 0x40 raw ML-DSA / 0x50 Pulsar-M
func TestSigSchemeID_StableIntegers(t *testing.T) {
cases := []struct {
scheme SigSchemeID
want uint8
}{
{SigSchemeNone, 0x00},
{SigSchemeBLS12381, 0x10},
{SigSchemeNasua, 0x20},
{SigSchemePulsarR, 0x30},
{SigSchemeMLDSA44, 0x41},
{SigSchemeMLDSA65, 0x42},
{SigSchemeMLDSA87, 0x43},
{SigSchemePulsar44, 0x51},
{SigSchemePulsar65, 0x52}, // production default
{SigSchemePulsar87, 0x53},
}
for _, c := range cases {
if got := uint8(c.scheme); got != c.want {
t.Errorf("SigSchemeID %q = 0x%02x, want 0x%02x", c.scheme.String(), got, c.want)
}
}
}
// TestSigSchemeID_String pins the canonical wire name.
func TestSigSchemeID_String(t *testing.T) {
cases := []struct {
scheme SigSchemeID
want string
}{
{SigSchemeNone, "none"},
{SigSchemeBLS12381, "bls12-381"},
{SigSchemeNasua, "nasua"},
{SigSchemePulsarR, "pulsar-r"},
{SigSchemeMLDSA44, "ml-dsa-44"},
{SigSchemeMLDSA65, "ml-dsa-65"},
{SigSchemeMLDSA87, "ml-dsa-87"},
{SigSchemePulsar44, "pulsar-m-44"},
{SigSchemePulsar65, "pulsar-m-65"},
{SigSchemePulsar87, "pulsar-m-87"},
}
for _, c := range cases {
if got := c.scheme.String(); got != c.want {
t.Errorf("SigSchemeID(0x%02x).String() = %q, want %q", uint8(c.scheme), got, c.want)
}
}
}
// TestSigSchemeID_IsPulsarM separates the Pulsar-M threshold family from
// everything else. Pulsar-M outputs verify under unmodified FIPS 204
// ML-DSA.Verify but are produced by a threshold protocol.
func TestSigSchemeID_IsPulsarM(t *testing.T) {
pulsarM := []SigSchemeID{SigSchemePulsar44, SigSchemePulsar65, SigSchemePulsar87}
for _, s := range pulsarM {
if !s.IsPulsarM() {
t.Errorf("%q must be Pulsar-M", s.String())
}
}
notPulsarM := []SigSchemeID{
SigSchemeNone, SigSchemeBLS12381, SigSchemeNasua,
SigSchemePulsarR,
SigSchemeMLDSA44, SigSchemeMLDSA65, SigSchemeMLDSA87,
}
for _, s := range notPulsarM {
if s.IsPulsarM() {
t.Errorf("%q must not be Pulsar-M", s.String())
}
}
}
// TestSigSchemeID_IsRawMLDSA covers the 0x40 block — single-party FIPS 204
// signatures (no threshold protocol).
func TestSigSchemeID_IsRawMLDSA(t *testing.T) {
rawMLDSA := []SigSchemeID{SigSchemeMLDSA44, SigSchemeMLDSA65, SigSchemeMLDSA87}
for _, s := range rawMLDSA {
if !s.IsRawMLDSA() {
t.Errorf("%q must be raw ML-DSA", s.String())
}
}
notRaw := []SigSchemeID{
SigSchemeNone, SigSchemeBLS12381, SigSchemeNasua, SigSchemePulsarR,
SigSchemePulsar44, SigSchemePulsar65, SigSchemePulsar87,
}
for _, s := range notRaw {
if s.IsRawMLDSA() {
t.Errorf("%q must not be raw ML-DSA", s.String())
}
}
}
// TestSigSchemeID_VerifiesUnderFIPS204 — both raw ML-DSA (0x40) and
// Pulsar-M (0x50) verify under FIPS 204 ML-DSA.Verify.
func TestSigSchemeID_VerifiesUnderFIPS204(t *testing.T) {
yes := []SigSchemeID{
SigSchemeMLDSA44, SigSchemeMLDSA65, SigSchemeMLDSA87,
SigSchemePulsar44, SigSchemePulsar65, SigSchemePulsar87,
}
for _, s := range yes {
if !s.VerifiesUnderFIPS204() {
t.Errorf("%q must verify under FIPS 204", s.String())
}
}
no := []SigSchemeID{
SigSchemeNone, SigSchemeBLS12381, SigSchemeNasua, SigSchemePulsarR,
}
for _, s := range no {
if s.VerifiesUnderFIPS204() {
t.Errorf("%q must not verify under FIPS 204", s.String())
}
}
}
// TestProofPolicyID_StableIntegers locks the wire bytes for the ZK proof
// policy enum (security class). Z-Chain v1 ships with STARK_FRI_SHA3_PQ
// (0x10); classical IDs exist on the wire so audit pipelines flag
// misconfigurations.
//
// Renumbering an entry breaks every cert ever signed under it. Adding a
// new entry only requires a new free integer in the appropriate block.
func TestProofPolicyID_StableIntegers(t *testing.T) {
cases := []struct {
ps ProofPolicyID
want uint8
}{
{ProofPolicyNone, 0x00},
{ProofPolicySTARKFRISHA3PQ, 0x10},
{ProofPolicySTARKFRIKeccak, 0x11},
{ProofPolicyGroth16BN254Forbid, 0x80},
{ProofPolicyPLONKKZGForbid, 0x81},
{ProofPolicyHaloECForbid, 0x82},
{ProofPolicyIPAECForbid, 0x83},
}
for _, c := range cases {
if got := uint8(c.ps); got != c.want {
t.Errorf("ProofPolicyID %q = 0x%02x, want 0x%02x", c.ps.String(), got, c.want)
}
}
}
// TestProofPolicyID_String pins canonical wire names.
func TestProofPolicyID_String(t *testing.T) {
cases := []struct {
ps ProofPolicyID
want string
}{
{ProofPolicyNone, "none"},
{ProofPolicySTARKFRISHA3PQ, "stark-fri-sha3-pq"},
{ProofPolicySTARKFRIKeccak, "stark-fri-keccak-pq"},
{ProofPolicyGroth16BN254Forbid, "groth16-bn254-classical-forbidden-in-pq"},
{ProofPolicyPLONKKZGForbid, "plonk-kzg-classical-forbidden-in-pq"},
{ProofPolicyHaloECForbid, "halo-ec-classical-forbidden-in-pq"},
{ProofPolicyIPAECForbid, "ipa-ec-classical-forbidden-in-pq"},
}
for _, c := range cases {
if got := c.ps.String(); got != c.want {
t.Errorf("ProofPolicyID(0x%02x).String() = %q, want %q", uint8(c.ps), got, c.want)
}
}
}
// TestProofPolicyID_IsPostQuantum gates strict-PQ verifier acceptance.
// Lux primary-network producers MUST emit only IsPostQuantum=true; any
// classical wire byte makes it onto the wire only as a forbidden marker.
func TestProofPolicyID_IsPostQuantum(t *testing.T) {
pq := []ProofPolicyID{
ProofPolicySTARKFRISHA3PQ,
ProofPolicySTARKFRIKeccak,
}
for _, p := range pq {
if !p.IsPostQuantum() {
t.Errorf("%q must be IsPostQuantum=true", p.String())
}
}
notPQ := []ProofPolicyID{
ProofPolicyNone, // None is not PQ-positive; it's "no commitment".
ProofPolicyGroth16BN254Forbid,
ProofPolicyPLONKKZGForbid,
ProofPolicyHaloECForbid,
ProofPolicyIPAECForbid,
}
for _, p := range notPQ {
if p.IsPostQuantum() {
t.Errorf("%q must be IsPostQuantum=false", p.String())
}
}
}
// TestProofPolicyID_IsForbiddenInPQMode — strict-PQ deployments use this
// to refuse misconfigured certs. The forbidden markers exist as explicit
// wire bytes so audit tooling can name them precisely.
func TestProofPolicyID_IsForbiddenInPQMode(t *testing.T) {
for _, p := range []ProofPolicyID{
ProofPolicyGroth16BN254Forbid,
ProofPolicyPLONKKZGForbid,
ProofPolicyHaloECForbid,
ProofPolicyIPAECForbid,
} {
if !p.IsForbiddenInPQMode() {
t.Errorf("%q must be forbidden in PQ mode", p.String())
}
}
if ProofPolicySTARKFRISHA3PQ.IsForbiddenInPQMode() {
t.Errorf("STARK_FRI_SHA3_PQ must NOT be forbidden")
}
}
// TestProofPolicyID_LegacyAliases keeps the deprecated ProofSystem* names
// compiling for one release. New code must use the ProofPolicy* names.
// Drop this test when the aliases are removed at the 2026-Aug-31 freeze.
func TestProofPolicyID_LegacyAliases(t *testing.T) {
if ProofSystemNone != ProofPolicyNone {
t.Errorf("legacy alias ProofSystemNone must equal ProofPolicyNone")
}
if ProofSystemSTARKFRISHA3PQ != ProofPolicySTARKFRISHA3PQ {
t.Errorf("legacy alias ProofSystemSTARKFRISHA3PQ must equal ProofPolicySTARKFRISHA3PQ")
}
if ProofSystemSTARKFRIKeccakPQ != ProofPolicySTARKFRIKeccak {
t.Errorf("legacy alias ProofSystemSTARKFRIKeccakPQ must equal ProofPolicySTARKFRIKeccak")
}
if ProofSystemGroth16BN254Forbid != ProofPolicyGroth16BN254Forbid {
t.Errorf("legacy alias ProofSystemGroth16BN254Forbid must equal ProofPolicyGroth16BN254Forbid")
}
if ProofSystemKZGForbid != ProofPolicyPLONKKZGForbid {
t.Errorf("legacy alias ProofSystemKZGForbid must equal ProofPolicyPLONKKZGForbid")
}
}
// TestProofBackendID_StableIntegers locks the wire bytes for the proof
// **backend** (implementation) enum, orthogonal to ProofPolicyID. Backends
// covered:
//
// 0x20 RISC0_SUCCINCT_STARK succinct receipt path (no Groth16 wrapper)
// 0x21 SP1_COMPRESSED_STARK SP1 compressed STARK
// 0x22 P3Q_STARK_FRI_SHA3 Lux P3Q (Plonky3 fork, cSHAKE256 Merkle)
// 0x23 STONE_CAIRO_STARK Cairo / Stone backend
// 0x24 STWO_CIRCLE_STARK Stwo Circle STARK
// 0x70 RISC0_RAW_STARK_DEV dev/CI only
// 0x71 SP1_CORE_STARK_DEV dev/CI only
// 0x80 GROTH16_WRAP_FORBID forbidden wrapper in strict-PQ
// 0x81 KZG_WRAP_FORBID forbidden wrapper in strict-PQ
func TestProofBackendID_StableIntegers(t *testing.T) {
cases := []struct {
backend ProofBackendID
want uint8
}{
{ProofBackendNone, 0x00},
{ProofBackendRISC0SuccinctSTARK, 0x20},
{ProofBackendSP1CompressedSTARK, 0x21},
{ProofBackendP3QSTARKFRISHA3, 0x22},
{ProofBackendStoneCairoSTARK, 0x23},
{ProofBackendStwoCircleSTARK, 0x24},
{ProofBackendRISC0RawSTARKDev, 0x70},
{ProofBackendSP1CoreSTARKDev, 0x71},
{ProofBackendGroth16WrapForbid, 0x80},
{ProofBackendKZGWrapForbid, 0x81},
}
for _, c := range cases {
if got := uint8(c.backend); got != c.want {
t.Errorf("ProofBackendID %q = 0x%02x, want 0x%02x",
c.backend.String(), got, c.want)
}
}
}
// TestProofBackendID_String pins canonical wire names so logs and audit
// pipelines see the same string in every cluster.
func TestProofBackendID_String(t *testing.T) {
cases := []struct {
backend ProofBackendID
want string
}{
{ProofBackendNone, "none"},
{ProofBackendRISC0SuccinctSTARK, "risc0-succinct-stark-pq"},
{ProofBackendSP1CompressedSTARK, "sp1-compressed-stark-pq"},
{ProofBackendP3QSTARKFRISHA3, "p3q-stark-fri-sha3-pq"},
{ProofBackendStoneCairoSTARK, "stone-cairo-stark-pq"},
{ProofBackendStwoCircleSTARK, "stwo-circle-stark-pq"},
{ProofBackendRISC0RawSTARKDev, "risc0-raw-stark-dev"},
{ProofBackendSP1CoreSTARKDev, "sp1-core-stark-dev"},
{ProofBackendGroth16WrapForbid, "groth16-wrap-classical-forbidden-in-pq"},
{ProofBackendKZGWrapForbid, "kzg-wrap-classical-forbidden-in-pq"},
}
for _, c := range cases {
if got := c.backend.String(); got != c.want {
t.Errorf("ProofBackendID(0x%02x).String() = %q, want %q",
uint8(c.backend), got, c.want)
}
}
}
// TestProofBackendID_IsProductionPQ separates the production block (0x20)
// from dev (0x70) and forbidden (0x80). Strict-PQ profiles MUST allow only
// production backends.
func TestProofBackendID_IsProductionPQ(t *testing.T) {
prod := []ProofBackendID{
ProofBackendRISC0SuccinctSTARK,
ProofBackendSP1CompressedSTARK,
ProofBackendP3QSTARKFRISHA3,
ProofBackendStoneCairoSTARK,
ProofBackendStwoCircleSTARK,
}
for _, b := range prod {
if !b.IsProductionPQ() {
t.Errorf("%q must be IsProductionPQ=true", b.String())
}
}
nonProd := []ProofBackendID{
ProofBackendNone,
ProofBackendRISC0RawSTARKDev,
ProofBackendSP1CoreSTARKDev,
ProofBackendGroth16WrapForbid,
ProofBackendKZGWrapForbid,
}
for _, b := range nonProd {
if b.IsProductionPQ() {
t.Errorf("%q must be IsProductionPQ=false", b.String())
}
}
}
// TestProofBackendID_IsForbiddenInPQMode mirrors the policy-layer test.
func TestProofBackendID_IsForbiddenInPQMode(t *testing.T) {
for _, b := range []ProofBackendID{
ProofBackendGroth16WrapForbid,
ProofBackendKZGWrapForbid,
} {
if !b.IsForbiddenInPQMode() {
t.Errorf("%q must be forbidden in PQ mode", b.String())
}
}
for _, b := range []ProofBackendID{
ProofBackendNone,
ProofBackendRISC0SuccinctSTARK,
ProofBackendSP1CompressedSTARK,
ProofBackendP3QSTARKFRISHA3,
ProofBackendStoneCairoSTARK,
ProofBackendStwoCircleSTARK,
ProofBackendRISC0RawSTARKDev,
ProofBackendSP1CoreSTARKDev,
} {
if b.IsForbiddenInPQMode() {
t.Errorf("%q must NOT carry the forbidden marker", b.String())
}
}
}
// TestIdentitySchemeID_StableIntegers locks the wire bytes for validator
// identity schemes. Block 0x40 mirrors raw ML-DSA in SigSchemeID so the
// byte pattern is consistent across the wire.
//
// 0x00 None no identity scheme committed
// 0x41 ML-DSA-44 NIST PQ Cat 2 (devnet only)
// 0x42 ML-DSA-65 NIST PQ Cat 3 (production identity default)
// 0x43 ML-DSA-87 NIST PQ Cat 5 (high-value root identities)
func TestIdentitySchemeID_StableIntegers(t *testing.T) {
cases := []struct {
ident IdentitySchemeID
want uint8
}{
{IdentitySchemeNone, 0x00},
{IdentitySchemeMLDSA44, 0x41},
{IdentitySchemeMLDSA65, 0x42}, // production default
{IdentitySchemeMLDSA87, 0x43},
}
for _, c := range cases {
if got := uint8(c.ident); got != c.want {
t.Errorf("IdentitySchemeID %q = 0x%02x, want 0x%02x",
c.ident.String(), got, c.want)
}
}
}
// TestIdentitySchemeID_String pins canonical wire names.
func TestIdentitySchemeID_String(t *testing.T) {
cases := []struct {
ident IdentitySchemeID
want string
}{
{IdentitySchemeNone, "none"},
{IdentitySchemeMLDSA44, "ml-dsa-44"},
{IdentitySchemeMLDSA65, "ml-dsa-65"},
{IdentitySchemeMLDSA87, "ml-dsa-87"},
}
for _, c := range cases {
if got := c.ident.String(); got != c.want {
t.Errorf("IdentitySchemeID(0x%02x).String() = %q, want %q",
uint8(c.ident), got, c.want)
}
}
}
// TestIdentitySchemeID_IsFIPS204 — every non-None identity scheme uses
// unmodified FIPS 204 ML-DSA verification.
func TestIdentitySchemeID_IsFIPS204(t *testing.T) {
for _, i := range []IdentitySchemeID{
IdentitySchemeMLDSA44,
IdentitySchemeMLDSA65,
IdentitySchemeMLDSA87,
} {
if !i.IsFIPS204() {
t.Errorf("%q must verify under FIPS 204", i.String())
}
}
if IdentitySchemeNone.IsFIPS204() {
t.Errorf("IdentitySchemeNone must not advertise FIPS 204")
}
}
// TestPQMode_DKGRequired asserts the DKG class. Public chains MUST pick
// modes whose DKG class != "trusted-dealer".
func TestPQMode_DKGRequired(t *testing.T) {
cases := []struct {
mode PQMode
want string
}{
{PQModeBLS, "none"},
{PQModeNasua, "trusted-dealer"},
{PQModePulsar, "pedersen-dkg-over-rq"},
{PQModeQuasar, "pedersen-dkg-over-rq"},
{PQModeMLDSA, "none"},
}
for _, c := range cases {
if got := c.mode.DKGRequired(); got != c.want {
t.Errorf("PQMode(%d).DKGRequired() = %q, want %q", c.mode, got, c.want)
}
}
}
// TestPQMode_SuitableForPublicChain enforces HIP-0077 §"PQ defaults":
// production Lux meshes accept Pulsar / Quasar / MLDSA. BLS (no PQ)
// and Corona (trusted-dealer DKG) are rejected.
func TestPQMode_SuitableForPublicChain(t *testing.T) {
cases := []struct {
mode PQMode
want bool
}{
{PQModeBLS, false},
{PQModeNasua, false},
{PQModePulsar, true},
{PQModeQuasar, true},
{PQModeMLDSA, true},
}
for _, c := range cases {
if got := c.mode.SuitableForPublicChain(); got != c.want {
t.Errorf("PQMode(%d).SuitableForPublicChain() = %v, want %v", c.mode, got, c.want)
}
}
}
// TestPQMode_IsPostQuantum is true for everything except plain BLS.
func TestPQMode_IsPostQuantum(t *testing.T) {
cases := []struct {
mode PQMode
want bool
}{
{PQModeBLS, false},
{PQModeNasua, true},
{PQModePulsar, true},
{PQModeQuasar, true},
{PQModeMLDSA, true},
}
for _, c := range cases {
if got := c.mode.IsPostQuantum(); got != c.want {
t.Errorf("PQMode(%d).IsPostQuantum() = %v, want %v", c.mode, got, c.want)
}
}
}