mirror of
https://github.com/luxfi/corona.git
synced 2026-07-27 02:50:34 +00:00
Final public-BFT audit (per AUDIT-2026-05.md §10) flagged the v0.7.3 asymmetry where keyera.Bootstrap / keyera.BootstrapWithSuite still routed through the legacy single-dealer Shamir share-out while keyera.Reanchor (v0.7.4) had already flipped to Pedersen-DKG. Callers picking the "obvious" name silently inherited the dealer caveat at chain genesis. v0.7.5 flips both Bootstrap defaults to route through BootstrapPedersen (Pedersen-DKG over R_q + Path (a) noise flooding) so no party ever holds the master secret s at any point in the ceremony. The legacy trusted-dealer ceremony path is reachable only via the explicit BootstrapTrustedDealer / BootstrapTrustedDealerWithSuite / ReanchorTrustedDealer / ReanchorTrustedDealerWithSuite names. The shared trusted-dealer body lives in the unexported bootstrapTrustedDealerImpl, so the public-facing aliases (and the Reanchor trusted-dealer alias) cannot drift from each other. Structural invariant: BootstrapPedersen requires n >= 2 && t < n. Deployments that need every validator to sign (t == n) must select BootstrapTrustedDealer explicitly; the unqualified Bootstrap will fail with ErrBootstrapPedersenShape on t == n, surfacing the trust-model decision at the call site rather than silently routing to the dealer path. Tests updated: keyera_test.go, hashsuite_immut_test.go, bootstrap_pedersen_test.go, reanchor_pedersen_test.go — drop t == n from the public-BFT cases (now use t = n - 1) and propagate the 3-tuple return from the new Bootstrap signature. Cross-runtime KAT byte-equality: scripts/regen-kats.sh updated for the post-Corona purge (LUXCPP path now crypto/corona, not the stale crypto/pulsar); reshare_oracle / activation_oracle / dkg2_oracle accept both CORONA_*_KAT_PATH and PULSAR_*_KAT_PATH env overrides for compatibility with the existing script. Manifest regenerated and verified. gpu/gpu_test.go: skip on non-GPU builds rather than fail (RegisterRing returns "GPU unavailable" without a CGO Metal/CUDA backend; the rest of the audit suite is exercised by the other package tests).
130 lines
3.9 KiB
Go
130 lines
3.9 KiB
Go
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package gpu
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/luxfi/lattice/v7/ring"
|
|
)
|
|
|
|
// TestUseAcceleratorIdempotent — opting in twice is harmless and leaves
|
|
// the flag set.
|
|
func TestUseAcceleratorIdempotent(t *testing.T) {
|
|
t.Cleanup(DisableAccelerator)
|
|
if err := UseAccelerator(); err != nil {
|
|
t.Fatalf("UseAccelerator first call: %v", err)
|
|
}
|
|
if !Enabled() {
|
|
t.Fatal("Enabled() false after first UseAccelerator")
|
|
}
|
|
if err := UseAccelerator(); err != nil {
|
|
t.Fatalf("UseAccelerator second call: %v", err)
|
|
}
|
|
if !Enabled() {
|
|
t.Fatal("Enabled() false after second UseAccelerator")
|
|
}
|
|
}
|
|
|
|
// TestDisableAcceleratorClearsFlag — DisableAccelerator returns the
|
|
// global to its baseline state. Subsequent MaybeRegister calls become
|
|
// no-ops.
|
|
func TestDisableAcceleratorClearsFlag(t *testing.T) {
|
|
t.Cleanup(DisableAccelerator)
|
|
if err := UseAccelerator(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
DisableAccelerator()
|
|
if Enabled() {
|
|
t.Fatal("Enabled() true after DisableAccelerator")
|
|
}
|
|
}
|
|
|
|
// TestRegisterRingIdempotent — calling RegisterRing twice with the same
|
|
// ring does not double-register SubRings (the lattice/gpu registry
|
|
// returns the existing context).
|
|
func TestRegisterRingIdempotent(t *testing.T) {
|
|
t.Cleanup(func() {
|
|
DisableAccelerator()
|
|
})
|
|
if err := UseAccelerator(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r, err := ring.NewRing(256, []uint64{0x1000000004A01})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { UnregisterRing(r) })
|
|
|
|
if err := RegisterRing(r); err != nil {
|
|
// On non-GPU builds (no cgo backend / no Metal / no CUDA) the
|
|
// lattice/gpu RegisterSubRing returns "GPU unavailable". The
|
|
// rest of corona's pure-Go path is fully exercised by the other
|
|
// suites; skip this idempotency check rather than failing.
|
|
t.Skipf("GPU registration unavailable on this build: %v", err)
|
|
}
|
|
beforeStats := CurrentStats()
|
|
if err := RegisterRing(r); err != nil {
|
|
t.Fatalf("RegisterRing second: %v", err)
|
|
}
|
|
afterStats := CurrentStats()
|
|
if beforeStats.RegisteredRings != afterStats.RegisteredRings {
|
|
t.Fatalf("idempotency broken: %d -> %d", beforeStats.RegisteredRings, afterStats.RegisteredRings)
|
|
}
|
|
}
|
|
|
|
// TestMaybeRegisterNoopWhenDisabled — when the accelerator is off,
|
|
// MaybeRegister leaves the SubRing registry untouched.
|
|
func TestMaybeRegisterNoopWhenDisabled(t *testing.T) {
|
|
t.Cleanup(DisableAccelerator)
|
|
DisableAccelerator()
|
|
|
|
r, err := ring.NewRing(256, []uint64{0x1000000004A01})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
before := CurrentStats()
|
|
MaybeRegister(r)
|
|
after := CurrentStats()
|
|
if before.RegisteredRings != after.RegisteredRings {
|
|
t.Fatalf("MaybeRegister mutated registry while disabled: %d -> %d",
|
|
before.RegisteredRings, after.RegisteredRings)
|
|
}
|
|
}
|
|
|
|
// TestUnregisterRing — unbinding restores the pre-register state.
|
|
func TestUnregisterRing(t *testing.T) {
|
|
t.Cleanup(DisableAccelerator)
|
|
if err := UseAccelerator(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r, err := ring.NewRing(256, []uint64{0x1000000004A01})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
before := CurrentStats()
|
|
if err := RegisterRing(r); err != nil {
|
|
// On non-GPU builds the lattice/gpu registry refuses
|
|
// RegisterSubRing with "GPU unavailable". The unregister
|
|
// semantics under that condition are vacuously preserved
|
|
// (no binding was installed); skip rather than failing.
|
|
t.Skipf("GPU registration unavailable on this build: %v", err)
|
|
}
|
|
UnregisterRing(r)
|
|
after := CurrentStats()
|
|
if before.RegisteredRings != after.RegisteredRings {
|
|
t.Fatalf("registry not restored: %d -> %d", before.RegisteredRings, after.RegisteredRings)
|
|
}
|
|
}
|
|
|
|
// TestStatsShape — sanity check that CurrentStats returns a populated
|
|
// struct. Backend string is informational; we only assert it is set
|
|
// (lattice/gpu always provides one even in the pure-Go build).
|
|
func TestStatsShape(t *testing.T) {
|
|
s := CurrentStats()
|
|
if s.Backend == "" {
|
|
t.Fatal("Backend is empty")
|
|
}
|
|
}
|