mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
corona v0.7.9 (commit 7c102d2, a descendant of the old single-return 4d9334ee) re-added the error return on Signer.Round1 as kernel-boundary hardening on SignRound1. consensus quasar still called it single-value (with now-stale 'error return was removed upstream' comments), so node v1.30.40 failed to compile: 'assignment mismatch: 1 variable but Round1 returns 2 values' at epoch.go:679/1052, grouped_threshold.go:510, quasar.go:269. Thread the error through all four production sites (enclosing funcs already return error) and the 11 quasar test sites. Forward-only fix; corona v0.7.9 is the intended current API.
267 lines
9.7 KiB
Go
267 lines
9.7 KiB
Go
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
|
|
// Test BLS threshold signing for quantum-safe consensus.
|
|
// Note: Full Corona integration requires multi-round protocol coordination.
|
|
|
|
package quasar
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/luxfi/crypto/threshold"
|
|
coronaThreshold "github.com/luxfi/threshold/protocols/corona"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestBLSThresholdSigningFlow tests BLS threshold signing via the Quasar signer.
|
|
func TestBLSThresholdSigningFlow(t *testing.T) {
|
|
// Generate BLS threshold keys for 2-of-3 threshold
|
|
shares, groupKey, err := GenerateThresholdKeys(threshold.SchemeBLS, 2, 3)
|
|
require.NoError(t, err, "Failed to generate BLS threshold keys")
|
|
|
|
require.Len(t, shares, 3, "Should have 3 key shares")
|
|
require.NotNil(t, groupKey, "Group key should not be nil")
|
|
|
|
t.Logf("Generated BLS threshold keys: t=2, n=3")
|
|
t.Logf("Group key size: %d bytes", len(groupKey.Bytes()))
|
|
|
|
// Create key shares map
|
|
keyShares := make(map[string]threshold.KeyShare)
|
|
keyShares["v0"] = shares[0]
|
|
keyShares["v1"] = shares[1]
|
|
keyShares["v2"] = shares[2]
|
|
|
|
// Create Quasar signer with BLS threshold signing
|
|
config := ThresholdConfig{
|
|
SchemeID: threshold.SchemeBLS,
|
|
Threshold: 2,
|
|
TotalParties: 3,
|
|
KeyShares: keyShares,
|
|
GroupKey: groupKey,
|
|
}
|
|
|
|
h, err := NewSignerWithThresholdConfig(config)
|
|
require.NoError(t, err, "Failed to create Quasar signer")
|
|
|
|
// Verify threshold mode is enabled
|
|
require.True(t, h.IsThresholdMode(), "Should be in threshold mode")
|
|
|
|
// Sign a message with 2 validators (meeting 2-of-3 threshold)
|
|
ctx := context.Background()
|
|
message := []byte("consensus block for quantum security")
|
|
|
|
// Get signature shares from validators
|
|
share0, err := h.SignMessageThreshold(ctx, "v0", message)
|
|
require.NoError(t, err, "Validator 0 signing failed")
|
|
require.NotNil(t, share0, "Signature share should not be nil")
|
|
|
|
share1, err := h.SignMessageThreshold(ctx, "v1", message)
|
|
require.NoError(t, err, "Validator 1 signing failed")
|
|
|
|
t.Logf("Share 0: %d bytes", len(share0.Bytes()))
|
|
t.Logf("Share 1: %d bytes", len(share1.Bytes()))
|
|
|
|
// Aggregate signatures (need t+1=3 shares for 2-of-3, but API expects >threshold)
|
|
// Getting a third share
|
|
share2, err := h.SignMessageThreshold(ctx, "v2", message)
|
|
require.NoError(t, err, "Validator 2 signing failed")
|
|
|
|
aggSig, err := h.AggregateThresholdSignatures(ctx, message, []threshold.SignatureShare{share0, share1, share2})
|
|
require.NoError(t, err, "Signature aggregation failed")
|
|
require.NotNil(t, aggSig, "Aggregated signature should not be nil")
|
|
|
|
t.Logf("Aggregated signature: %d bytes", len(aggSig.Bytes()))
|
|
|
|
// Verify the aggregated signature
|
|
valid := h.VerifyThresholdSignature(message, aggSig)
|
|
require.True(t, valid, "Aggregated signature should verify")
|
|
|
|
// Also verify via bytes
|
|
validBytes := h.VerifyThresholdSignatureBytes(message, aggSig.Bytes())
|
|
require.True(t, validBytes, "Serialized signature should verify")
|
|
|
|
t.Log("✓ BLS threshold signing flow complete")
|
|
}
|
|
|
|
// TestBLSThresholdInsufficientShares tests that aggregation fails without enough shares.
|
|
func TestBLSThresholdInsufficientShares(t *testing.T) {
|
|
shares, groupKey, err := GenerateThresholdKeys(threshold.SchemeBLS, 2, 3)
|
|
require.NoError(t, err)
|
|
|
|
keyShares := make(map[string]threshold.KeyShare)
|
|
keyShares["v0"] = shares[0]
|
|
keyShares["v1"] = shares[1]
|
|
keyShares["v2"] = shares[2]
|
|
|
|
h, err := NewSignerWithThresholdConfig(ThresholdConfig{
|
|
SchemeID: threshold.SchemeBLS,
|
|
Threshold: 2,
|
|
TotalParties: 3,
|
|
KeyShares: keyShares,
|
|
GroupKey: groupKey,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
ctx := context.Background()
|
|
message := []byte("need more signatures")
|
|
|
|
// Sign with only 2 validators (need 3 for t+1 with threshold=2)
|
|
share0, _ := h.SignMessageThreshold(ctx, "v0", message)
|
|
share1, _ := h.SignMessageThreshold(ctx, "v1", message)
|
|
|
|
// Aggregation should fail with insufficient signatures
|
|
_, err = h.AggregateThresholdSignatures(ctx, message, []threshold.SignatureShare{share0, share1})
|
|
require.Error(t, err, "Should fail with insufficient signatures")
|
|
}
|
|
|
|
// TestBLSThresholdWrongMessage tests that verification fails for wrong message.
|
|
func TestBLSThresholdWrongMessage(t *testing.T) {
|
|
shares, groupKey, err := GenerateThresholdKeys(threshold.SchemeBLS, 2, 3)
|
|
require.NoError(t, err)
|
|
|
|
keyShares := make(map[string]threshold.KeyShare)
|
|
keyShares["v0"] = shares[0]
|
|
keyShares["v1"] = shares[1]
|
|
keyShares["v2"] = shares[2]
|
|
|
|
h, err := NewSignerWithThresholdConfig(ThresholdConfig{
|
|
SchemeID: threshold.SchemeBLS,
|
|
Threshold: 2,
|
|
TotalParties: 3,
|
|
KeyShares: keyShares,
|
|
GroupKey: groupKey,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
ctx := context.Background()
|
|
message := []byte("original message")
|
|
wrongMessage := []byte("tampered message")
|
|
|
|
share0, _ := h.SignMessageThreshold(ctx, "v0", message)
|
|
share1, _ := h.SignMessageThreshold(ctx, "v1", message)
|
|
share2, _ := h.SignMessageThreshold(ctx, "v2", message)
|
|
|
|
aggSig, err := h.AggregateThresholdSignatures(ctx, message, []threshold.SignatureShare{share0, share1, share2})
|
|
require.NoError(t, err)
|
|
|
|
// Verify with wrong message should fail
|
|
valid := h.VerifyThresholdSignature(wrongMessage, aggSig)
|
|
require.False(t, valid, "Verification should fail for wrong message")
|
|
}
|
|
|
|
// TestDualThresholdKeyGeneration tests that dual key generation works.
|
|
func TestDualThresholdKeyGeneration(t *testing.T) {
|
|
config, err := GenerateDualThresholdKeys(2, 3)
|
|
require.NoError(t, err, "Failed to generate dual threshold keys")
|
|
|
|
// Verify both BLS and Corona keys were generated
|
|
require.NotNil(t, config.BLSGroupKey, "BLS group key should not be nil")
|
|
require.NotNil(t, config.CoronaGroupKey, "Corona group key should not be nil")
|
|
require.Len(t, config.BLSKeyShares, 3, "Should have 3 BLS key shares")
|
|
require.Len(t, config.CoronaShares, 3, "Should have 3 Corona key shares")
|
|
|
|
t.Logf("BLS group key: %d bytes", len(config.BLSGroupKey.Bytes()))
|
|
t.Logf("Corona group key: %d bytes", len(config.CoronaGroupKey.Bytes()))
|
|
|
|
t.Log("✓ Dual threshold key generation works")
|
|
}
|
|
|
|
// TestDualSigningFlow tests the full BLS + Corona parallel signing flow.
|
|
// This is how validators sign blocks in quantum-safe consensus.
|
|
func TestDualSigningFlow(t *testing.T) {
|
|
// Generate dual keys (would be done at epoch start in production)
|
|
config, err := GenerateDualKeys(2, 3)
|
|
require.NoError(t, err, "Failed to generate dual keys")
|
|
|
|
// Create Quasar signer with dual threshold signing
|
|
h, err := NewSignerWithDualThreshold(*config)
|
|
require.NoError(t, err, "Failed to create Quasar signer")
|
|
|
|
require.True(t, h.IsDualThresholdMode(), "Should be in dual threshold mode")
|
|
|
|
ctx := context.Background()
|
|
message := []byte("block hash for quantum-safe consensus")
|
|
sessionID := 1
|
|
prfKey := []byte("prf-key-for-signing-session-!!")
|
|
|
|
validatorIDs := []string{"v0", "v1", "v2"}
|
|
|
|
// === ROUND 1: All validators compute BLS share + Corona D+MACs in parallel ===
|
|
t.Log("=== Round 1: BLS signing + Corona D matrices ===")
|
|
|
|
blsSigs := make([]*QuasarSig, 3)
|
|
allRound1 := make(map[int]*coronaThreshold.Round1Data)
|
|
|
|
for i, vid := range validatorIDs {
|
|
blsSig, rtRound1, err := h.DualSignRound1(ctx, vid, message, sessionID, prfKey)
|
|
require.NoError(t, err, "Round1 failed for %s", vid)
|
|
require.NotNil(t, blsSig, "BLS signature should not be nil")
|
|
require.NotNil(t, rtRound1, "Corona Round1 data should not be nil")
|
|
|
|
blsSigs[i] = blsSig
|
|
allRound1[rtRound1.PartyID] = rtRound1
|
|
t.Logf(" %s: BLS share=%d bytes, Corona D=%dx%d", vid, len(blsSig.BLS), len(rtRound1.D), len(rtRound1.D[0]))
|
|
}
|
|
|
|
// === BLS IS DONE: Can aggregate immediately ===
|
|
t.Log("=== BLS Aggregation (single round) ===")
|
|
blsAggSig, err := h.AggregateSignatures(message, blsSigs)
|
|
require.NoError(t, err, "BLS aggregation failed")
|
|
require.NotNil(t, blsAggSig, "BLS aggregated signature should not be nil")
|
|
t.Logf(" BLS aggregated: %d bytes", len(blsAggSig.BLSAggregated))
|
|
|
|
// Verify BLS immediately
|
|
blsValid := h.VerifyAggregatedSignature(message, blsAggSig)
|
|
require.True(t, blsValid, "BLS signature verification failed")
|
|
t.Log(" BLS: ✓ verified")
|
|
|
|
// === Corona uses the native package directly for full 2-round flow ===
|
|
// This demonstrates the protocol but in production would use consensus messages
|
|
t.Log("=== Corona 2-Round Protocol (via native package) ===")
|
|
|
|
// Use the native corona threshold package directly
|
|
rtShares := config.CoronaShares
|
|
rtGroupKey := config.CoronaGroupKey
|
|
|
|
signers := make([]*coronaThreshold.Signer, 3)
|
|
for i := 0; i < 3; i++ {
|
|
signers[i] = coronaThreshold.NewSigner(rtShares[validatorIDs[i]])
|
|
}
|
|
|
|
signerIDs := []int{0, 1, 2}
|
|
messageStr := string(message)
|
|
|
|
// Round 1: All parties compute D + MACs
|
|
rtRound1Data := make(map[int]*coronaThreshold.Round1Data)
|
|
for _, signer := range signers {
|
|
data, err := signer.Round1(sessionID, prfKey, signerIDs)
|
|
require.NoError(t, err)
|
|
rtRound1Data[data.PartyID] = data
|
|
}
|
|
t.Log(" Round1: D matrices computed")
|
|
|
|
// Round 2: All parties compute z shares
|
|
rtRound2Data := make(map[int]*coronaThreshold.Round2Data)
|
|
for _, signer := range signers {
|
|
data, err := signer.Round2(sessionID, messageStr, prfKey, signerIDs, rtRound1Data)
|
|
require.NoError(t, err, "Corona Round2 failed")
|
|
rtRound2Data[data.PartyID] = data
|
|
}
|
|
t.Log(" Round2: z shares computed")
|
|
|
|
// Finalize: Any signer aggregates
|
|
rtSig, err := signers[0].Finalize(rtRound2Data)
|
|
require.NoError(t, err, "Corona finalization failed")
|
|
require.NotNil(t, rtSig, "Corona signature should not be nil")
|
|
t.Logf(" Finalize: Z=%d, Delta=%d", len(rtSig.Z), len(rtSig.Delta))
|
|
|
|
// Verify Corona
|
|
rtValid := coronaThreshold.Verify(rtGroupKey, messageStr, rtSig)
|
|
require.True(t, rtValid, "Corona signature verification failed")
|
|
t.Log(" Corona: ✓ verified")
|
|
|
|
t.Log("✓ Dual BLS + Corona signing flow complete")
|
|
t.Log(" - BLS: 1 round, aggregated, verified")
|
|
t.Log(" - Corona: 2 rounds, aggregated, verified (post-quantum)")
|
|
}
|