Files
zeekay 1f13e8ef37 protocol/wave: implement Fast Probabilistic Consensus (FPC)
Add FPC threshold selector with PRF-based phase-dependent threshold selection.
Integrates seamlessly with Wave protocol for dynamic alpha thresholds.

Features:
- PRF-based deterministic threshold selection using SHA-256
- Configurable theta range [θ_min, θ_max] (default: 0.5-0.8)
- Phase-dependent thresholds: α = ⌈θ·k⌉
- Backward compatible with EnableFPC flag (default: false)
- Zero breaking changes to existing Wave API

Test Coverage:
- 10/10 FPC unit tests PASS (determinism, theta range, benchmarks)
- 5/5 FPC integration tests PASS (enabled/disabled, variation, comparison)
- 18/18 Wave tests PASS (all existing tests still work)

Implementation:
- /protocol/wave/fpc/fpc.go (83 lines) - Core threshold selector
- /protocol/wave/fpc/fpc_test.go (177 lines) - Comprehensive tests
- /protocol/wave/fpc_integration_test.go (274 lines) - Integration demos
- /protocol/wave/wave.go - Wave integration with FPC support

All 78/78 protocol tests passing. FPC is production-ready!
2025-11-10 18:58:56 -08:00

83 lines
2.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package fpc
import (
"crypto/sha256"
"encoding/binary"
"math"
)
// Selector provides phase-dependent threshold selection for FPC
type Selector struct {
thetaMin float64
thetaMax float64
seed []byte
}
// NewSelector creates a new FPC threshold selector
func NewSelector(thetaMin, thetaMax float64, seed []byte) *Selector {
if thetaMin <= 0 || thetaMin >= 1 {
thetaMin = 0.5
}
if thetaMax <= thetaMin || thetaMax > 1 {
thetaMax = 0.8
}
if seed == nil {
seed = []byte("lux-fpc-default-seed")
}
return &Selector{
thetaMin: thetaMin,
thetaMax: thetaMax,
seed: seed,
}
}
// SelectThreshold picks θ ∈ [θ_min, θ_max] using PRF for phase
// Returns α = ⌈θ·k⌉ for both preference and confidence
func (s *Selector) SelectThreshold(phase uint64, k int) int {
theta := s.computeTheta(phase)
return int(math.Ceil(theta * float64(k)))
}
// computeTheta uses PRF to deterministically select θ for a given phase
func (s *Selector) computeTheta(phase uint64) float64 {
// Create PRF input: seed || phase
h := sha256.New()
h.Write(s.seed)
phaseBytes := make([]byte, 8)
binary.BigEndian.PutUint64(phaseBytes, phase)
h.Write(phaseBytes)
hash := h.Sum(nil)
// Convert first 8 bytes of hash to uint64, normalize to [0,1]
hashUint := binary.BigEndian.Uint64(hash[:8])
normalized := float64(hashUint) / float64(^uint64(0))
// Scale to [thetaMin, thetaMax]
theta := s.thetaMin + normalized*(s.thetaMax-s.thetaMin)
return theta
}
// Theta returns the raw theta value for a phase (for testing/debugging)
func (s *Selector) Theta(phase uint64) float64 {
return s.computeTheta(phase)
}
// Range returns the configured theta range
func (s *Selector) Range() (min, max float64) {
return s.thetaMin, s.thetaMax
}
// DefaultSelector returns a selector with default parameters
func DefaultSelector() *Selector {
return NewSelector(0.5, 0.8, nil)
}
// SelectThreshold is a convenience function using default selector
func SelectThreshold(phase uint64, k int, thetaMin, thetaMax float64) int {
s := NewSelector(thetaMin, thetaMax, nil)
return s.SelectThreshold(phase, k)
}