Files
zeekayandHanzo Dev fd0b427d46 refactor(layout): ref/go/pkg -> pkg, ref/go/cmd -> cmd (canonical Go layout); home pulsard daemon at pkg/pulsard
Moves the Go reference implementation to the idiomatic repo-root pkg/ (library)
+ cmd/ (genkat tool) layout, dropping the ref/go/ nesting. Imports become
github.com/luxfi/pulsar/pkg/pulsar. The pulsar DAEMON (formerly the unhomed
standalone github.com/luxfi/pulsard module) now lives canonically at
github.com/luxfi/pulsar/pkg/pulsard. No replace directives.

BREAKING: consumers must update github.com/luxfi/pulsar/ref/go/pkg/... ->
github.com/luxfi/pulsar/pkg/... and bump to v1.9.0.

Co-authored-by: Hanzo Dev <[email protected]>
2026-06-30 15:59:28 -07:00

137 lines
4.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// keyera_test.go — key-era records, the resolver, committee bounds, and the
// session/nonce plumbing (DeriveSessionID, NoncePool).
package pulsard_test
import (
"errors"
"testing"
mldsa65 "github.com/luxfi/crypto/pq/mldsa/mldsa65"
"github.com/luxfi/pulsar/pkg/pulsard"
"github.com/luxfi/warp"
)
// TestNewKeyEra_RejectsMalformedKey asserts a non-ML-DSA-65 public key is
// rejected at era creation (a malformed key can never verify).
func TestNewKeyEra_RejectsMalformedKey(t *testing.T) {
if _, err := pulsard.NewKeyEra(testID("c"), testID("s"), 1, 0, 0, []byte{1, 2, 3}, testThreshold, "x"); !errors.Is(err, pulsard.ErrBadGroupKey) {
t.Fatalf("NewKeyEra(short key) err = %v, want ErrBadGroupKey", err)
}
// A correctly-sized but invalid key is also rejected.
if _, err := pulsard.NewKeyEra(testID("c"), testID("s"), 1, 0, 0, make([]byte, mldsa65.PublicKeySize), testThreshold, "x"); err != nil {
// circl accepts an all-zero key as a structurally valid (if useless)
// public key, so this may or may not error; the load-bearing case is the
// wrong-length one above. We only assert NewKeyEra pins the suite.
t.Logf("all-zero key: %v (acceptable)", err)
}
}
// TestNewKeyEra_PinsSuite asserts the era's SchemeID is always the Pulsar suite.
func TestNewKeyEra_PinsSuite(t *testing.T) {
_, era, err := pulsard.NewReferenceDealer(testID("c"), testID("s"), 1, 0, 0, testThreshold, nil)
if err != nil {
t.Fatalf("dealer: %v", err)
}
if era.SchemeID != warp.SuitePulsarThresholdMLDSA65 {
t.Fatalf("SchemeID = %q, want Pulsar suite", era.SchemeID)
}
}
// TestKeyEraStore_RoundTripAndMiss asserts Put/Resolve roundtrips and a miss
// fails closed.
func TestKeyEraStore_RoundTripAndMiss(t *testing.T) {
_, era, err := pulsard.NewReferenceDealer(testID("c"), testID("s"), 9, 2, 0, testThreshold, nil)
if err != nil {
t.Fatalf("dealer: %v", err)
}
store := pulsard.NewKeyEraStore()
store.Put(era)
got, err := store.ResolvePulsarKeyEra(era.SignerSetID, era.KeyEraID, era.Generation)
if err != nil {
t.Fatalf("Resolve: %v", err)
}
if got.KeyEraID != era.KeyEraID || got.Generation != era.Generation {
t.Fatal("resolved era identifiers mismatch")
}
if _, err := store.ResolvePulsarKeyEra(era.SignerSetID, era.KeyEraID, era.Generation+1); !errors.Is(err, pulsard.ErrKeyEraNotFound) {
t.Fatalf("miss err = %v, want ErrKeyEraNotFound", err)
}
}
// TestCommittee asserts validity bounds and the TALUS N≥2T−1 MPC bound.
func TestCommittee(t *testing.T) {
if err := (pulsard.Committee{Threshold: 0, Parties: 3}).Validate(); err == nil {
t.Error("T=0 accepted")
}
if err := (pulsard.Committee{Threshold: 4, Parties: 3}).Validate(); err == nil {
t.Error("T>N accepted")
}
if err := (pulsard.Committee{Threshold: 3, Parties: 5}).Validate(); err != nil {
t.Errorf("valid committee rejected: %v", err)
}
for _, tc := range []struct{ t, want int }{{1, 1}, {2, 2}, {3, 5}, {4, 7}} {
if got := (pulsard.Committee{Threshold: tc.t}).MinPartiesMPC(); got != tc.want {
t.Errorf("MinPartiesMPC(T=%d) = %d, want %d", tc.t, got, tc.want)
}
}
}
// TestDeriveSessionID asserts determinism and binding to (subject, era).
func TestDeriveSessionID(t *testing.T) {
_, era, _ := pulsard.NewReferenceDealer(testID("c"), testID("s"), 1, 0, 0, testThreshold, nil)
subA := subject32("A")
subB := subject32("B")
if pulsard.DeriveSessionID(subA, era) != pulsard.DeriveSessionID(subA, era) {
t.Fatal("DeriveSessionID not deterministic")
}
if pulsard.DeriveSessionID(subA, era) == pulsard.DeriveSessionID(subB, era) {
t.Fatal("different subjects yield same session id")
}
era2 := era
era2.KeyEraID++
if pulsard.DeriveSessionID(subA, era) == pulsard.DeriveSessionID(subA, era2) {
t.Fatal("different eras yield same session id")
}
}
// TestNoncePool asserts canonical selection is deterministic, consumption is
// one-shot, and exhaustion fails closed.
func TestNoncePool(t *testing.T) {
pool := pulsard.NewNoncePool()
ids := [][32]byte{{1}, {2}, {3}, {4}, {5}}
for _, id := range ids {
pool.Add(id)
}
pool.Add(ids[0]) // duplicate: idempotent
if pool.Available() != 5 {
t.Fatalf("Available = %d, want 5", pool.Available())
}
sid := [32]byte{0xAB}
pick1, err := pool.SelectCanonical(sid)
if err != nil {
t.Fatalf("SelectCanonical: %v", err)
}
pick2, _ := pool.SelectCanonical(sid)
if pick1 != pick2 {
t.Fatal("canonical selection not deterministic for the same session")
}
// Consume everything; selection must then fail closed.
for _, id := range ids {
pool.Consume(id)
}
if pool.Available() != 0 {
t.Fatalf("Available after consume = %d, want 0", pool.Available())
}
if _, err := pool.SelectCanonical(sid); !errors.Is(err, pulsard.ErrNoncePoolEmpty) {
t.Fatalf("exhausted pool err = %v, want ErrNoncePoolEmpty", err)
}
}