Files
consensus/protocol/quasar/quasar_bench_test.go
zeekay b4bb89df97 quasar: complete Ringtail purge — Corona (production R-LWE) + Nasua (academic upstream)
Followup to the QuasarCert field rename (commit 9b9bdda2). This sweeps
the remaining Ringtail identifiers and prose out of the consensus repo
entirely:

  - Identifiers (Go code):
    ringtailThreshold   → coronaThreshold      (import alias)
    ringtailGroupKey    → coronaGroupKey
    ringtailSigners     → coronaSigners
    ringtailShares      → coronaShares
    ringtailGobEncode   → coronaGobEncode
    ringtailGobDecode   → coronaGobDecode
    decodeRingtailSig   → decodeCoronaSig
    EncodeRingtailSig   → EncodeCoronaSig
    RingtailRound1{,Data,State} → CoronaRound1{,Data,State}
    RingtailGroupKey/Pub/Shares → Corona*

  - File rename:
    protocol/quasar/ringtail_gob.go → corona_gob.go

  - PQMode constants:
    PQModeRingtail            → PQModeNasua
    SigSchemeRingtailAcademic → SigSchemeNasua
    "ringtail" / "ringtail-academic" string forms → "nasua"

  - Prose & comments throughout the repo: "Ringtail" → "Corona" when
    naming the production Ring-LWE library, "Nasua" when naming the
    historical academic upstream (luxfi/nasua, formerly luxfi/ringtail).

Canonical Pulsar family taxonomy after this rename:

  Nasua  — academic R-LWE 2-round threshold sig (Boschini et al,
           ePrint 2024/1113); trusted-dealer DKG; kept as historical
           reference fork only. NOT for an open public chain.
  Corona — production Ring-LWE threshold ML-DSA (Pedersen DKG over R_q
           + proactive resharing); `github.com/luxfi/corona`. The
           Ring-LWE leg of QuasarCert.
  Pulsar — production Module-LWE threshold ML-DSA (FIPS 204
           byte-equal); `github.com/luxfi/pulsar`. The Module-LWE
           leg of QuasarCert. Pure-PQ pair = Corona + Pulsar.

Wire-format unchanged. Tests: 28/28 packages green (-short suite,
quasar 29s, integration 58s, no regressions).
2026-05-12 08:58:51 -07:00

747 lines
16 KiB
Go

