mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
CR-12 — bind effectivePolicy byte into ComputeRoundDigest transcript protocol/quasar/round_digest.go: new uint8 parameter, refused if zero, bound as a TupleHash part between verifierBytes and netBytes. Closes cross-policy replay: an adversary can no longer sign a Round R as PolicyPQ and strip the Q witness on retransmit, presenting the bare P witness as a PolicyQuorum cert under an identical digest. Tests updated: canonical inputs include effectivePolicy=1, FixedHash- Family mirror parts include the policy byte at the matching position. CR-13 — rejection sampling closes modulo bias in three samplers protocol/photon/emitter.go cryptoRandInt protocol/prism/cut.go cryptoRandInt protocol/prism/stake_weighted_cut.go cryptoRandUint64 Prior `% max` form was structurally biased for non-power-of-2 max; under nation-state grinding model (Pinkas-Reiter style) the bias was an exploit on committee sampling. Rejection sampling computes the largest multiple of max that fits in uint64 and rejects values above it. Expected iterations ~1-2; never more than 2 in expectation. Tests pass on protocol/quasar, protocol/photon, protocol/prism. BLOCKERS.md CR-12 and CR-13 marked closed. Two of 13 critical findings down. Remaining 11 are in progress.
97 lines
2.6 KiB
Go
97 lines
2.6 KiB
Go
package prism
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/binary"
|
|
|
|
"github.com/luxfi/consensus/core/types"
|
|
)
|
|
|
|
// Cut provides random cutting of peers for consensus voting (like a prism cuts light)
|
|
type Cut[T comparable] interface {
|
|
// Sample returns k random peers for voting (cuts k rays from the population)
|
|
Sample(k int) []types.NodeID
|
|
|
|
// Luminance returns light intensity metrics for the cut
|
|
Luminance() Luminance
|
|
}
|
|
|
|
// Luminance measures the intensity of light across the peer network
|
|
// Following SI units: lux (lx) = lumens per square meter
|
|
type Luminance struct {
|
|
ActivePeers int
|
|
TotalPeers int
|
|
Lx float64 // Illuminance in lux (lx) - minimum 1 lx per active peer/photon
|
|
}
|
|
|
|
// UniformCut implements uniform random cutting
|
|
type UniformCut struct {
|
|
peers []types.NodeID
|
|
}
|
|
|
|
// NewUniformCut creates a new uniform cut
|
|
func NewUniformCut(peers []types.NodeID) *UniformCut {
|
|
return &UniformCut{peers: peers}
|
|
}
|
|
|
|
// Sample implements Cut interface using Fisher-Yates shuffle with crypto/rand.
|
|
// This provides uniform random sampling of k peers from the population.
|
|
func (c *UniformCut) Sample(k int) []types.NodeID {
|
|
n := len(c.peers)
|
|
if k >= n {
|
|
return c.peers
|
|
}
|
|
|
|
// Create a copy to shuffle (don't modify original)
|
|
shuffled := make([]types.NodeID, n)
|
|
copy(shuffled, c.peers)
|
|
|
|
// Fisher-Yates shuffle with cryptographic randomness
|
|
// Only need to shuffle first k elements
|
|
for i := 0; i < k; i++ {
|
|
// Generate random index j where i <= j < n
|
|
j := i + cryptoRandInt(n-i)
|
|
shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
|
|
}
|
|
|
|
return shuffled[:k]
|
|
}
|
|
|
|
// cryptoRandInt returns a cryptographically secure random integer in
|
|
// [0, max) — uniformly distributed via rejection sampling. Closes
|
|
// BLOCKERS.md CR-13. See protocol/photon/emitter.go for the same
|
|
// rationale; both samplers were exploitable under nation-state
|
|
// committee-grinding.
|
|
func cryptoRandInt(max int) int {
|
|
if max <= 0 {
|
|
return 0
|
|
}
|
|
limit := (^uint64(0) / uint64(max)) * uint64(max)
|
|
var buf [8]byte
|
|
for {
|
|
_, _ = rand.Read(buf[:])
|
|
v := binary.LittleEndian.Uint64(buf[:])
|
|
if v < limit {
|
|
return int(v % uint64(max))
|
|
}
|
|
}
|
|
}
|
|
|
|
// Luminance implements Cut interface
|
|
func (c *UniformCut) Luminance() Luminance {
|
|
activePeers := len(c.peers)
|
|
// Minimum 1 lx per active peer/photon, scaling with network health
|
|
lx := float64(activePeers) // Base: 1 lx per peer
|
|
if activePeers >= 100 {
|
|
lx = 500.0 // Office lighting level for healthy large networks
|
|
} else if activePeers >= 20 {
|
|
lx = 300.0 // Classroom level for medium networks
|
|
}
|
|
|
|
return Luminance{
|
|
ActivePeers: activePeers,
|
|
TotalPeers: activePeers,
|
|
Lx: lx,
|
|
}
|
|
}
|