mirror of
https://github.com/luxfi/lens.git
synced 2026-10-30 16:21:16 +00:00
Lens is the curve-based threshold signing kernel for Lux. It is the classical analogue of Pulsar (the lattice kernel at github.com/luxfi/pulsar). Both kernels share the same lifecycle shape — Bootstrap → Reshare* → Reanchor — and the same activation circuit-breaker contract. They differ only in the math field: Pulsar over R_q (Module-LWE), Lens over a prime-order curve group (discrete log). Curves shipped: Ed25519, secp256k1, Ristretto255. Each has a deterministically-derived secondary Pedersen generator H from a Lens-specific nothing-up-my-sleeve tag. Sign math: FROST 2-round Schnorr threshold signing per RFC 9591 (Komlo-Goldberg 2020). Verifier checks z·G ?= R + c·X under the unchanged group public key. Reshare math: HJKY97 zero-poly Refresh + Desmedt-Jajodia ReshareToNewSet over the curve scalar field. Master secret s is preserved; only the share distribution rotates per epoch. Activation cert message format: QUASAR-LENS-ACTIVATE-v1, byte-equivalent shape to QUASAR-PULSAR-ACTIVATE-v1. Hash suite: HashSuite interface with Lens-SHA3 (production, cSHAKE256 / KMAC256 / TupleHash256) and Lens-BLAKE3 (legacy) profiles. DKG: distributed FROST DKG with Feldman commitments. No trusted dealer required at genesis if the chain prefers proper distributed DKG. KAT oracles: cmd/reshare_oracle and cmd/dkg_oracle emit deterministic JSON vectors for cross-language byte-equality validation against a future luxcpp/crypto/lens/ port. Tests: full unit + integration coverage on every shipped curve. 60 unique tests, 0 failures.
77 lines
2.3 KiB
Go
77 lines
2.3 KiB
Go
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package reshare
|
|
|
|
import "testing"
|
|
|
|
// TestActivationMessageSignableBytesStable — same inputs → same bytes.
|
|
func TestActivationMessageSignableBytesStable(t *testing.T) {
|
|
msg := &ActivationMessage{
|
|
Transcript: TranscriptInputs{
|
|
ChainID: []byte("test"), GroupID: []byte("g"), Variant: "reshare",
|
|
ThresholdOld: 3, ThresholdNew: 3,
|
|
},
|
|
ReshareTranscript: ReshareTranscript{
|
|
CommitDigests: map[int][32]byte{1: {0x01}, 2: {0x02}},
|
|
QualifiedQuorum: []int{1, 2, 3},
|
|
},
|
|
}
|
|
a := msg.SignableBytes(nil)
|
|
b := msg.SignableBytes(nil)
|
|
if string(a) != string(b) {
|
|
t.Fatal("SignableBytes not deterministic")
|
|
}
|
|
}
|
|
|
|
// TestVerifyActivationGood/Bad covers the chain-side circuit-breaker.
|
|
func TestVerifyActivationCircuitBreaker(t *testing.T) {
|
|
transcriptInputs := TranscriptInputs{
|
|
ChainID: []byte("test"), GroupID: []byte("g"), Variant: "reshare",
|
|
ThresholdOld: 3, ThresholdNew: 3,
|
|
}
|
|
exchange := ReshareTranscript{
|
|
QualifiedQuorum: []int{1, 2, 3},
|
|
}
|
|
cert := &ActivationCert{
|
|
Message: ActivationMessage{
|
|
Transcript: transcriptInputs,
|
|
ReshareTranscript: exchange,
|
|
},
|
|
Signature: []byte("sigblob"),
|
|
}
|
|
good := func(_, _ []byte) bool { return true }
|
|
bad := func(_, _ []byte) bool { return false }
|
|
|
|
tHash := transcriptInputs.Hash(nil)
|
|
xHash := exchange.Hash(nil)
|
|
|
|
if err := VerifyActivation(cert, tHash, xHash, nil, good); err != nil {
|
|
t.Errorf("good verify failed: %v", err)
|
|
}
|
|
if err := VerifyActivation(cert, tHash, xHash, nil, bad); err == nil {
|
|
t.Error("bad verify should have failed")
|
|
}
|
|
|
|
// Mismatched transcript hash → ErrTranscriptMismatch.
|
|
wrong := [32]byte{0xFF}
|
|
if err := VerifyActivation(cert, wrong, xHash, nil, good); err == nil {
|
|
t.Error("transcript mismatch should fail")
|
|
}
|
|
}
|
|
|
|
// TestReshareTranscriptHashStable — deterministic exchange digest.
|
|
func TestReshareTranscriptHashStable(t *testing.T) {
|
|
rt := ReshareTranscript{
|
|
CommitDigests: map[int][32]byte{1: {0x01}, 2: {0x02}, 3: {0x03}},
|
|
ComplaintHashes: [][32]byte{{0xAA}, {0xBB}},
|
|
DisqualifiedSenders: []int{2},
|
|
QualifiedQuorum: []int{1, 3},
|
|
}
|
|
a := rt.Hash(nil)
|
|
b := rt.Hash(nil)
|
|
if a != b {
|
|
t.Fatal("ReshareTranscript.Hash not deterministic")
|
|
}
|
|
}
|