// Copyright (C) 2025, Lux Industries Inc All rights reserved.
// Benchmarks for Quasar post-quantum consensus
package quasar
import (
"crypto/rand"
"fmt"
"testing"
"time"
"github.com/luxfi/crypto/bls"
"github.com/luxfi/crypto/mldsa"
)
// =============================================================================
// BLS Signing and Verification Benchmarks
// =============================================================================
func BenchmarkBLSKeyGeneration(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := bls.NewSecretKey()
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkBLSSigning(b *testing.B) {
sk, err := bls.NewSecretKey()
if err != nil {
b.Fatal(err)
}
message := make([]byte, 32)
rand.Read(message)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := sk.Sign(message)
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkBLSVerification(b *testing.B) {
sk, err := bls.NewSecretKey()
if err != nil {
b.Fatal(err)
}
pk := sk.PublicKey()
message := make([]byte, 32)
rand.Read(message)
sig, err := sk.Sign(message)
if err != nil {
b.Fatal(err)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if !bls.Verify(pk, sig, message) {
b.Fatal("verification failed")
}
}
}
func BenchmarkBLSSigningVariableMessageSize(b *testing.B) {
sizes := []int{32, 64, 128, 256, 512, 1024, 4096}
sk, err := bls.NewSecretKey()
if err != nil {
b.Fatal(err)
}
for _, size := range sizes {
b.Run(fmt.Sprintf("msg_%d_bytes", size), func(b *testing.B) {
message := make([]byte, size)
rand.Read(message)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := sk.Sign(message)
if err != nil {
b.Fatal(err)
}
}
})
}
}
// =============================================================================
// Hybrid Signature Benchmarks (BLS + ML-DSA/Corona)
// =============================================================================
func BenchmarkQuasarSigCreation(b *testing.B) {
hybrid, err := NewSigner(1)
if err != nil {
b.Fatal(err)
}
if err := hybrid.AddValidator("bench-validator", 100); err != nil {
b.Fatal(err)
}
message := make([]byte, 32)
rand.Read(message)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := hybrid.SignMessage("bench-validator", message)
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkQuasarSigVerification(b *testing.B) {
hybrid, err := NewSigner(1)
if err != nil {
b.Fatal(err)
}
if err := hybrid.AddValidator("bench-validator", 100); err != nil {
b.Fatal(err)
}
message := make([]byte, 32)
rand.Read(message)
sig, err := hybrid.SignMessage("bench-validator", message)
if err != nil {
b.Fatal(err)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if !hybrid.VerifyQuasarSig(message, sig) {
b.Fatal("verification failed")
}
}
}
func BenchmarkQuasarSigVariableMessageSize(b *testing.B) {
sizes := []int{32, 64, 128, 256, 512, 1024}
hybrid, err := NewSigner(1)
if err != nil {
b.Fatal(err)
}
if err := hybrid.AddValidator("bench-validator", 100); err != nil {
b.Fatal(err)
}
for _, size := range sizes {
b.Run(fmt.Sprintf("sign_msg_%d_bytes", size), func(b *testing.B) {
message := make([]byte, size)
rand.Read(message)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := hybrid.SignMessage("bench-validator", message)
if err != nil {
b.Fatal(err)
}
}
})
b.Run(fmt.Sprintf("verify_msg_%d_bytes", size), func(b *testing.B) {
message := make([]byte, size)
rand.Read(message)
sig, err := hybrid.SignMessage("bench-validator", message)
if err != nil {
b.Fatal(err)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if !hybrid.VerifyQuasarSig(message, sig) {
b.Fatal("verification failed")
}
}
})
}
}
// =============================================================================
// Signature Aggregation Benchmarks
// =============================================================================
func BenchmarkBLSAggregation(b *testing.B) {
counts := []int{4, 8, 16, 32, 64, 100}
for _, count := range counts {
b.Run(fmt.Sprintf("%d_signatures", count), func(b *testing.B) {
// Generate keys and signatures
message := make([]byte, 32)
rand.Read(message)
sigs := make([]*bls.Signature, count)
for i := 0; i < count; i++ {
sk, err := bls.NewSecretKey()
if err != nil {
b.Fatal(err)
}
sig, err := sk.Sign(message)
if err != nil {
b.Fatal(err)
}
sigs[i] = sig
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := bls.AggregateSignatures(sigs)
if err != nil {
b.Fatal(err)
}
}
})
}
}
func BenchmarkBLSAggregatedVerification(b *testing.B) {
counts := []int{4, 8, 16, 32, 64, 100}
for _, count := range counts {
b.Run(fmt.Sprintf("%d_signers", count), func(b *testing.B) {
message := make([]byte, 32)
rand.Read(message)
sigs := make([]*bls.Signature, count)
pks := make([]*bls.PublicKey, count)
for i := 0; i < count; i++ {
sk, err := bls.NewSecretKey()
if err != nil {
b.Fatal(err)
}
pks[i] = sk.PublicKey()
sig, err := sk.Sign(message)
if err != nil {
b.Fatal(err)
}
sigs[i] = sig
}
aggSig, err := bls.AggregateSignatures(sigs)
if err != nil {
b.Fatal(err)
}
aggPK, err := bls.AggregatePublicKeys(pks)
if err != nil {
b.Fatal(err)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if !bls.Verify(aggPK, aggSig, message) {
b.Fatal("verification failed")
}
}
})
}
}
func BenchmarkSignerAggregation(b *testing.B) {
counts := []int{4, 8, 16, 32}
for _, count := range counts {
b.Run(fmt.Sprintf("%d_validators", count), func(b *testing.B) {
hybrid, err := NewSigner(count)
if err != nil {
b.Fatal(err)
}
message := make([]byte, 32)
rand.Read(message)
// Add validators and collect signatures
sigs := make([]*QuasarSig, count)
for i := 0; i < count; i++ {
id := fmt.Sprintf("validator-%d", i)
if err := hybrid.AddValidator(id, 100); err != nil {
b.Fatal(err)
}
sig, err := hybrid.SignMessage(id, message)
if err != nil {
b.Fatal(err)
}
sigs[i] = sig
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := hybrid.AggregateSignatures(message, sigs)
if err != nil {
b.Fatal(err)
}
}
})
}
}
func BenchmarkSignerAggregatedVerification(b *testing.B) {
counts := []int{4, 8, 16, 32}
for _, count := range counts {
b.Run(fmt.Sprintf("%d_validators", count), func(b *testing.B) {
hybrid, err := NewSigner(count)
if err != nil {
b.Fatal(err)
}
message := make([]byte, 32)
rand.Read(message)
// Add validators and collect signatures
sigs := make([]*QuasarSig, count)
for i := 0; i < count; i++ {
id := fmt.Sprintf("validator-%d", i)
if err := hybrid.AddValidator(id, 100); err != nil {
b.Fatal(err)
}
sig, err := hybrid.SignMessage(id, message)
if err != nil {
b.Fatal(err)
}
sigs[i] = sig
}
aggSig, err := hybrid.AggregateSignatures(message, sigs)
if err != nil {
b.Fatal(err)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if !hybrid.VerifyAggregatedSignature(message, aggSig) {
b.Fatal("verification failed")
}
}
})
}
}
// =============================================================================
// Threshold Signing Benchmarks
// =============================================================================
func BenchmarkThresholdRound(b *testing.B) {
// Benchmark a complete threshold signing round
thresholds := []struct {
n int // total validators
t int // threshold
}{
{4, 3},
{10, 7},
{20, 14},
{50, 34},
{100, 67},
}
for _, tc := range thresholds {
b.Run(fmt.Sprintf("n%d_t%d", tc.n, tc.t), func(b *testing.B) {
hybrid, err := NewSigner(tc.t)
if err != nil {
b.Fatal(err)
}
message := make([]byte, 32)
rand.Read(message)
// Add all validators
ids := make([]string, tc.n)
for i := 0; i < tc.n; i++ {
id := fmt.Sprintf("validator-%d", i)
ids[i] = id
if err := hybrid.AddValidator(id, 100); err != nil {
b.Fatal(err)
}
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
// Collect threshold signatures (simulate a round)
sigs := make([]*QuasarSig, tc.t)
for j := 0; j < tc.t; j++ {
sig, err := hybrid.SignMessage(ids[j], message)
if err != nil {
b.Fatal(err)
}
sigs[j] = sig
}
// Aggregate
aggSig, err := hybrid.AggregateSignatures(message, sigs)
if err != nil {
b.Fatal(err)
}
// Verify
if !hybrid.VerifyAggregatedSignature(message, aggSig) {
b.Fatal("verification failed")
}
}
})
}
}
func BenchmarkThresholdSigningOnly(b *testing.B) {
// Benchmark just the signing phase (parallel in production)
thresholds := []int{4, 8, 16, 32, 64}
for _, t := range thresholds {
b.Run(fmt.Sprintf("%d_signers", t), func(b *testing.B) {
hybrid, err := NewSigner(t)
if err != nil {
b.Fatal(err)
}
message := make([]byte, 32)
rand.Read(message)
// Add validators
ids := make([]string, t)
for i := 0; i < t; i++ {
id := fmt.Sprintf("validator-%d", i)
ids[i] = id
if err := hybrid.AddValidator(id, 100); err != nil {
b.Fatal(err)
}
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for j := 0; j < t; j++ {
_, err := hybrid.SignMessage(ids[j], message)
if err != nil {
b.Fatal(err)
}
}
}
})
}
}
// =============================================================================
// ML-DSA (Corona Post-Quantum) Benchmarks
// =============================================================================
func BenchmarkMLDSAKeyGeneration(b *testing.B) {
modes := []mldsa.Mode{mldsa.MLDSA44, mldsa.MLDSA65, mldsa.MLDSA87}
names := []string{"MLDSA44_L2", "MLDSA65_L3", "MLDSA87_L5"}
for i, mode := range modes {
b.Run(names[i], func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for j := 0; j < b.N; j++ {
_, err := mldsa.GenerateKey(rand.Reader, mode)
if err != nil {
b.Fatal(err)
}
}
})
}
}
func BenchmarkMLDSASigning(b *testing.B) {
modes := []mldsa.Mode{mldsa.MLDSA44, mldsa.MLDSA65, mldsa.MLDSA87}
names := []string{"MLDSA44_L2", "MLDSA65_L3", "MLDSA87_L5"}
for i, mode := range modes {
b.Run(names[i], func(b *testing.B) {
sk, err := mldsa.GenerateKey(rand.Reader, mode)
if err != nil {
b.Fatal(err)
}
message := make([]byte, 32)
rand.Read(message)
b.ReportAllocs()
b.ResetTimer()
for j := 0; j < b.N; j++ {
_, err := sk.Sign(rand.Reader, message, nil)
if err != nil {
b.Fatal(err)
}
}
})
}
}
func BenchmarkMLDSAVerification(b *testing.B) {
modes := []mldsa.Mode{mldsa.MLDSA44, mldsa.MLDSA65, mldsa.MLDSA87}
names := []string{"MLDSA44_L2", "MLDSA65_L3", "MLDSA87_L5"}
for i, mode := range modes {
b.Run(names[i], func(b *testing.B) {
sk, err := mldsa.GenerateKey(rand.Reader, mode)
if err != nil {
b.Fatal(err)
}
message := make([]byte, 32)
rand.Read(message)
sig, err := sk.Sign(rand.Reader, message, nil)
if err != nil {
b.Fatal(err)
}
pk := sk.PublicKey
b.ReportAllocs()
b.ResetTimer()
for j := 0; j < b.N; j++ {
if !pk.Verify(message, sig, nil) {
b.Fatal("verification failed")
}
}
})
}
}
// =============================================================================
// Corona Benchmarks (Reference)
// =============================================================================
// NOTE: Post-quantum threshold benchmarks live in the Pulsar package at
// github.com/luxfi/corona/threshold. The quasar package uses that real
// implementation via the Signer type in quasar.go.
// =============================================================================
// End-to-End Quasar Benchmarks
// =============================================================================
func BenchmarkQuasarBlockProcessing(b *testing.B) {
qa, err := NewTestQuasar(1)
if err != nil {
b.Fatal(err)
}
// Need at least 2 validators for epoch manager
if _, err := qa.AddValidator("validator1", 100); err != nil {
b.Fatal(err)
}
if _, err := qa.AddValidator("validator2", 100); err != nil {
b.Fatal(err)
}
block := &ChainBlock{
ChainID: [32]byte{1},
ChainName: "P-Chain",
ID: [32]byte{0x01, 0x02, 0x03},
Height: 100,
Timestamp: time.Now(),
Data: make([]byte, 1024),
}
rand.Read(block.Data)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
block.Height = uint64(i)
qa.processBlock(block)
}
}
func BenchmarkQuasarQuantumHash(b *testing.B) {
qa, err := NewTestQuasar(1)
if err != nil {
b.Fatal(err)
}
block := &Block{
ChainID: [32]byte{1},
ChainName: "P-Chain",
ID: [32]byte{0x01, 0x02, 0x03},
Height: 100,
Timestamp: time.Now(),
Data: make([]byte, 1024),
}
rand.Read(block.Data)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = qa.computeQuantumHash(block)
}
}
func BenchmarkQuasarValidatorAddition(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
b.StopTimer()
hybrid, err := NewSigner(1)
if err != nil {
b.Fatal(err)
}
b.StartTimer()
err = hybrid.AddValidator(fmt.Sprintf("validator-%d", i), 100)
if err != nil {
b.Fatal(err)
}
}
}
// =============================================================================
// Comparison Benchmarks (BLS vs ML-DSA)
// =============================================================================
func BenchmarkComparisonSigning(b *testing.B) {
message := make([]byte, 32)
rand.Read(message)
b.Run("BLS", func(b *testing.B) {
sk, err := bls.NewSecretKey()
if err != nil {
b.Fatal(err)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := sk.Sign(message)
if err != nil {
b.Fatal(err)
}
}
})
b.Run("MLDSA65", func(b *testing.B) {
sk, err := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
if err != nil {
b.Fatal(err)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := sk.Sign(rand.Reader, message, nil)
if err != nil {
b.Fatal(err)
}
}
})
}
func BenchmarkComparisonVerification(b *testing.B) {
message := make([]byte, 32)
rand.Read(message)
b.Run("BLS", func(b *testing.B) {
sk, err := bls.NewSecretKey()
if err != nil {
b.Fatal(err)
}
pk := sk.PublicKey()
sig, err := sk.Sign(message)
if err != nil {
b.Fatal(err)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if !bls.Verify(pk, sig, message) {
b.Fatal("verification failed")
}
}
})
b.Run("MLDSA65", func(b *testing.B) {
sk, err := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
if err != nil {
b.Fatal(err)
}
pk := sk.PublicKey
sig, err := sk.Sign(rand.Reader, message, nil)
if err != nil {
b.Fatal(err)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if !pk.Verify(message, sig, nil) {
b.Fatal("verification failed")
}
}
})
}
func BenchmarkComparisonKeyGen(b *testing.B) {
b.Run("BLS", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := bls.NewSecretKey()
if err != nil {
b.Fatal(err)
}
}
})
b.Run("MLDSA44", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA44)
if err != nil {
b.Fatal(err)
}
}
})
b.Run("MLDSA65", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
if err != nil {
b.Fatal(err)
}
}
})
b.Run("MLDSA87", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA87)
if err != nil {
b.Fatal(err)
}
}
})
}