Files
consensus/pkg/go/engine/pq/consensus.go
2025-09-24 04:01:37 -05:00

223 lines
5.7 KiB
Go

// Package pq implements the post-quantum consensus engine that combines
// classical and quantum-resistant consensus mechanisms.
package pq
import (
"context"
"fmt"
"sync"
"time"
"github.com/luxfi/consensus/config"
"github.com/luxfi/consensus/core/dag"
"github.com/luxfi/consensus/protocol/quasar"
"github.com/luxfi/ids"
)
// ConsensusEngine implements post-quantum consensus combining classical and quantum-resistant security
type ConsensusEngine struct {
params config.Parameters
quasar *quasar.Quasar
finality chan FinalityEvent
mu sync.RWMutex
// State tracking
height uint64
round uint64
finalized map[ids.ID]bool
}
// FinalityEvent represents a finalized block with quantum-resistant proofs
type FinalityEvent struct {
Height uint64
BlockID ids.ID
Timestamp time.Time
PQProof []byte // Post-quantum proof
BLSProof []byte // Classical BLS proof
}
// memoryStore is a simple in-memory implementation of dag.Store for P-Chain vertices
type memoryStore struct {
vertices map[quasar.VertexID]*memoryVertex
heads []quasar.VertexID
mu sync.RWMutex
}
type memoryVertex struct {
id quasar.VertexID
parents []quasar.VertexID
author string
round uint64
children []quasar.VertexID
}
func (v *memoryVertex) ID() quasar.VertexID { return v.id }
func (v *memoryVertex) Parents() []quasar.VertexID { return v.parents }
func (v *memoryVertex) Author() string { return v.author }
func (v *memoryVertex) Round() uint64 { return v.round }
func (s *memoryStore) Head() []quasar.VertexID {
if s.vertices == nil {
s.vertices = make(map[quasar.VertexID]*memoryVertex)
}
s.mu.RLock()
defer s.mu.RUnlock()
result := make([]quasar.VertexID, len(s.heads))
copy(result, s.heads)
return result
}
func (s *memoryStore) Get(id quasar.VertexID) (dag.BlockView[quasar.VertexID], bool) {
if s.vertices == nil {
s.vertices = make(map[quasar.VertexID]*memoryVertex)
}
s.mu.RLock()
defer s.mu.RUnlock()
vertex, exists := s.vertices[id]
return vertex, exists
}
func (s *memoryStore) Children(id quasar.VertexID) []quasar.VertexID {
if s.vertices == nil {
s.vertices = make(map[quasar.VertexID]*memoryVertex)
}
s.mu.RLock()
defer s.mu.RUnlock()
if vertex, exists := s.vertices[id]; exists {
result := make([]quasar.VertexID, len(vertex.children))
copy(result, vertex.children)
return result
}
return []quasar.VertexID{}
}
// NewConsensus creates a new post-quantum consensus engine
func NewConsensus(params config.Parameters) *ConsensusEngine {
// Create a simple in-memory store for P-Chain vertices
// TODO: Use proper persistent storage in production
store := &memoryStore{}
return &ConsensusEngine{
params: params,
quasar: quasar.New(params, store),
finality: make(chan FinalityEvent, 100),
finalized: make(map[ids.ID]bool),
}
}
// Initialize sets up the PQ engine with keys
func (e *ConsensusEngine) Initialize(ctx context.Context, blsKey, pqKey []byte) error {
return e.quasar.Initialize(ctx, blsKey, pqKey)
}
// ProcessBlock processes a block through PQ consensus
func (e *ConsensusEngine) ProcessBlock(ctx context.Context, blockID ids.ID, votes map[string]int) error {
e.mu.Lock()
defer e.mu.Unlock()
// Check if already finalized
if e.finalized[blockID] {
return nil
}
// For now, use simplified consensus logic
// In production, this would integrate with quasar's internal methods
totalVotes := 0
maxVotes := 0
bestBlock := ""
for block, count := range votes {
totalVotes += count
if count > maxVotes {
maxVotes = count
bestBlock = block
}
}
// Check if we have enough votes for the best block
if totalVotes == 0 || float64(maxVotes)/float64(totalVotes) < e.params.Alpha {
return fmt.Errorf("insufficient votes for finality: %d/%d for block %s", maxVotes, totalVotes, bestBlock)
}
// Create mock certificate
cert := &quasar.CertBundle{
BLSAgg: []byte("mock-bls-aggregate"),
PQCert: []byte("mock-pq-certificate"),
}
// Validate certificate (placeholder logic)
if len(cert.BLSAgg) == 0 && len(cert.PQCert) == 0 {
return fmt.Errorf("insufficient votes for finality")
}
// Mark as finalized
e.finalized[blockID] = true
e.height++
// Emit finality event
select {
case e.finality <- FinalityEvent{
Height: e.height,
BlockID: blockID,
Timestamp: time.Now(),
PQProof: cert.PQCert,
BLSProof: cert.BLSAgg,
}:
case <-ctx.Done():
return ctx.Err()
default:
// Channel full, drop oldest
}
return nil
}
// IsFinalized checks if a block has achieved PQ finality
func (e *ConsensusEngine) IsFinalized(blockID ids.ID) bool {
e.mu.RLock()
defer e.mu.RUnlock()
return e.finalized[blockID]
}
// FinalityChannel returns the channel for finality events
func (e *ConsensusEngine) FinalityChannel() <-chan FinalityEvent {
return e.finality
}
// Height returns the current finalized height
func (e *ConsensusEngine) Height() uint64 {
e.mu.RLock()
defer e.mu.RUnlock()
return e.height
}
// SetFinalizedCallback sets a callback for finalized blocks
func (e *ConsensusEngine) SetFinalizedCallback(cb func(FinalityEvent)) {
e.quasar.SetFinalizedCallback(func(qb quasar.QBlock) {
blockID, _ := ids.FromString(qb.Hash)
cb(FinalityEvent{
Height: uint64(qb.Height),
BlockID: blockID,
Timestamp: qb.Timestamp,
PQProof: qb.Cert.PQCert,
BLSProof: qb.Cert.BLSAgg,
})
})
}
// Metrics returns engine metrics
func (e *ConsensusEngine) Metrics() map[string]interface{} {
e.mu.RLock()
defer e.mu.RUnlock()
return map[string]interface{}{
"height": e.height,
"round": e.round,
"finalized": len(e.finalized),
"k": e.params.K,
"alpha": e.params.Alpha,
"beta": e.params.Beta,
"block_time": e.params.BlockTime.String(),
}
}