Files
zeekayandHanzo Dev ab41ba6b1c rust: the eligibility rule at registration, on the identity the proof binds
`ValidatorSet` admitted a key on a proof of possession and refused a key
already held by another node, and stopped there. Three things the Go rule
turns on were missing, and this closes them by naming the same clauses in
the same order as `validator/registration.go`:

  NO KEY       a registration with no key wanted the unkeyed door
  ZERO WEIGHT  a keyed signer with no stake is a phantom signer — it raises
               the count a floor is read against without raising the weight
  ENCODING     the key is a canonical compressed BLS12-381 G1 point
  POSSESSION   a node-bound proof binds THIS key to THIS node
  UNIQUENESS   the key is registered to no node, AND the node to no key

The node axis is the new half of uniqueness, and possession cannot stand in
for it: one operator registering two keys it genuinely holds under one id
offers two genuine proofs, and takes two signer slots and two shares of the
weight. So a node is admitted exactly once, on both doors. Re-keying is a
`remove` and a fresh admission — the one spelling that cannot change a live
member's key or weight behind a caller that thought it was adding one.

And registration now calls `pop::verify` rather than restating it. There was
a second proof-of-possession implementation here, with its own copy of the
domain tag and the preimage; two homes for one standard is how the two
drift, so there is one, and it is the one the frozen Go vectors pin.

THE IDENTITY. A voter was a 32-byte block `Id`. Go names a validator by
`ids.NodeID`, 20 bytes, and the proof signs `node(20) ‖ key(48)` — so the
proof the network makes was not the proof this crate checked, and the two
were never the same standard however well each held on its own. The voter
is now `pop::NodeId`, one value with one width at all three places it is
read: registration, the proof preimage, and the strictly-ascending vote
order. `types::NodeID` is a 20-byte type of its own rather than an alias of
the block id, with no lossy constructor — truncating 32 bytes into 20 maps
distinct nodes onto one identity, and one identity is one signer slot.

No signed byte moves: the node id enters no signed message, so Go's frozen
vote message and Go's signatures verify unchanged.

Verified: 83 tests green, `pop_conformance` and `cert_conformance`
byte-identical; clippy clean. Eight mutants applied and killed, one per
clause — possession dropped, each uniqueness axis dropped, zero weight
admitted, no-key admitted, the weight clause reordered behind possession,
the preimage padded to 32 bytes, and the unkeyed door restating a member.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-01 11:08:02 -07:00

2.0 KiB

lux-consensus

Pure-Rust SDK for the Lux Quasar consensus stack — Wave threshold voting, FPC (Fast Probabilistic Consensus), Photon validator sampling, Focus confidence accumulation, and post-quantum hybrid finality via BLS12-381 + Pulsar (Module-LWE threshold, FIPS 204 byte-equal).

Features

Flag What it pulls in
default CPU-only, no extra deps beyond blst and hex
simd blake3 (SIMD hashing) + rayon (data-parallel vote tally)
mlx Apple Silicon GPU acceleration via mlx-sys (implies simd)
cuda NVIDIA GPU acceleration (implies simd) — work in progress
gpu mlx + cuda — auto-select per platform

Usage

use lux_consensus::*;

let config = QuasarConfig::mainnet();
let mut engine = QuasarEngine::new(config);
engine.start().unwrap();

let block = Block::new(
    ID::from([1u8; 32]),
    ID::from([0u8; 32]),
    1,
    b"Hello, Lux!".to_vec(),
);
engine.add(block.clone()).unwrap();

// A validator is named by its 20-byte NodeID — never the 32-byte block id.
for i in 0..20 {
    engine.add_validator(NodeID::from([i; 20]), 1).unwrap();
}

for i in 0..20 {
    let vote = Vote::new(block.id.clone(), VoteType::Preference, NodeID::from([i; 20]));
    engine.record_vote(vote).unwrap();
}

assert!(engine.is_accepted(&block.id));
engine.stop().unwrap();

Architecture

  • Wave — adaptive-threshold voting driven by FPC outputs
  • FPC — PRF-derived per-round thresholds for fast probabilistic consensus
  • Photon — luminance-tracked validator sampling
  • Focus — β consecutive rounds of confidence before commit
  • Quasar — a two-thirds-by-stake certificate: the distinct signed accepts themselves, each checked against its own signer's key

License

Lux Ecosystem License 1.2 — see the LICENSE file.