mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
198 lines
5.3 KiB
Go
198 lines
5.3 KiB
Go
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
|
|
// Tests for the Quasar signing path: BLS + Corona + ML-DSA.
|
|
|
|
package quasar
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestTripleSignBasic creates a signer with threshold=1, adds a validator,
|
|
// signs a message, and verifies that both BLS and MLDSA fields are filled.
|
|
func TestTripleSignBasic(t *testing.T) {
|
|
s, err := NewSigner(1)
|
|
if err != nil {
|
|
t.Fatalf("NewSigner: %v", err)
|
|
}
|
|
|
|
if err := s.AddValidator("v0", 100); err != nil {
|
|
t.Fatalf("AddValidator: %v", err)
|
|
}
|
|
|
|
msg := []byte("triple-sign-basic-test")
|
|
sig, err := s.SignMessage("v0", msg)
|
|
if err != nil {
|
|
t.Fatalf("SignMessage: %v", err)
|
|
}
|
|
|
|
if len(sig.BLS) == 0 {
|
|
t.Fatal("BLS field is empty")
|
|
}
|
|
if len(sig.MLDSA) == 0 {
|
|
t.Fatal("MLDSA field is empty")
|
|
}
|
|
|
|
if !s.VerifyQuasarSig(msg, sig) {
|
|
t.Fatal("VerifyQuasarSig returned false for valid signature")
|
|
}
|
|
}
|
|
|
|
// TestTripleSignVerify creates a signer, signs a message, and independently
|
|
// verifies that both the BLS and ML-DSA paths pass verification.
|
|
func TestTripleSignVerify(t *testing.T) {
|
|
s, err := NewSigner(1)
|
|
if err != nil {
|
|
t.Fatalf("NewSigner: %v", err)
|
|
}
|
|
|
|
if err := s.AddValidator("v0", 100); err != nil {
|
|
t.Fatalf("AddValidator: %v", err)
|
|
}
|
|
|
|
msg := []byte("triple-sign-verify-test")
|
|
sig, err := s.SignMessage("v0", msg)
|
|
if err != nil {
|
|
t.Fatalf("SignMessage: %v", err)
|
|
}
|
|
|
|
// Full verification (BLS + MLDSA)
|
|
if !s.VerifyQuasarSig(msg, sig) {
|
|
t.Fatal("full verification failed")
|
|
}
|
|
|
|
// Verify that a tampered message fails
|
|
bad := []byte("tampered-message")
|
|
if s.VerifyQuasarSig(bad, sig) {
|
|
t.Fatal("verification should fail for tampered message")
|
|
}
|
|
|
|
// Verify BLS alone passes (strip MLDSA)
|
|
blsOnly := &QuasarSig{
|
|
BLS: sig.BLS,
|
|
ValidatorID: sig.ValidatorID,
|
|
IsThreshold: sig.IsThreshold,
|
|
SignerIndex: sig.SignerIndex,
|
|
}
|
|
if !s.VerifyQuasarSig(msg, blsOnly) {
|
|
t.Fatal("BLS-only verification failed")
|
|
}
|
|
|
|
// Verify that corrupted MLDSA fails
|
|
corruptMLDSA := &QuasarSig{
|
|
BLS: sig.BLS,
|
|
MLDSA: []byte("not-a-valid-mldsa-sig"),
|
|
ValidatorID: sig.ValidatorID,
|
|
IsThreshold: sig.IsThreshold,
|
|
SignerIndex: sig.SignerIndex,
|
|
}
|
|
if s.VerifyQuasarSig(msg, corruptMLDSA) {
|
|
t.Fatal("verification should fail with corrupted MLDSA")
|
|
}
|
|
}
|
|
|
|
// TestIsTripleMode verifies that IsTripleMode correctly reflects the
|
|
// presence of all three signing paths: BLS threshold + Corona + ML-DSA.
|
|
func TestIsTripleMode(t *testing.T) {
|
|
// A basic signer with AddValidator has BLS (legacy) + ML-DSA but
|
|
// no threshold BLS signers and no Corona signers.
|
|
basic, err := NewSigner(1)
|
|
if err != nil {
|
|
t.Fatalf("NewSigner: %v", err)
|
|
}
|
|
if err := basic.AddValidator("v0", 100); err != nil {
|
|
t.Fatalf("AddValidator: %v", err)
|
|
}
|
|
if basic.IsTripleMode() {
|
|
t.Fatal("basic signer should not be in triple mode (no threshold BLS, no Corona)")
|
|
}
|
|
|
|
// A dual-threshold signer has BLS threshold + Corona but no ML-DSA.
|
|
config, err := GenerateDualKeys(1, 3)
|
|
if err != nil {
|
|
t.Fatalf("GenerateDualKeys: %v", err)
|
|
}
|
|
dual, err := NewSignerWithDualThreshold(*config)
|
|
if err != nil {
|
|
t.Fatalf("NewSignerWithDualThreshold: %v", err)
|
|
}
|
|
if dual.IsTripleMode() {
|
|
t.Fatal("dual signer should not be in triple mode (no ML-DSA)")
|
|
}
|
|
|
|
// Adding validators with ML-DSA keys to the dual signer should enable
|
|
// triple mode (blsSigners + coronaSigners + mldsaKeys all populated).
|
|
for _, id := range []string{"v0", "v1", "v2"} {
|
|
if err := dual.AddValidator(id, 100); err != nil {
|
|
t.Fatalf("AddValidator(%s): %v", id, err)
|
|
}
|
|
}
|
|
if !dual.IsTripleMode() {
|
|
t.Fatal("signer with BLS threshold + Corona + ML-DSA should be in triple mode")
|
|
}
|
|
}
|
|
|
|
// TestTripleSignRound1 creates a full triple signer (BLS threshold + Corona +
|
|
// ML-DSA) and calls TripleSignRound1 to verify all three fields are produced.
|
|
func TestTripleSignRound1(t *testing.T) {
|
|
config, err := GenerateDualKeys(1, 3)
|
|
if err != nil {
|
|
t.Fatalf("GenerateDualKeys: %v", err)
|
|
}
|
|
|
|
s, err := NewSignerWithDualThreshold(*config)
|
|
if err != nil {
|
|
t.Fatalf("NewSignerWithDualThreshold: %v", err)
|
|
}
|
|
|
|
// Add ML-DSA keys for the validators
|
|
for _, id := range []string{"v0", "v1", "v2"} {
|
|
if err := s.AddValidator(id, 100); err != nil {
|
|
t.Fatalf("AddValidator(%s): %v", id, err)
|
|
}
|
|
}
|
|
|
|
if !s.IsTripleMode() {
|
|
t.Fatal("expected triple mode after adding ML-DSA keys")
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
msg := []byte("triple-round1-test-message")
|
|
sessionID := 42
|
|
prfKey := []byte("test-prf-key-for-triple!!")
|
|
|
|
sig, round1Data, err := s.TripleSignRound1(ctx, "v0", msg, sessionID, prfKey)
|
|
if err != nil {
|
|
t.Fatalf("TripleSignRound1: %v", err)
|
|
}
|
|
|
|
// BLS threshold share must be present
|
|
if len(sig.BLS) == 0 {
|
|
t.Fatal("BLS field is empty after TripleSignRound1")
|
|
}
|
|
|
|
// ML-DSA signature must be present
|
|
if len(sig.MLDSA) == 0 {
|
|
t.Fatal("MLDSA field is empty after TripleSignRound1")
|
|
}
|
|
|
|
// Corona Round1 data must be present (for the 2-round continuation)
|
|
if round1Data == nil {
|
|
t.Fatal("Corona Round1Data is nil")
|
|
}
|
|
if len(round1Data.D) == 0 {
|
|
t.Fatal("Corona Round1Data.D is empty")
|
|
}
|
|
|
|
// Verify the signature is marked as threshold
|
|
if !sig.IsThreshold {
|
|
t.Fatal("signature should be marked as threshold")
|
|
}
|
|
if sig.ValidatorID != "v0" {
|
|
t.Fatalf("expected ValidatorID v0, got %s", sig.ValidatorID)
|
|
}
|
|
}
|