mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
Replaces every direct import of luxfi/corona/* and luxfi/pulsar/ref/go
in consensus with the canonical luxfi/threshold/protocols/{corona,pulsar}
alias surface. Pure import swap + go.mod bump; no behaviour change.
Files touched (16):
bench/pq_modes_bench_test.go
engine/pq/crypto.go
engine/pq/engine.go
pkg/wire/policies.go
protocol/quasar/cert_size_compare_test.go
protocol/quasar/corona_gob.go
protocol/quasar/dual_threshold_test.go
protocol/quasar/epoch.go
protocol/quasar/epoch_test.go
protocol/quasar/gpu_accel.go
protocol/quasar/grouped_threshold.go
protocol/quasar/quasar.go
protocol/quasar/types.go
protocol/quasar/wave_signer.go
protocol/quasar/wave_signer_test.go
protocol/quasar/witness.go
protocol/quasar/witness_pulsar_test.go
Import path swaps:
luxfi/corona/threshold -> luxfi/threshold/protocols/corona
luxfi/corona/keyera -> luxfi/threshold/protocols/corona/keyera
luxfi/corona/primitives -> luxfi/threshold/protocols/corona/primitives
luxfi/corona/reshare -> luxfi/threshold/protocols/corona/reshare
luxfi/corona/sign -> luxfi/threshold/protocols/corona/sign
luxfi/corona/gpu -> luxfi/threshold/protocols/corona/gpu
luxfi/pulsar/ref/go/pkg/pulsar -> luxfi/threshold/protocols/pulsar
The aliased package name "coronaThreshold" used by quasar / engine/pq /
pkg/wire identifiers is preserved at the call sites (it now refers to
the threshold/protocols/corona alias package). engine/pq/crypto.go is
the only file where the package was bare-imported as `threshold`; that
identifier was renamed to `corona` to match the new alias path.
go.mod:
- luxfi/threshold bumped to a branch pseudo-version that carries the
new alias subpackages; will pin to a stable tag once threshold's
companion commit lands and a release is cut.
- luxfi/corona and luxfi/pulsar drop to indirect (consensus now
reaches them only via threshold/protocols).
- luxfi/math v1.4.0 -> v1.4.1 and luxfi/metric v1.5.0 -> v1.5.7 are
transitive minimum-version bumps pulled by the new threshold
requirement; they are no-op for consensus call sites.
Comment-only references to "github.com/luxfi/corona/..." in bls.go,
round_digest.go, reshare_epoch.go, comprehensive_test.go,
engine_test.go, quasar_bench_test.go and config/pq_mode.go are
documentation pointers to where the underlying implementation lives;
they are accurate either way and are left unchanged (out of refactor
scope).
Layering shape after:
consensus -> threshold/protocols/{corona,pulsar} (stable alias)
|
v
luxfi/corona, luxfi/pulsar (primitive implementations)
Closes the 22-file layering violation flagged by the 2026-05-31
integration audit. Tests pass: ./engine/pq/..., ./pkg/wire/...,
./protocol/quasar/... (all key cases — TestEpochManager*,
TestRoundSigner*, TestVerifyPQFinality*, TestBLSThreshold*,
TestDualSigningFlow, TestCoronaCertSize* — green; the pre-existing
TestGroupedEpochManager_ParallelKeygen perf-bound flake on slow
machines was reproduced on unmodified main, so it is not a
regression).
199 lines
5.7 KiB
Go
199 lines
5.7 KiB
Go
package pq
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/luxfi/consensus/protocol/quasar"
|
|
"github.com/luxfi/crypto/bls"
|
|
"github.com/luxfi/crypto/mldsa"
|
|
"github.com/luxfi/ids"
|
|
coronaThreshold "github.com/luxfi/threshold/protocols/corona"
|
|
)
|
|
|
|
// Engine defines the post-quantum consensus engine
|
|
type Engine interface {
|
|
// Start starts the engine
|
|
Start(context.Context, uint32) error
|
|
|
|
// Stop stops the engine
|
|
Stop(context.Context) error
|
|
|
|
// HealthCheck performs a health check
|
|
HealthCheck(context.Context) (interface{}, error)
|
|
|
|
// IsBootstrapped returns whether the engine is bootstrapped
|
|
IsBootstrapped() bool
|
|
|
|
// VerifyQuantumSignature verifies a post-quantum signature
|
|
VerifyQuantumSignature([]byte, []byte, []byte) error
|
|
|
|
// GenerateQuantumProof generates a quantum-resistant proof
|
|
GenerateQuantumProof(context.Context, ids.ID) ([]byte, error)
|
|
}
|
|
|
|
// PostQuantum implements post-quantum consensus engine
|
|
type PostQuantum struct {
|
|
bootstrapped bool
|
|
algorithm string // ML-DSA, ML-KEM, etc.
|
|
|
|
mu sync.RWMutex
|
|
// Real signing engine, optional. When set, GenerateQuantumProof
|
|
// produces a serialized QuasarCert via TripleSignRound1.
|
|
signer *quasar.Signer
|
|
// Verification keys, optional. When set, VerifyQuantumSignature
|
|
// performs real BLS+Corona+ML-DSA verification on a serialized
|
|
// QuasarCert.
|
|
blsAggKey *bls.PublicKey
|
|
rtGroupKey *coronaThreshold.GroupKey
|
|
mldsaPubKey *mldsa.PublicKey
|
|
}
|
|
|
|
// New creates a new post-quantum consensus engine
|
|
func New() *PostQuantum {
|
|
return &PostQuantum{
|
|
bootstrapped: false,
|
|
algorithm: "ML-DSA-65", // Default to ML-DSA-65
|
|
}
|
|
}
|
|
|
|
// AttachSigner wires a real PQ signer for proof generation.
|
|
func (pq *PostQuantum) AttachSigner(s *quasar.Signer) {
|
|
pq.mu.Lock()
|
|
defer pq.mu.Unlock()
|
|
pq.signer = s
|
|
}
|
|
|
|
// AttachVerifyKeys wires real PQ verification keys. blsAggKey is required;
|
|
// rtGroupKey and mldsaPubKey are optional (skipped if nil).
|
|
func (pq *PostQuantum) AttachVerifyKeys(blsAggKey *bls.PublicKey, rtGroupKey *coronaThreshold.GroupKey, mldsaPubKey *mldsa.PublicKey) {
|
|
pq.mu.Lock()
|
|
defer pq.mu.Unlock()
|
|
pq.blsAggKey = blsAggKey
|
|
pq.rtGroupKey = rtGroupKey
|
|
pq.mldsaPubKey = mldsaPubKey
|
|
}
|
|
|
|
// Start starts the engine
|
|
func (pq *PostQuantum) Start(ctx context.Context, requestID uint32) error {
|
|
pq.bootstrapped = true
|
|
return nil
|
|
}
|
|
|
|
// Stop stops the engine
|
|
func (pq *PostQuantum) Stop(ctx context.Context) error {
|
|
return nil
|
|
}
|
|
|
|
// HealthCheck performs a health check
|
|
func (pq *PostQuantum) HealthCheck(ctx context.Context) (interface{}, error) {
|
|
return map[string]interface{}{
|
|
"healthy": true,
|
|
"algorithm": pq.algorithm,
|
|
}, nil
|
|
}
|
|
|
|
// IsBootstrapped returns whether the engine is bootstrapped
|
|
func (pq *PostQuantum) IsBootstrapped() bool {
|
|
return pq.bootstrapped
|
|
}
|
|
|
|
// VerifyQuantumSignature verifies a serialized QuasarCert.
|
|
//
|
|
// signature must be the bytes produced by QuasarCert.MarshalBinary.
|
|
// message is the digest the cert commits to.
|
|
// publicKey is reserved for future single-key paths; current implementation
|
|
// uses verification keys attached via AttachVerifyKeys.
|
|
func (pq *PostQuantum) VerifyQuantumSignature(message, signature, publicKey []byte) error {
|
|
pq.mu.RLock()
|
|
blsAggKey := pq.blsAggKey
|
|
rtGroupKey := pq.rtGroupKey
|
|
mldsaPubKey := pq.mldsaPubKey
|
|
pq.mu.RUnlock()
|
|
|
|
if blsAggKey == nil {
|
|
return errors.New("pq: no BLS aggregate verify key attached")
|
|
}
|
|
|
|
cert := &quasar.QuasarCert{}
|
|
if err := cert.UnmarshalBinary(signature); err != nil {
|
|
return fmt.Errorf("pq: decode QuasarCert: %w", err)
|
|
}
|
|
|
|
var mldsaKeys []*mldsa.PublicKey
|
|
if mldsaPubKey != nil {
|
|
mldsaKeys = []*mldsa.PublicKey{mldsaPubKey}
|
|
}
|
|
|
|
if !cert.VerifyWithRealKeys(message, blsAggKey, rtGroupKey, mldsaKeys) {
|
|
return errors.New("pq: QuasarCert verification failed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GenerateQuantumProof generates a serialized QuasarCert for the given block.
|
|
// Requires AttachSigner to have been called with a fully-configured signer.
|
|
func (pq *PostQuantum) GenerateQuantumProof(ctx context.Context, blockID ids.ID) ([]byte, error) {
|
|
pq.mu.RLock()
|
|
s := pq.signer
|
|
pq.mu.RUnlock()
|
|
if s == nil {
|
|
return nil, errors.New("pq: no signer attached")
|
|
}
|
|
|
|
// Pick any configured validator.
|
|
validatorID, err := pickValidator(s)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Build canonical message: SHA-256(blockID || "lux-pq-engine-v1").
|
|
h := sha256.New()
|
|
h.Write(blockID[:])
|
|
h.Write([]byte("lux-pq-engine-v1"))
|
|
msg := h.Sum(nil)
|
|
|
|
prfKeyArr := sha256.Sum256(blockID[:])
|
|
prfKey := prfKeyArr[:]
|
|
sessionID := int(blockID[0])<<24 | int(blockID[1])<<16 | int(blockID[2])<<8 | int(blockID[3])
|
|
|
|
sig, _, err := s.TripleSignRound1(ctx, validatorID, msg, sessionID, prfKey)
|
|
if err != nil {
|
|
// Fall back to legacy single-key path for signers without BLS
|
|
// threshold configuration.
|
|
legacy, lerr := s.SignMessageWithContext(ctx, validatorID, msg)
|
|
if lerr != nil {
|
|
return nil, fmt.Errorf("pq: sign block: %w", err)
|
|
}
|
|
sig = legacy
|
|
}
|
|
|
|
cert := &quasar.QuasarCert{
|
|
BLS: append([]byte(nil), sig.BLS...),
|
|
}
|
|
if len(sig.MLDSA) > 0 {
|
|
cert.MLDSARollup = quasar.EncodeMLDSASigs([][]byte{sig.MLDSA})
|
|
}
|
|
return cert.MarshalBinary()
|
|
}
|
|
|
|
// pickValidator returns any validator ID configured on the signer.
|
|
func pickValidator(s *quasar.Signer) (string, error) {
|
|
// Use exported helpers: AddValidator stores into validators map but
|
|
// there is no public iterator, so use any configured BLS key.
|
|
// For test-driver paths the caller knows which validator they added;
|
|
// we expose a helper here that callers can override by attaching
|
|
// a signer that already has at least one validator.
|
|
if s == nil {
|
|
return "", errors.New("pq: nil signer")
|
|
}
|
|
id := s.AnyValidatorID()
|
|
if id == "" {
|
|
return "", errors.New("pq: signer has no configured validators")
|
|
}
|
|
return id, nil
|
|
}
|