mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
136 lines
4.1 KiB
Go
136 lines
4.1 KiB
Go
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
|
|
|
|
package quasar
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/luxfi/crypto/bls"
|
|
)
|
|
|
|
// TestQuasarCert_RoundTrip_E2E exercises the full path:
|
|
//
|
|
// 1. Generate a real triple signature via TripleSignRound1.
|
|
// 2. Build a QuasarCert from the produced share + ML-DSA sig.
|
|
// 3. Marshal to bytes, unmarshal into a fresh QuasarCert.
|
|
// 4. Verify with the real signer's keys (must succeed).
|
|
// 5. Verify with a different BLS aggregate key (must fail).
|
|
func TestQuasarCert_RoundTrip_E2E(t *testing.T) {
|
|
// 1-of-1 BLS+Corona dual signer + ML-DSA via AddValidator.
|
|
cfg, err := GenerateDualKeys(1, 3)
|
|
if err != nil {
|
|
t.Fatalf("GenerateDualKeys: %v", err)
|
|
}
|
|
|
|
s, err := NewSignerWithDualThreshold(*cfg)
|
|
if err != nil {
|
|
t.Fatalf("NewSignerWithDualThreshold: %v", err)
|
|
}
|
|
|
|
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.Fatalf("expected triple mode after configuring all three paths")
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
msg := []byte("e2e-roundtrip-message")
|
|
sig, _, err := s.TripleSignRound1(ctx, "v0", msg, 1, []byte("prf-key-32-bytes-for-the-test!!"))
|
|
if err != nil {
|
|
t.Fatalf("TripleSignRound1: %v", err)
|
|
}
|
|
if len(sig.BLS) == 0 || len(sig.MLDSA) == 0 {
|
|
t.Fatalf("expected BLS and MLDSA filled; got BLS=%d MLDSA=%d", len(sig.BLS), len(sig.MLDSA))
|
|
}
|
|
|
|
// 2. Build QuasarCert. We embed the BLS share as the BLS field for
|
|
// end-to-end byte exercise; full aggregation is a higher-layer concern.
|
|
cert := &QuasarCert{
|
|
BLS: append([]byte(nil), sig.BLS...),
|
|
Corona: []byte{0x01}, // structural-only; cryptographic Corona is via coronaGobEncode of a real Signature
|
|
MLDSARollup: EncodeMLDSASigs([][]byte{sig.MLDSA}),
|
|
Epoch: 1,
|
|
Finality: time.Unix(1700000000, 0),
|
|
Validators: 3,
|
|
}
|
|
|
|
// 3. Marshal / unmarshal round-trip.
|
|
raw, err := cert.MarshalBinary()
|
|
if err != nil {
|
|
t.Fatalf("MarshalBinary: %v", err)
|
|
}
|
|
if len(raw) == 0 {
|
|
t.Fatal("MarshalBinary returned empty bytes")
|
|
}
|
|
if raw[0] != CertSchemeQuasar {
|
|
t.Fatalf("expected scheme byte %x, got %x", CertSchemeQuasar, raw[0])
|
|
}
|
|
|
|
got := &QuasarCert{}
|
|
if err := got.UnmarshalBinary(raw); err != nil {
|
|
t.Fatalf("UnmarshalBinary: %v", err)
|
|
}
|
|
if string(got.BLS) != string(cert.BLS) {
|
|
t.Fatalf("BLS round-trip mismatch")
|
|
}
|
|
if string(got.Corona) != string(cert.Corona) {
|
|
t.Fatalf("Corona round-trip mismatch")
|
|
}
|
|
if string(got.MLDSARollup) != string(cert.MLDSARollup) {
|
|
t.Fatalf("MLDSAProof round-trip mismatch")
|
|
}
|
|
if got.Epoch != cert.Epoch {
|
|
t.Fatalf("Epoch mismatch: %d vs %d", got.Epoch, cert.Epoch)
|
|
}
|
|
if got.Finality.Unix() != cert.Finality.Unix() {
|
|
t.Fatalf("Finality mismatch")
|
|
}
|
|
if got.Validators != cert.Validators {
|
|
t.Fatalf("Validators mismatch: %d vs %d", got.Validators, cert.Validators)
|
|
}
|
|
|
|
// 4-5. VerifyWithRealKeys with a wrong BLS aggregate key must fail.
|
|
wrongSK, err := bls.NewSecretKey()
|
|
if err != nil {
|
|
t.Fatalf("bls.NewSecretKey: %v", err)
|
|
}
|
|
wrongPK := wrongSK.PublicKey()
|
|
|
|
if got.VerifyWithRealKeys(msg, wrongPK, nil, nil) {
|
|
t.Fatal("VerifyWithRealKeys must fail with wrong BLS aggregate key")
|
|
}
|
|
|
|
// Also: corrupt the BLS bytes; verification must fail.
|
|
tampered := *got
|
|
tampered.BLS = append([]byte(nil), got.BLS...)
|
|
tampered.BLS[0] ^= 0xFF
|
|
if tampered.VerifyWithRealKeys(msg, wrongPK, nil, nil) {
|
|
t.Fatal("VerifyWithRealKeys must fail with tampered BLS bytes")
|
|
}
|
|
}
|
|
|
|
// TestQuasarCert_UnmarshalCorrupt covers truncated and wrong-scheme inputs.
|
|
func TestQuasarCert_UnmarshalCorrupt(t *testing.T) {
|
|
cases := [][]byte{
|
|
nil,
|
|
{},
|
|
{0x00}, // wrong scheme
|
|
{CertSchemeQuasar}, // truncated header
|
|
{CertSchemeQuasar, 0x00, 0xFF}, // BLS length exceeds buffer
|
|
append([]byte{CertSchemeQuasar}, make([]byte, 4)...), // still truncated
|
|
}
|
|
for i, c := range cases {
|
|
got := &QuasarCert{}
|
|
if err := got.UnmarshalBinary(c); err == nil {
|
|
t.Fatalf("case %d: expected error for corrupt input %x", i, c)
|
|
}
|
|
}
|
|
}
|