use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::time::{Duration, Instant, SystemTime};
pub mod finality;
pub mod cert;
pub mod pop;
pub mod vote;
pub mod zap;
pub fn sha256(input: &[u8]) -> [u8; 32] {
let mut out = [0u8; 32];
unsafe { blst::blst_sha256(out.as_mut_ptr(), input.as_ptr(), input.len()) };
out
}
pub use crate::finality::{
canonical_vote_message, crash_tolerance, half_stake_floor, nova_beta, nova_quorum,
nova_signer_floor, two_thirds_count, two_thirds_stake_floor, weighted_quasar, Finality, Position,
QC_FINALITY, QUORUM_CERT_VERSION, VOTE_MESSAGE_LEN, VOTE_TAG,
};
pub use crate::types::*;
pub use crate::errors::*;
pub use crate::fpc::*;
pub use crate::photon::*;
pub use crate::focus::*;
pub use crate::wave::*;
pub use crate::quasar::*;
pub use crate::engine::*;
pub use crate::vote::{SignedVote, Slot, Tally, VoteTransport, VOTE};
pub mod types {
use std::fmt;
use std::time::{Duration, SystemTime};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ID(pub [u8; 32]);
impl ID {
pub fn new(data: [u8; 32]) -> Self {
ID(data)
}
pub fn zero() -> Self {
ID([0u8; 32])
}
pub fn from_slice(data: &[u8]) -> Self {
let mut arr = [0u8; 32];
let len = data.len().min(32);
arr[..len].copy_from_slice(&data[..len]);
ID(arr)
}
pub fn to_vec(&self) -> Vec<u8> {
self.0.to_vec()
}
pub fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
}
impl From<[u8; 32]> for ID {
fn from(data: [u8; 32]) -> Self {
ID(data)
}
}
impl From<Vec<u8>> for ID {
fn from(data: Vec<u8>) -> Self {
ID::from_slice(&data)
}
}
impl fmt::Display for ID {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", hex::encode(self.0))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct NodeID(pub crate::pop::NodeId);
impl NodeID {
pub fn as_bytes(&self) -> &crate::pop::NodeId {
&self.0
}
}
impl From<crate::pop::NodeId> for NodeID {
fn from(data: crate::pop::NodeId) -> Self {
NodeID(data)
}
}
impl fmt::Display for NodeID {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", hex::encode(self.0))
}
}
pub type Hash = ID;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Status {
Unknown,
Processing,
Rejected,
Accepted,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Decision {
Undecided,
Accept,
Reject,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VoteType {
Preference, Commit, Cancel, }
#[derive(Debug, Clone)]
pub struct Block {
pub id: ID,
pub parent_id: ID,
pub height: u64,
pub payload: Vec<u8>,
pub timestamp: SystemTime,
}
impl Block {
pub fn new(id: ID, parent_id: ID, height: u64, payload: Vec<u8>) -> Self {
Block {
id,
parent_id,
height,
payload,
timestamp: SystemTime::now(),
}
}
pub fn genesis() -> Self {
Block {
id: ID::zero(),
parent_id: ID::zero(),
height: 0,
payload: Vec::new(),
timestamp: SystemTime::UNIX_EPOCH,
}
}
}
#[derive(Debug, Clone)]
pub struct Vote {
pub block_id: ID,
pub vote_type: VoteType,
pub voter: NodeID,
pub signature: Vec<u8>,
pub timestamp: SystemTime,
}
impl Vote {
pub fn new(block_id: ID, vote_type: VoteType, voter: NodeID) -> Self {
Vote {
block_id,
vote_type,
voter,
signature: Vec::new(),
timestamp: SystemTime::now(),
}
}
pub fn with_signature(mut self, signature: Vec<u8>) -> Self {
self.signature = signature;
self
}
pub fn prefer(&self) -> bool {
matches!(self.vote_type, VoteType::Preference | VoteType::Commit)
}
}
pub type Certificate = crate::cert::QuorumCert;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Default)]
pub enum SecurityLevel {
Low = 2, #[default]
Medium = 3, High = 5, }
#[derive(Debug, Clone)]
pub struct QuasarConfig {
pub k: usize, pub alpha: f64, pub beta: u32, pub round_timeout: Duration,
pub enable_fpc: bool, pub theta_min: f64, pub theta_max: f64, pub fpc_seed: [u8; 32],
pub base_luminance: f64, pub max_luminance: f64, pub min_luminance: f64, pub success_multiplier: f64, pub failure_multiplier: f64,
pub network_timeout: Duration,
pub max_message_size: usize,
pub max_outstanding: usize,
pub security_level: SecurityLevel,
pub quantum_resistant: bool,
pub gpu_acceleration: bool,
}
pub const DEFAULT_FPC_SEED: [u8; 32] = *b"lux-fpc-default-seed-00000000000";
pub const TESTNET_FPC_SEED: [u8; 32] = *b"lux-testnet-fpc-seed-00000000000";
pub const MAINNET_FPC_SEED: [u8; 32] = *b"lux-mainnet-fpc-secure-seed-2025";
impl Default for QuasarConfig {
fn default() -> Self {
QuasarConfig {
k: 20,
alpha: 0.69, beta: 20,
round_timeout: Duration::from_millis(100),
enable_fpc: true,
theta_min: 0.5,
theta_max: 0.8,
fpc_seed: DEFAULT_FPC_SEED,
base_luminance: 100.0,
max_luminance: 1000.0,
min_luminance: 10.0,
success_multiplier: 1.1,
failure_multiplier: 0.9,
network_timeout: Duration::from_secs(5),
max_message_size: 2 * 1024 * 1024, max_outstanding: 10,
security_level: SecurityLevel::Medium,
quantum_resistant: true,
gpu_acceleration: true,
}
}
}
impl QuasarConfig {
pub fn testnet() -> Self {
QuasarConfig {
k: 5,
alpha: 0.6,
beta: 5,
round_timeout: Duration::from_millis(50),
enable_fpc: false,
theta_max: 0.7,
fpc_seed: TESTNET_FPC_SEED,
max_luminance: 500.0,
min_luminance: 20.0,
success_multiplier: 1.05,
failure_multiplier: 0.95,
network_timeout: Duration::from_secs(10),
max_message_size: 1024 * 1024,
max_outstanding: 5,
security_level: SecurityLevel::Low,
quantum_resistant: false,
gpu_acceleration: false,
..QuasarConfig::default()
}
}
pub fn mainnet() -> Self {
QuasarConfig {
k: 21,
fpc_seed: MAINNET_FPC_SEED,
security_level: SecurityLevel::High,
..QuasarConfig::default()
}
}
pub fn alpha_count(&self) -> usize {
(self.alpha * self.k as f64).ceil() as usize
}
}
}
pub mod errors {
use std::error::Error;
use std::fmt;
#[derive(Debug)]
pub enum ConsensusError {
BlockNotFound,
InvalidBlock,
InvalidVote,
InvalidSignature,
NoQuorum,
AlreadyVoted,
NotValidator,
Timeout,
NotInitialized,
AlreadyStarted,
CryptoError(String),
NetworkError(String),
Other(String),
}
impl fmt::Display for ConsensusError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ConsensusError::BlockNotFound => write!(f, "Block not found"),
ConsensusError::InvalidBlock => write!(f, "Invalid block"),
ConsensusError::InvalidVote => write!(f, "Invalid vote"),
ConsensusError::InvalidSignature => write!(f, "Invalid signature"),
ConsensusError::NoQuorum => write!(f, "No quorum reached"),
ConsensusError::AlreadyVoted => write!(f, "Already voted"),
ConsensusError::NotValidator => write!(f, "Not a validator"),
ConsensusError::Timeout => write!(f, "Operation timeout"),
ConsensusError::NotInitialized => write!(f, "Engine not initialized"),
ConsensusError::AlreadyStarted => write!(f, "Engine already started"),
ConsensusError::CryptoError(msg) => write!(f, "Crypto error: {}", msg),
ConsensusError::NetworkError(msg) => write!(f, "Network error: {}", msg),
ConsensusError::Other(msg) => write!(f, "{}", msg),
}
}
}
impl Error for ConsensusError {}
pub type Result<T> = std::result::Result<T, ConsensusError>;
}
pub mod fpc {
use super::*;
#[derive(Debug, Clone)]
pub struct FpcSelector {
theta_min: f64,
theta_max: f64,
seed: [u8; 32],
}
impl FpcSelector {
pub fn new(theta_min: f64, theta_max: f64, seed: [u8; 32]) -> Self {
let theta_min = if theta_min > 0.0 && theta_min < 1.0 {
theta_min
} else {
0.5
};
let theta_max = if theta_max > theta_min && theta_max <= 1.0 {
theta_max
} else {
0.8
};
FpcSelector {
theta_min,
theta_max,
seed,
}
}
fn compute_theta(&self, phase: u64) -> f64 {
let mut input = [0u8; 40];
input[..32].copy_from_slice(&self.seed);
input[32..40].copy_from_slice(&phase.to_be_bytes());
let hash = sha256(&input);
let hash_u64 = u64::from_be_bytes([
hash[0], hash[1], hash[2], hash[3],
hash[4], hash[5], hash[6], hash[7],
]);
let normalized = (hash_u64 as f64) / (u64::MAX as f64);
self.theta_min + normalized * (self.theta_max - self.theta_min)
}
pub fn select_threshold(&self, phase: u64, k: usize) -> usize {
let theta = self.compute_theta(phase);
(theta * k as f64).ceil() as usize
}
pub fn theta(&self, phase: u64) -> f64 {
self.compute_theta(phase)
}
pub fn range(&self) -> (f64, f64) {
(self.theta_min, self.theta_max)
}
}
impl Default for FpcSelector {
fn default() -> Self {
let c = QuasarConfig::default();
FpcSelector::new(c.theta_min, c.theta_max, c.fpc_seed)
}
}
}
pub mod photon {
use super::*;
#[derive(Debug, Clone)]
pub struct Luminance {
lux: HashMap<NodeID, f64>,
base: f64,
max: f64,
min: f64,
success_mult: f64,
failure_mult: f64,
}
impl Luminance {
pub fn new(config: &QuasarConfig) -> Self {
Luminance {
lux: HashMap::new(),
base: config.base_luminance,
max: config.max_luminance,
min: config.min_luminance,
success_mult: config.success_multiplier,
failure_mult: config.failure_multiplier,
}
}
pub fn illuminate(&mut self, id: &NodeID, success: bool) {
let current = self.lux.entry(*id).or_insert(self.base);
if success {
*current *= self.success_mult;
if *current > self.max {
*current = self.max;
}
} else {
*current *= self.failure_mult;
if *current < self.min {
*current = self.min;
}
}
}
pub fn brightness(&self, id: &NodeID) -> f64 {
self.lux.get(id).copied().unwrap_or(self.base) / self.base
}
pub fn lux(&self, id: &NodeID) -> f64 {
self.lux.get(id).copied().unwrap_or(self.base)
}
pub fn total_luminance(&self) -> f64 {
self.lux.values().sum()
}
pub fn node_count(&self) -> usize {
self.lux.len()
}
}
impl Default for Luminance {
fn default() -> Self {
Luminance::new(&QuasarConfig::default())
}
}
pub struct PhotonSampler {
peers: Vec<NodeID>,
luminance: Luminance,
}
impl PhotonSampler {
pub fn new(peers: Vec<NodeID>, config: &QuasarConfig) -> Self {
PhotonSampler {
peers,
luminance: Luminance::new(config),
}
}
pub fn sample(&self, k: usize) -> Vec<NodeID> {
if self.peers.is_empty() {
return Vec::new();
}
let k = k.min(self.peers.len());
let weights: Vec<f64> = self.peers
.iter()
.map(|p| self.luminance.brightness(p))
.collect();
let total_weight: f64 = weights.iter().sum();
if total_weight == 0.0 {
return self.peers.iter().take(k).cloned().collect();
}
let mut selected = Vec::with_capacity(k);
let mut used = vec![false; self.peers.len()];
for i in 0..k {
let mut best_idx = 0;
let mut best_score = f64::MIN;
for (idx, &weight) in weights.iter().enumerate() {
if used[idx] {
continue;
}
let score = weight * ((idx + i + 1) as f64 / self.peers.len() as f64);
if score > best_score {
best_score = score;
best_idx = idx;
}
}
used[best_idx] = true;
selected.push(self.peers[best_idx]);
}
selected
}
pub fn update_luminance(&mut self, id: &NodeID, success: bool) {
self.luminance.illuminate(id, success);
}
pub fn add_peer(&mut self, peer: NodeID) {
if !self.peers.contains(&peer) {
self.peers.push(peer);
}
}
pub fn remove_peer(&mut self, peer: &NodeID) {
self.peers.retain(|p| p != peer);
}
pub fn luminance(&self) -> &Luminance {
&self.luminance
}
}
}
pub mod focus {
use super::*;
#[derive(Debug)]
pub struct Focus<ID: Eq + std::hash::Hash + Clone> {
threshold: u32, alpha: f64, states: HashMap<ID, FocusState>,
}
#[derive(Debug, Clone)]
pub struct FocusState {
pub confidence: u32, pub preference: bool, pub decided: bool, pub decision: Decision, pub last_ratio: f64, }
impl Default for FocusState {
fn default() -> Self {
FocusState {
confidence: 0,
preference: false,
decided: false,
decision: Decision::Undecided,
last_ratio: 0.0,
}
}
}
pub type Verdict = Option<bool>;
pub fn accumulate(
preference: &mut bool,
confidence: &mut u32,
verdict: Verdict,
beta: u32,
) -> Option<Decision> {
match verdict {
Some(v) if *preference == v => *confidence += 1,
Some(v) => {
*preference = v;
*confidence = 1;
}
None => *confidence = 0,
}
if *confidence >= beta {
Some(if *preference {
Decision::Accept
} else {
Decision::Reject
})
} else {
None
}
}
impl<ID: Eq + std::hash::Hash + Clone> Focus<ID> {
pub fn new(threshold: u32, alpha: f64) -> Self {
Focus {
threshold,
alpha,
states: HashMap::new(),
}
}
pub fn update(&mut self, id: ID, yes_votes: usize, total_votes: usize) -> bool {
if total_votes == 0 {
return false;
}
let ratio = yes_votes as f64 / total_votes as f64;
let beta = self.threshold;
let alpha = self.alpha;
let state = self.states.entry(id).or_default();
if state.decided {
return false;
}
state.last_ratio = ratio;
let verdict = if ratio >= alpha {
Some(true)
} else if ratio <= 1.0 - alpha {
Some(false)
} else {
None
};
match accumulate(&mut state.preference, &mut state.confidence, verdict, beta) {
Some(decision) => {
state.decided = true;
state.decision = decision;
true
}
None => false,
}
}
pub fn state(&self, id: &ID) -> Option<&FocusState> {
self.states.get(id)
}
pub fn is_decided(&self, id: &ID) -> bool {
self.states.get(id).is_some_and(|s| s.decided)
}
pub fn decision(&self, id: &ID) -> Decision {
self.states.get(id).map_or(Decision::Undecided, |s| s.decision)
}
pub fn confidence(&self, id: &ID) -> u32 {
self.states.get(id).map_or(0, |s| s.confidence)
}
pub fn reset(&mut self, id: &ID) {
self.states.remove(id);
}
}
pub struct WindowedFocus<ID: Eq + std::hash::Hash + Clone> {
inner: Focus<ID>,
window: Duration,
last_update: HashMap<ID, Instant>,
}
impl<ID: Eq + std::hash::Hash + Clone> WindowedFocus<ID> {
pub fn new(threshold: u32, alpha: f64, window: Duration) -> Self {
WindowedFocus {
inner: Focus::new(threshold, alpha),
window,
last_update: HashMap::new(),
}
}
pub fn update(&mut self, id: ID, yes_votes: usize, total_votes: usize) -> bool {
let now = Instant::now();
if let Some(&last) = self.last_update.get(&id) {
if now.duration_since(last) > self.window {
self.inner.reset(&id);
}
}
self.last_update.insert(id.clone(), now);
self.inner.update(id, yes_votes, total_votes)
}
pub fn is_decided(&self, id: &ID) -> bool {
self.inner.is_decided(id)
}
pub fn decision(&self, id: &ID) -> Decision {
self.inner.decision(id)
}
}
}
pub mod wave {
use super::*;
#[derive(Debug, Clone)]
pub struct WaveState {
pub votes: Vec<Vote>,
pub yes_count: usize,
pub no_count: usize,
pub preference: bool,
pub confidence: u32,
pub decided: bool,
pub decision: Decision,
}
impl Default for WaveState {
fn default() -> Self {
WaveState {
votes: Vec::new(),
yes_count: 0,
no_count: 0,
preference: false,
confidence: 0,
decided: false,
decision: Decision::Undecided,
}
}
}
pub struct Wave {
config: QuasarConfig,
fpc: Option<FpcSelector>,
phase: u64,
states: HashMap<ID, WaveState>,
}
impl Wave {
pub fn new(config: QuasarConfig) -> Self {
let fpc = if config.enable_fpc {
Some(FpcSelector::new(
config.theta_min,
config.theta_max,
config.fpc_seed,
))
} else {
None
};
Wave {
config,
fpc,
phase: 0,
states: HashMap::new(),
}
}
pub fn get_or_create_state(&mut self, block_id: &ID) -> &mut WaveState {
self.states.entry(block_id.clone()).or_default()
}
pub fn record_vote(&mut self, vote: Vote) -> bool {
let block_id = vote.block_id.clone();
let state = self.states.entry(block_id.clone())
.or_default();
if state.decided {
return false;
}
if state.votes.iter().any(|v| v.voter == vote.voter) {
return false;
}
if vote.prefer() {
state.yes_count += 1;
} else {
state.no_count += 1;
}
state.votes.push(vote);
self.check_consensus(&block_id)
}
fn check_consensus(&mut self, block_id: &ID) -> bool {
self.advance_phase();
let threshold = self.threshold();
let (k, beta) = (self.config.k, self.config.beta);
let state = match self.states.get_mut(block_id) {
Some(s) => s,
None => return false,
};
if state.decided {
return false;
}
if state.yes_count + state.no_count < k {
return false;
}
let verdict = if state.yes_count >= threshold {
Some(true)
} else if state.no_count >= threshold {
Some(false)
} else {
None
};
match crate::focus::accumulate(
&mut state.preference,
&mut state.confidence,
verdict,
beta,
) {
Some(decision) => {
state.decided = true;
state.decision = decision;
true
}
None => false,
}
}
pub fn threshold(&self) -> usize {
match self.fpc {
Some(ref fpc) => fpc.select_threshold(self.phase, self.config.k),
None => self.config.alpha_count(),
}
}
fn advance_phase(&mut self) {
if self.fpc.is_some() {
self.phase += 1;
}
}
pub fn state(&self, block_id: &ID) -> Option<&WaveState> {
self.states.get(block_id)
}
pub fn is_decided(&self, block_id: &ID) -> bool {
self.states.get(block_id).is_some_and(|s| s.decided)
}
pub fn decision(&self, block_id: &ID) -> Decision {
self.states.get(block_id).map_or(Decision::Undecided, |s| s.decision)
}
pub fn reset(&mut self, block_id: &ID) {
self.states.remove(block_id);
}
pub fn phase(&self) -> u64 {
self.phase
}
}
}
pub mod quasar {
use super::*;
use crate::cert::{ValidatorSet, Vote as CertVote, VoteVerifier};
pub struct QuasarConsensus {
validators: ValidatorSet,
threshold: usize,
finalized: HashMap<ID, Certificate>,
}
impl QuasarConsensus {
pub fn new(config: &QuasarConfig) -> Self {
QuasarConsensus {
validators: ValidatorSet::new(),
threshold: config.alpha_count(),
finalized: HashMap::new(),
}
}
pub fn add_validator(&mut self, id: NodeID, weight: u64) -> Result<()> {
self.validators
.insert_unkeyed(*id.as_bytes(), weight)
.map_err(|e| ConsensusError::CryptoError(format!("{e:?}")))
}
pub fn add_validator_with_key(
&mut self,
id: NodeID,
weight: u64,
bls_pubkey: &[u8],
pop: &[u8],
) -> Result<()> {
self.validators
.insert(*id.as_bytes(), weight, bls_pubkey, pop)
.map_err(|e| ConsensusError::CryptoError(format!("{e:?}")))
}
pub fn remove_validator(&mut self, id: &NodeID) {
self.validators.remove(id.as_bytes());
}
pub fn validator_count(&self) -> usize {
self.validators.len()
}
pub fn is_validator(&self, id: &NodeID) -> bool {
self.validators.contains(id.as_bytes())
}
pub fn validators(&self) -> &ValidatorSet {
&self.validators
}
pub fn has_quorum(&self) -> bool {
self.validators.len() >= self.threshold
}
pub fn create_certificate(
&mut self,
position: Position,
votes: &[Vote],
) -> Result<Certificate> {
let message = canonical_vote_message(&position, true);
let mut accepted: Vec<CertVote> = Vec::new();
let mut seen: std::collections::HashSet<crate::pop::NodeId> =
std::collections::HashSet::new();
for v in votes.iter().filter(|v| v.prefer()) {
let id = *v.voter.as_bytes();
if !seen.insert(id) {
continue;
}
if !self.validators.verify_vote(&id, &message, &v.signature, position.height) {
continue;
}
accepted.push(CertVote {
node_id: id,
accept: true,
signature: v.signature.clone(),
});
}
if accepted.len() < self.threshold {
return Err(ConsensusError::NoQuorum);
}
let key = ID::from(position.signed_identity());
let cert = Certificate::assemble(
Finality::Quasar,
position,
self.threshold as u32,
&accepted,
)
.map_err(|e| ConsensusError::CryptoError(e.to_string()))?;
cert.verify_weighted(&self.validators, &self.validators, 0)
.map_err(|_| ConsensusError::NoQuorum)?;
self.finalized.insert(key, cert.clone());
Ok(cert)
}
pub fn verify_certificate(&self, cert: &Certificate) -> bool {
cert.verify_weighted(&self.validators, &self.validators, 0).is_ok()
}
pub fn is_finalized(&self, signed_identity: &ID) -> bool {
self.finalized.contains_key(signed_identity)
}
pub fn get_certificate(&self, signed_identity: &ID) -> Option<&Certificate> {
self.finalized.get(signed_identity)
}
}
pub struct EventHorizon {
quasar: QuasarConsensus,
chains: HashMap<String, Vec<ID>>,
height: u64,
}
impl EventHorizon {
pub fn new(config: &QuasarConfig) -> Self {
EventHorizon {
quasar: QuasarConsensus::new(config),
chains: HashMap::new(),
height: 0,
}
}
pub fn register_chain(&mut self, chain_id: String) {
self.chains.entry(chain_id).or_default();
}
pub fn accept_block(&mut self, chain_id: &str, block_id: ID) {
if let Some(blocks) = self.chains.get_mut(chain_id) {
blocks.push(block_id);
self.height += 1;
}
}
pub fn height(&self) -> u64 {
self.height
}
pub fn quasar(&self) -> &QuasarConsensus {
&self.quasar
}
pub fn quasar_mut(&mut self) -> &mut QuasarConsensus {
&mut self.quasar
}
}
}
pub mod engine {
use super::*;
pub trait Engine {
fn add(&mut self, block: Block) -> Result<()>;
fn record_vote(&mut self, vote: Vote) -> Result<()>;
fn record_votes_batch(&mut self, votes: Vec<Vote>) -> usize;
fn is_accepted(&self, id: &ID) -> bool;
fn get_status(&self, id: &ID) -> Status;
fn start(&mut self) -> Result<()>;
fn stop(&mut self) -> Result<()>;
}
pub struct QuasarEngine {
config: QuasarConfig,
wave: Wave,
quasar: QuasarConsensus,
blocks: Arc<RwLock<HashMap<ID, Block>>>,
status: Arc<RwLock<HashMap<ID, Status>>>,
started: Arc<RwLock<bool>>,
height: Arc<RwLock<u64>>,
}
impl QuasarEngine {
pub fn new(config: QuasarConfig) -> Self {
let wave = Wave::new(config.clone());
let quasar = QuasarConsensus::new(&config);
QuasarEngine {
config,
wave,
quasar,
blocks: Arc::new(RwLock::new(HashMap::new())),
status: Arc::new(RwLock::new(HashMap::new())),
started: Arc::new(RwLock::new(false)),
height: Arc::new(RwLock::new(0)),
}
}
pub fn testnet() -> Self {
QuasarEngine::new(QuasarConfig::testnet())
}
pub fn mainnet() -> Self {
QuasarEngine::new(QuasarConfig::mainnet())
}
pub fn add_validator(&mut self, id: NodeID, weight: u64) -> Result<()> {
self.quasar.add_validator(id, weight)
}
pub fn config(&self) -> &QuasarConfig {
&self.config
}
pub fn height(&self) -> u64 {
*self.height.read().unwrap()
}
fn accept_block(&mut self, block_id: &ID) {
let mut status = self.status.write().unwrap();
status.insert(block_id.clone(), Status::Accepted);
let blocks = self.blocks.read().unwrap();
if let Some(block) = blocks.get(block_id) {
let mut height = self.height.write().unwrap();
if block.height > *height {
*height = block.height;
}
}
let position = {
let blocks = self.blocks.read().unwrap();
blocks.get(block_id).map(|block| Position {
height: block.height,
block_id: *block_id.as_bytes(),
parent_id: *block.parent_id.as_bytes(),
..Position::default()
})
};
if let (Some(position), Some(votes)) =
(position, self.wave.state(block_id).map(|s| s.votes.clone()))
{
let _ = self.quasar.create_certificate(position, &votes);
}
}
}
impl Default for QuasarEngine {
fn default() -> Self {
QuasarEngine::new(QuasarConfig::default())
}
}
impl Engine for QuasarEngine {
fn add(&mut self, block: Block) -> Result<()> {
if !*self.started.read().unwrap() {
return Err(ConsensusError::NotInitialized);
}
let id = block.id.clone();
{
let mut blocks = self.blocks.write().unwrap();
blocks.insert(id.clone(), block);
}
{
let mut status = self.status.write().unwrap();
status.insert(id.clone(), Status::Processing);
}
self.wave.get_or_create_state(&id);
Ok(())
}
fn record_vote(&mut self, vote: Vote) -> Result<()> {
if !*self.started.read().unwrap() {
return Err(ConsensusError::NotInitialized);
}
{
let blocks = self.blocks.read().unwrap();
if !blocks.contains_key(&vote.block_id) {
return Err(ConsensusError::BlockNotFound);
}
}
if !self.quasar.is_validator(&vote.voter) {
return Err(ConsensusError::NotValidator);
}
let block_id = vote.block_id.clone();
let decided = self.wave.record_vote(vote);
if decided {
let decision = self.wave.decision(&block_id);
match decision {
Decision::Accept => self.accept_block(&block_id),
Decision::Reject => {
let mut status = self.status.write().unwrap();
status.insert(block_id, Status::Rejected);
}
Decision::Undecided => {}
}
}
Ok(())
}
fn record_votes_batch(&mut self, votes: Vec<Vote>) -> usize {
let mut success_count = 0;
for vote in votes {
if self.record_vote(vote).is_ok() {
success_count += 1;
}
}
success_count
}
fn is_accepted(&self, id: &ID) -> bool {
self.status.read().unwrap()
.get(id)
.is_some_and(|s| *s == Status::Accepted)
}
fn get_status(&self, id: &ID) -> Status {
self.status.read().unwrap()
.get(id)
.copied()
.unwrap_or(Status::Unknown)
}
fn start(&mut self) -> Result<()> {
let mut started = self.started.write().unwrap();
if *started {
return Err(ConsensusError::AlreadyStarted);
}
let genesis = Block::genesis();
{
let mut blocks = self.blocks.write().unwrap();
blocks.insert(genesis.id.clone(), genesis.clone());
}
{
let mut status = self.status.write().unwrap();
status.insert(genesis.id, Status::Accepted);
}
*started = true;
Ok(())
}
fn stop(&mut self) -> Result<()> {
let mut started = self.started.write().unwrap();
*started = false;
Ok(())
}
}
}
pub fn quick_start() -> Result<QuasarEngine> {
let mut engine = QuasarEngine::default();
engine.start()?;
Ok(engine)
}
pub fn new_block(id: ID, parent_id: ID, height: u64, payload: Vec<u8>) -> Block {
Block::new(id, parent_id, height, payload)
}
pub fn new_vote(block_id: ID, vote_type: VoteType, voter: NodeID) -> Vote {
Vote::new(block_id, vote_type, voter)
}
pub fn generate_block_id() -> ID {
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or_default();
let seed = now.as_nanos() as u64;
let mut state = seed;
let mut bytes = [0u8; 32];
for i in 0..4 {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
let chunk = state.to_le_bytes();
bytes[i*8..(i+1)*8].copy_from_slice(&chunk);
}
ID::new(bytes)
}
pub fn version() -> &'static str {
env!("CARGO_PKG_VERSION")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fpc_selector() {
let fpc = FpcSelector::default();
let theta1 = fpc.theta(100);
let theta2 = fpc.theta(100);
assert_eq!(theta1, theta2);
let theta3 = fpc.theta(101);
assert_ne!(theta1, theta3);
for phase in 0..1000 {
let theta = fpc.theta(phase);
assert!((0.5..=0.8).contains(&theta), "theta {} out of range", theta);
}
}
#[test]
fn test_fpc_threshold() {
let fpc = FpcSelector::new(0.5, 0.8, *b"test-seed-0000000000000000000000");
let k = 20;
let threshold = fpc.select_threshold(0, k);
assert!((10..=16).contains(&threshold));
}
#[test]
fn test_luminance() {
let config = QuasarConfig::testnet();
let mut luminance = photon::Luminance::new(&config);
let node = NodeID::from([1u8; 20]);
assert_eq!(luminance.brightness(&node), 1.0);
luminance.illuminate(&node, true);
assert!(luminance.brightness(&node) > 1.0);
let bright_before = luminance.brightness(&node);
luminance.illuminate(&node, false);
assert!(luminance.brightness(&node) < bright_before);
}
#[test]
fn test_focus_confidence() {
let mut focus: focus::Focus<ID> = focus::Focus::new(5, 0.6);
let block_id = ID::from([1u8; 32]);
assert!(!focus.is_decided(&block_id));
for _ in 0..5 {
focus.update(block_id.clone(), 7, 10); }
assert!(focus.is_decided(&block_id));
assert_eq!(focus.decision(&block_id), Decision::Accept);
}
#[test]
fn test_wave_voting() {
let config = QuasarConfig::testnet(); let mut wave = wave::Wave::new(config);
let block_id = ID::from([1u8; 32]);
for i in 0..5 {
let vote = Vote::new(
block_id.clone(),
VoteType::Preference,
NodeID::from([i; 20]),
);
wave.record_vote(vote);
}
let state = wave.state(&block_id).unwrap();
assert_eq!(state.yes_count, 5);
}
#[test]
fn test_quasar_engine() {
let config = QuasarConfig::testnet();
let mut engine = QuasarEngine::new(config);
engine.start().unwrap();
for i in 0..5 {
engine.add_validator(NodeID::from([i; 20]), 1).unwrap();
}
let block = Block::new(
ID::from([1u8; 32]),
ID::zero(),
1,
b"test".to_vec(),
);
engine.add(block.clone()).unwrap();
for i in 0..5 {
let vote = Vote::new(
block.id.clone(),
VoteType::Preference,
NodeID::from([i; 20]),
);
engine.record_vote(vote).unwrap();
}
let status = engine.get_status(&block.id);
assert!(status == Status::Processing || status == Status::Accepted);
engine.stop().unwrap();
}
#[test]
fn test_full_consensus_flow() {
let config = QuasarConfig::testnet();
let mut engine = QuasarEngine::new(config.clone());
engine.start().unwrap();
for i in 0..10 {
engine.add_validator(NodeID::from([i; 20]), 1).unwrap();
}
let blocks: Vec<Block> = (1..=3).map(|height| {
let mut id = [0u8; 32];
id[0] = height as u8;
let mut parent_id = [0u8; 32];
if height > 1 {
parent_id[0] = (height - 1) as u8;
}
Block::new(ID::from(id), ID::from(parent_id), height, vec![])
}).collect();
for block in &blocks {
engine.add(block.clone()).unwrap();
}
for block in &blocks {
for i in 0..5 {
let vote = Vote::new(
block.id.clone(),
VoteType::Preference,
NodeID::from([i; 20]),
);
engine.record_vote(vote).unwrap();
}
}
for block in &blocks {
let status = engine.get_status(&block.id);
assert!(
status == Status::Accepted || status == Status::Processing,
"Block {} has unexpected status {:?}",
block.height,
status
);
}
engine.stop().unwrap();
}
#[test]
fn test_configs() {
let default = QuasarConfig::default();
assert_eq!(default.alpha, 0.69);
assert_eq!(default.k, 20);
assert_eq!(default.beta, 20);
assert!(default.quantum_resistant);
let testnet = QuasarConfig::testnet();
assert_eq!(testnet.alpha, 0.6);
assert_eq!(testnet.k, 5);
assert!(!testnet.quantum_resistant);
let mainnet = QuasarConfig::mainnet();
assert_eq!(mainnet.alpha, 0.69);
assert_eq!(mainnet.k, 21);
assert!(mainnet.quantum_resistant);
}
}