1use std::collections::HashMap;
62use std::sync::{Arc, RwLock};
63use std::time::{Duration, Instant, SystemTime};
64
65
66pub mod finality;
69
70pub mod cert;
73pub mod pop;
74
75pub mod vote;
79
80pub mod zap;
84
85pub fn sha256(input: &[u8]) -> [u8; 32] {
91 let mut out = [0u8; 32];
92 unsafe { blst::blst_sha256(out.as_mut_ptr(), input.as_ptr(), input.len()) };
95 out
96}
97
98pub use crate::finality::{
100 canonical_vote_message, crash_tolerance, half_stake_floor, nova_beta, nova_quorum,
101 nova_signer_floor, two_thirds_count, two_thirds_stake_floor, weighted_quasar, Finality, Position,
102 QC_FINALITY, QUORUM_CERT_VERSION, VOTE_MESSAGE_LEN, VOTE_TAG,
103};
104pub use crate::types::*;
105pub use crate::errors::*;
106pub use crate::fpc::*;
107pub use crate::photon::*;
108pub use crate::focus::*;
109pub use crate::wave::*;
110pub use crate::quasar::*;
111pub use crate::engine::*;
112pub use crate::vote::{SignedVote, Slot, Tally, VoteTransport, VOTE};
113
114pub mod types {
117 use std::fmt;
118 use std::time::{Duration, SystemTime};
119
120 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
122 pub struct ID(pub [u8; 32]);
123
124 impl ID {
125 pub fn new(data: [u8; 32]) -> Self {
126 ID(data)
127 }
128
129 pub fn zero() -> Self {
130 ID([0u8; 32])
131 }
132
133 pub fn from_slice(data: &[u8]) -> Self {
134 let mut arr = [0u8; 32];
135 let len = data.len().min(32);
136 arr[..len].copy_from_slice(&data[..len]);
137 ID(arr)
138 }
139
140 pub fn to_vec(&self) -> Vec<u8> {
141 self.0.to_vec()
142 }
143
144 pub fn as_bytes(&self) -> &[u8; 32] {
145 &self.0
146 }
147 }
148
149 impl From<[u8; 32]> for ID {
150 fn from(data: [u8; 32]) -> Self {
151 ID(data)
152 }
153 }
154
155 impl From<Vec<u8>> for ID {
156 fn from(data: Vec<u8>) -> Self {
157 ID::from_slice(&data)
158 }
159 }
160
161 impl fmt::Display for ID {
162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 write!(f, "{}", hex::encode(self.0))
164 }
165 }
166
167 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
183 pub struct NodeID(pub crate::pop::NodeId);
184
185 impl NodeID {
186 pub fn as_bytes(&self) -> &crate::pop::NodeId {
187 &self.0
188 }
189 }
190
191 impl From<crate::pop::NodeId> for NodeID {
192 fn from(data: crate::pop::NodeId) -> Self {
193 NodeID(data)
194 }
195 }
196
197 impl fmt::Display for NodeID {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 write!(f, "{}", hex::encode(self.0))
200 }
201 }
202
203 pub type Hash = ID;
204
205 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
207 pub enum Status {
208 Unknown,
209 Processing,
210 Rejected,
211 Accepted,
212 }
213
214 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
216 pub enum Decision {
217 Undecided,
218 Accept,
219 Reject,
220 }
221
222 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
224 pub enum VoteType {
225 Preference, Commit, Cancel, }
229
230 #[derive(Debug, Clone)]
232 pub struct Block {
233 pub id: ID,
234 pub parent_id: ID,
235 pub height: u64,
236 pub payload: Vec<u8>,
237 pub timestamp: SystemTime,
238 }
239
240 impl Block {
241 pub fn new(id: ID, parent_id: ID, height: u64, payload: Vec<u8>) -> Self {
242 Block {
243 id,
244 parent_id,
245 height,
246 payload,
247 timestamp: SystemTime::now(),
248 }
249 }
250
251 pub fn genesis() -> Self {
252 Block {
253 id: ID::zero(),
254 parent_id: ID::zero(),
255 height: 0,
256 payload: Vec::new(),
257 timestamp: SystemTime::UNIX_EPOCH,
258 }
259 }
260 }
261
262 #[derive(Debug, Clone)]
264 pub struct Vote {
265 pub block_id: ID,
266 pub vote_type: VoteType,
267 pub voter: NodeID,
268 pub signature: Vec<u8>,
269 pub timestamp: SystemTime,
270 }
271
272 impl Vote {
273 pub fn new(block_id: ID, vote_type: VoteType, voter: NodeID) -> Self {
274 Vote {
275 block_id,
276 vote_type,
277 voter,
278 signature: Vec::new(),
279 timestamp: SystemTime::now(),
280 }
281 }
282
283 pub fn with_signature(mut self, signature: Vec<u8>) -> Self {
284 self.signature = signature;
285 self
286 }
287
288 pub fn prefer(&self) -> bool {
289 matches!(self.vote_type, VoteType::Preference | VoteType::Commit)
290 }
291 }
292
293 pub type Certificate = crate::cert::QuorumCert;
303
304 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
306 #[derive(Default)]
307 pub enum SecurityLevel {
308 Low = 2, #[default]
310 Medium = 3, High = 5, }
313
314
315
316 #[derive(Debug, Clone)]
318 pub struct QuasarConfig {
319 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,
340 pub max_message_size: usize,
341 pub max_outstanding: usize,
342
343 pub security_level: SecurityLevel,
345 pub quantum_resistant: bool,
346 pub gpu_acceleration: bool,
347 }
348
349 pub const DEFAULT_FPC_SEED: [u8; 32] = *b"lux-fpc-default-seed-00000000000";
357
358 pub const TESTNET_FPC_SEED: [u8; 32] = *b"lux-testnet-fpc-seed-00000000000";
360
361 pub const MAINNET_FPC_SEED: [u8; 32] = *b"lux-mainnet-fpc-secure-seed-2025";
363
364 impl Default for QuasarConfig {
368 fn default() -> Self {
369 QuasarConfig {
370 k: 20,
372 alpha: 0.69, beta: 20,
374 round_timeout: Duration::from_millis(100),
375
376 enable_fpc: true,
378 theta_min: 0.5,
379 theta_max: 0.8,
380 fpc_seed: DEFAULT_FPC_SEED,
381
382 base_luminance: 100.0,
384 max_luminance: 1000.0,
385 min_luminance: 10.0,
386 success_multiplier: 1.1,
387 failure_multiplier: 0.9,
388
389 network_timeout: Duration::from_secs(5),
391 max_message_size: 2 * 1024 * 1024, max_outstanding: 10,
393
394 security_level: SecurityLevel::Medium,
396 quantum_resistant: true,
397 gpu_acceleration: true,
398 }
399 }
400 }
401
402 impl QuasarConfig {
403 pub fn testnet() -> Self {
405 QuasarConfig {
406 k: 5,
407 alpha: 0.6,
408 beta: 5,
409 round_timeout: Duration::from_millis(50),
410 enable_fpc: false,
411 theta_max: 0.7,
412 fpc_seed: TESTNET_FPC_SEED,
413 max_luminance: 500.0,
414 min_luminance: 20.0,
415 success_multiplier: 1.05,
416 failure_multiplier: 0.95,
417 network_timeout: Duration::from_secs(10),
418 max_message_size: 1024 * 1024,
419 max_outstanding: 5,
420 security_level: SecurityLevel::Low,
421 quantum_resistant: false,
422 gpu_acceleration: false,
423 ..QuasarConfig::default()
424 }
425 }
426
427 pub fn mainnet() -> Self {
430 QuasarConfig {
431 k: 21,
432 fpc_seed: MAINNET_FPC_SEED,
433 security_level: SecurityLevel::High,
434 ..QuasarConfig::default()
435 }
436 }
437
438 pub fn alpha_count(&self) -> usize {
440 (self.alpha * self.k as f64).ceil() as usize
441 }
442 }
443}
444
445pub mod errors {
448 use std::error::Error;
449 use std::fmt;
450
451 #[derive(Debug)]
453 pub enum ConsensusError {
454 BlockNotFound,
455 InvalidBlock,
456 InvalidVote,
457 InvalidSignature,
458 NoQuorum,
459 AlreadyVoted,
460 NotValidator,
461 Timeout,
462 NotInitialized,
463 AlreadyStarted,
464 CryptoError(String),
465 NetworkError(String),
466 Other(String),
467 }
468
469 impl fmt::Display for ConsensusError {
470 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
471 match self {
472 ConsensusError::BlockNotFound => write!(f, "Block not found"),
473 ConsensusError::InvalidBlock => write!(f, "Invalid block"),
474 ConsensusError::InvalidVote => write!(f, "Invalid vote"),
475 ConsensusError::InvalidSignature => write!(f, "Invalid signature"),
476 ConsensusError::NoQuorum => write!(f, "No quorum reached"),
477 ConsensusError::AlreadyVoted => write!(f, "Already voted"),
478 ConsensusError::NotValidator => write!(f, "Not a validator"),
479 ConsensusError::Timeout => write!(f, "Operation timeout"),
480 ConsensusError::NotInitialized => write!(f, "Engine not initialized"),
481 ConsensusError::AlreadyStarted => write!(f, "Engine already started"),
482 ConsensusError::CryptoError(msg) => write!(f, "Crypto error: {}", msg),
483 ConsensusError::NetworkError(msg) => write!(f, "Network error: {}", msg),
484 ConsensusError::Other(msg) => write!(f, "{}", msg),
485 }
486 }
487 }
488
489 impl Error for ConsensusError {}
490
491 pub type Result<T> = std::result::Result<T, ConsensusError>;
493}
494
495pub mod fpc {
498 use super::*;
499
500 #[derive(Debug, Clone)]
505 pub struct FpcSelector {
506 theta_min: f64,
507 theta_max: f64,
508 seed: [u8; 32],
509 }
510
511 impl FpcSelector {
512 pub fn new(theta_min: f64, theta_max: f64, seed: [u8; 32]) -> Self {
514 let theta_min = if theta_min > 0.0 && theta_min < 1.0 {
515 theta_min
516 } else {
517 0.5
518 };
519 let theta_max = if theta_max > theta_min && theta_max <= 1.0 {
520 theta_max
521 } else {
522 0.8
523 };
524
525 FpcSelector {
526 theta_min,
527 theta_max,
528 seed,
529 }
530 }
531
532
533 fn compute_theta(&self, phase: u64) -> f64 {
543 let mut input = [0u8; 40];
544 input[..32].copy_from_slice(&self.seed);
545 input[32..40].copy_from_slice(&phase.to_be_bytes());
546
547 let hash = sha256(&input);
548
549 let hash_u64 = u64::from_be_bytes([
551 hash[0], hash[1], hash[2], hash[3],
552 hash[4], hash[5], hash[6], hash[7],
553 ]);
554 let normalized = (hash_u64 as f64) / (u64::MAX as f64);
555
556 self.theta_min + normalized * (self.theta_max - self.theta_min)
557 }
558
559 pub fn select_threshold(&self, phase: u64, k: usize) -> usize {
561 let theta = self.compute_theta(phase);
562 (theta * k as f64).ceil() as usize
563 }
564
565 pub fn theta(&self, phase: u64) -> f64 {
567 self.compute_theta(phase)
568 }
569
570 pub fn range(&self) -> (f64, f64) {
572 (self.theta_min, self.theta_max)
573 }
574 }
575
576 impl Default for FpcSelector {
579 fn default() -> Self {
580 let c = QuasarConfig::default();
581 FpcSelector::new(c.theta_min, c.theta_max, c.fpc_seed)
582 }
583 }
584}
585
586pub mod photon {
589 use super::*;
590
591 #[derive(Debug, Clone)]
599 pub struct Luminance {
600 lux: HashMap<NodeID, f64>,
601 base: f64,
602 max: f64,
603 min: f64,
604 success_mult: f64,
605 failure_mult: f64,
606 }
607
608 impl Luminance {
609 pub fn new(config: &QuasarConfig) -> Self {
611 Luminance {
612 lux: HashMap::new(),
613 base: config.base_luminance,
614 max: config.max_luminance,
615 min: config.min_luminance,
616 success_mult: config.success_multiplier,
617 failure_mult: config.failure_multiplier,
618 }
619 }
620
621 pub fn illuminate(&mut self, id: &NodeID, success: bool) {
623 let current = self.lux.entry(*id).or_insert(self.base);
624
625 if success {
626 *current *= self.success_mult;
627 if *current > self.max {
628 *current = self.max;
629 }
630 } else {
631 *current *= self.failure_mult;
632 if *current < self.min {
633 *current = self.min;
634 }
635 }
636 }
637
638 pub fn brightness(&self, id: &NodeID) -> f64 {
640 self.lux.get(id).copied().unwrap_or(self.base) / self.base
641 }
642
643 pub fn lux(&self, id: &NodeID) -> f64 {
645 self.lux.get(id).copied().unwrap_or(self.base)
646 }
647
648 pub fn total_luminance(&self) -> f64 {
650 self.lux.values().sum()
651 }
652
653 pub fn node_count(&self) -> usize {
655 self.lux.len()
656 }
657 }
658
659 impl Default for Luminance {
662 fn default() -> Self {
663 Luminance::new(&QuasarConfig::default())
664 }
665 }
666
667 pub struct PhotonSampler {
669 peers: Vec<NodeID>,
670 luminance: Luminance,
671 }
672
673 impl PhotonSampler {
674 pub fn new(peers: Vec<NodeID>, config: &QuasarConfig) -> Self {
676 PhotonSampler {
677 peers,
678 luminance: Luminance::new(config),
679 }
680 }
681
682 pub fn sample(&self, k: usize) -> Vec<NodeID> {
684 if self.peers.is_empty() {
685 return Vec::new();
686 }
687
688 let k = k.min(self.peers.len());
689
690 let weights: Vec<f64> = self.peers
692 .iter()
693 .map(|p| self.luminance.brightness(p))
694 .collect();
695
696 let total_weight: f64 = weights.iter().sum();
697 if total_weight == 0.0 {
698 return self.peers.iter().take(k).cloned().collect();
700 }
701
702 let mut selected = Vec::with_capacity(k);
704 let mut used = vec![false; self.peers.len()];
705
706 for i in 0..k {
707 let mut best_idx = 0;
708 let mut best_score = f64::MIN;
709
710 for (idx, &weight) in weights.iter().enumerate() {
711 if used[idx] {
712 continue;
713 }
714 let score = weight * ((idx + i + 1) as f64 / self.peers.len() as f64);
716 if score > best_score {
717 best_score = score;
718 best_idx = idx;
719 }
720 }
721
722 used[best_idx] = true;
723 selected.push(self.peers[best_idx]);
724 }
725
726 selected
727 }
728
729 pub fn update_luminance(&mut self, id: &NodeID, success: bool) {
731 self.luminance.illuminate(id, success);
732 }
733
734 pub fn add_peer(&mut self, peer: NodeID) {
736 if !self.peers.contains(&peer) {
737 self.peers.push(peer);
738 }
739 }
740
741 pub fn remove_peer(&mut self, peer: &NodeID) {
743 self.peers.retain(|p| p != peer);
744 }
745
746 pub fn luminance(&self) -> &Luminance {
748 &self.luminance
749 }
750 }
751}
752
753pub mod focus {
756 use super::*;
757
758 #[derive(Debug)]
763 pub struct Focus<ID: Eq + std::hash::Hash + Clone> {
764 threshold: u32, alpha: f64, states: HashMap<ID, FocusState>,
767 }
768
769 #[derive(Debug, Clone)]
771 pub struct FocusState {
772 pub confidence: u32, pub preference: bool, pub decided: bool, pub decision: Decision, pub last_ratio: f64, }
778
779 impl Default for FocusState {
780 fn default() -> Self {
781 FocusState {
782 confidence: 0,
783 preference: false,
784 decided: false,
785 decision: Decision::Undecided,
786 last_ratio: 0.0,
787 }
788 }
789 }
790
791 pub type Verdict = Option<bool>;
798
799 pub fn accumulate(
808 preference: &mut bool,
809 confidence: &mut u32,
810 verdict: Verdict,
811 beta: u32,
812 ) -> Option<Decision> {
813 match verdict {
814 Some(v) if *preference == v => *confidence += 1,
815 Some(v) => {
816 *preference = v;
817 *confidence = 1;
818 }
819 None => *confidence = 0,
820 }
821
822 if *confidence >= beta {
823 Some(if *preference {
824 Decision::Accept
825 } else {
826 Decision::Reject
827 })
828 } else {
829 None
830 }
831 }
832
833 impl<ID: Eq + std::hash::Hash + Clone> Focus<ID> {
834 pub fn new(threshold: u32, alpha: f64) -> Self {
836 Focus {
837 threshold,
838 alpha,
839 states: HashMap::new(),
840 }
841 }
842
843 pub fn update(&mut self, id: ID, yes_votes: usize, total_votes: usize) -> bool {
847 if total_votes == 0 {
848 return false;
849 }
850
851 let ratio = yes_votes as f64 / total_votes as f64;
852 let beta = self.threshold;
853 let alpha = self.alpha;
854 let state = self.states.entry(id).or_default();
855
856 if state.decided {
857 return false;
858 }
859
860 state.last_ratio = ratio;
861
862 let verdict = if ratio >= alpha {
864 Some(true)
865 } else if ratio <= 1.0 - alpha {
866 Some(false)
867 } else {
868 None
869 };
870
871 match accumulate(&mut state.preference, &mut state.confidence, verdict, beta) {
872 Some(decision) => {
873 state.decided = true;
874 state.decision = decision;
875 true
876 }
877 None => false,
878 }
879 }
880
881 pub fn state(&self, id: &ID) -> Option<&FocusState> {
883 self.states.get(id)
884 }
885
886 pub fn is_decided(&self, id: &ID) -> bool {
888 self.states.get(id).is_some_and(|s| s.decided)
889 }
890
891 pub fn decision(&self, id: &ID) -> Decision {
893 self.states.get(id).map_or(Decision::Undecided, |s| s.decision)
894 }
895
896 pub fn confidence(&self, id: &ID) -> u32 {
898 self.states.get(id).map_or(0, |s| s.confidence)
899 }
900
901 pub fn reset(&mut self, id: &ID) {
903 self.states.remove(id);
904 }
905 }
906
907 pub struct WindowedFocus<ID: Eq + std::hash::Hash + Clone> {
909 inner: Focus<ID>,
910 window: Duration,
911 last_update: HashMap<ID, Instant>,
912 }
913
914 impl<ID: Eq + std::hash::Hash + Clone> WindowedFocus<ID> {
915 pub fn new(threshold: u32, alpha: f64, window: Duration) -> Self {
916 WindowedFocus {
917 inner: Focus::new(threshold, alpha),
918 window,
919 last_update: HashMap::new(),
920 }
921 }
922
923 pub fn update(&mut self, id: ID, yes_votes: usize, total_votes: usize) -> bool {
925 let now = Instant::now();
926
927 if let Some(&last) = self.last_update.get(&id) {
929 if now.duration_since(last) > self.window {
930 self.inner.reset(&id);
931 }
932 }
933
934 self.last_update.insert(id.clone(), now);
935 self.inner.update(id, yes_votes, total_votes)
936 }
937
938 pub fn is_decided(&self, id: &ID) -> bool {
939 self.inner.is_decided(id)
940 }
941
942 pub fn decision(&self, id: &ID) -> Decision {
943 self.inner.decision(id)
944 }
945 }
946}
947
948pub mod wave {
951 use super::*;
952
953 #[derive(Debug, Clone)]
955 pub struct WaveState {
956 pub votes: Vec<Vote>,
957 pub yes_count: usize,
958 pub no_count: usize,
959 pub preference: bool,
960 pub confidence: u32,
961 pub decided: bool,
962 pub decision: Decision,
963 }
964
965 impl Default for WaveState {
966 fn default() -> Self {
967 WaveState {
968 votes: Vec::new(),
969 yes_count: 0,
970 no_count: 0,
971 preference: false,
972 confidence: 0,
973 decided: false,
974 decision: Decision::Undecided,
975 }
976 }
977 }
978
979 pub struct Wave {
981 config: QuasarConfig,
982 fpc: Option<FpcSelector>,
983 phase: u64,
984 states: HashMap<ID, WaveState>,
985 }
986
987 impl Wave {
988 pub fn new(config: QuasarConfig) -> Self {
990 let fpc = if config.enable_fpc {
991 Some(FpcSelector::new(
992 config.theta_min,
993 config.theta_max,
994 config.fpc_seed,
995 ))
996 } else {
997 None
998 };
999
1000 Wave {
1001 config,
1002 fpc,
1003 phase: 0,
1004 states: HashMap::new(),
1005 }
1006 }
1007
1008 pub fn get_or_create_state(&mut self, block_id: &ID) -> &mut WaveState {
1010 self.states.entry(block_id.clone()).or_default()
1011 }
1012
1013 pub fn record_vote(&mut self, vote: Vote) -> bool {
1017 let block_id = vote.block_id.clone();
1018
1019 let state = self.states.entry(block_id.clone())
1020 .or_default();
1021
1022 if state.decided {
1023 return false;
1024 }
1025
1026 if state.votes.iter().any(|v| v.voter == vote.voter) {
1028 return false;
1029 }
1030
1031 if vote.prefer() {
1033 state.yes_count += 1;
1034 } else {
1035 state.no_count += 1;
1036 }
1037
1038 state.votes.push(vote);
1039
1040 self.check_consensus(&block_id)
1042 }
1043
1044 fn check_consensus(&mut self, block_id: &ID) -> bool {
1046 self.advance_phase();
1047 let threshold = self.threshold();
1048 let (k, beta) = (self.config.k, self.config.beta);
1049
1050 let state = match self.states.get_mut(block_id) {
1051 Some(s) => s,
1052 None => return false,
1053 };
1054
1055 if state.decided {
1056 return false;
1057 }
1058
1059 if state.yes_count + state.no_count < k {
1061 return false;
1062 }
1063
1064 let verdict = if state.yes_count >= threshold {
1066 Some(true)
1067 } else if state.no_count >= threshold {
1068 Some(false)
1069 } else {
1070 None
1071 };
1072
1073 match crate::focus::accumulate(
1074 &mut state.preference,
1075 &mut state.confidence,
1076 verdict,
1077 beta,
1078 ) {
1079 Some(decision) => {
1080 state.decided = true;
1081 state.decision = decision;
1082 true
1083 }
1084 None => false,
1085 }
1086 }
1087
1088 pub fn threshold(&self) -> usize {
1093 match self.fpc {
1094 Some(ref fpc) => fpc.select_threshold(self.phase, self.config.k),
1095 None => self.config.alpha_count(),
1096 }
1097 }
1098
1099 fn advance_phase(&mut self) {
1107 if self.fpc.is_some() {
1108 self.phase += 1;
1109 }
1110 }
1111
1112 pub fn state(&self, block_id: &ID) -> Option<&WaveState> {
1114 self.states.get(block_id)
1115 }
1116
1117 pub fn is_decided(&self, block_id: &ID) -> bool {
1119 self.states.get(block_id).is_some_and(|s| s.decided)
1120 }
1121
1122 pub fn decision(&self, block_id: &ID) -> Decision {
1124 self.states.get(block_id).map_or(Decision::Undecided, |s| s.decision)
1125 }
1126
1127 pub fn reset(&mut self, block_id: &ID) {
1129 self.states.remove(block_id);
1130 }
1131
1132 pub fn phase(&self) -> u64 {
1134 self.phase
1135 }
1136 }
1137}
1138
1139pub mod quasar {
1142 use super::*;
1143 use crate::cert::{ValidatorSet, Vote as CertVote, VoteVerifier};
1144
1145 pub struct QuasarConsensus {
1152 validators: ValidatorSet,
1153 threshold: usize,
1154 finalized: HashMap<ID, Certificate>,
1159 }
1160
1161 impl QuasarConsensus {
1162 pub fn new(config: &QuasarConfig) -> Self {
1164 QuasarConsensus {
1165 validators: ValidatorSet::new(),
1166 threshold: config.alpha_count(),
1167 finalized: HashMap::new(),
1168 }
1169 }
1170
1171 pub fn add_validator(&mut self, id: NodeID, weight: u64) -> Result<()> {
1178 self.validators
1179 .insert_unkeyed(*id.as_bytes(), weight)
1180 .map_err(|e| ConsensusError::CryptoError(format!("{e:?}")))
1181 }
1182
1183 pub fn add_validator_with_key(
1192 &mut self,
1193 id: NodeID,
1194 weight: u64,
1195 bls_pubkey: &[u8],
1196 pop: &[u8],
1197 ) -> Result<()> {
1198 self.validators
1199 .insert(*id.as_bytes(), weight, bls_pubkey, pop)
1200 .map_err(|e| ConsensusError::CryptoError(format!("{e:?}")))
1201 }
1202
1203 pub fn remove_validator(&mut self, id: &NodeID) {
1205 self.validators.remove(id.as_bytes());
1206 }
1207
1208 pub fn validator_count(&self) -> usize {
1210 self.validators.len()
1211 }
1212
1213 pub fn is_validator(&self, id: &NodeID) -> bool {
1215 self.validators.contains(id.as_bytes())
1216 }
1217
1218 pub fn validators(&self) -> &ValidatorSet {
1220 &self.validators
1221 }
1222
1223 pub fn has_quorum(&self) -> bool {
1225 self.validators.len() >= self.threshold
1226 }
1227
1228 pub fn create_certificate(
1242 &mut self,
1243 position: Position,
1244 votes: &[Vote],
1245 ) -> Result<Certificate> {
1246 let message = canonical_vote_message(&position, true);
1247
1248 let mut accepted: Vec<CertVote> = Vec::new();
1249 let mut seen: std::collections::HashSet<crate::pop::NodeId> =
1250 std::collections::HashSet::new();
1251
1252 for v in votes.iter().filter(|v| v.prefer()) {
1253 let id = *v.voter.as_bytes();
1254 if !seen.insert(id) {
1255 continue;
1256 }
1257 if !self.validators.verify_vote(&id, &message, &v.signature, position.height) {
1258 continue;
1259 }
1260 accepted.push(CertVote {
1261 node_id: id,
1262 accept: true,
1263 signature: v.signature.clone(),
1264 });
1265 }
1266
1267 if accepted.len() < self.threshold {
1268 return Err(ConsensusError::NoQuorum);
1269 }
1270
1271 let key = ID::from(position.signed_identity());
1276 let cert = Certificate::assemble(
1279 Finality::Quasar,
1280 position,
1281 self.threshold as u32,
1282 &accepted,
1283 )
1284 .map_err(|e| ConsensusError::CryptoError(e.to_string()))?;
1285
1286 cert.verify_weighted(&self.validators, &self.validators, 0)
1291 .map_err(|_| ConsensusError::NoQuorum)?;
1292
1293 self.finalized.insert(key, cert.clone());
1294 Ok(cert)
1295 }
1296
1297 pub fn verify_certificate(&self, cert: &Certificate) -> bool {
1315 cert.verify_weighted(&self.validators, &self.validators, 0).is_ok()
1316 }
1317
1318 pub fn is_finalized(&self, signed_identity: &ID) -> bool {
1323 self.finalized.contains_key(signed_identity)
1324 }
1325
1326 pub fn get_certificate(&self, signed_identity: &ID) -> Option<&Certificate> {
1329 self.finalized.get(signed_identity)
1330 }
1331 }
1332
1333 pub struct EventHorizon {
1335 quasar: QuasarConsensus,
1336 chains: HashMap<String, Vec<ID>>,
1337 height: u64,
1338 }
1339
1340 impl EventHorizon {
1341 pub fn new(config: &QuasarConfig) -> Self {
1342 EventHorizon {
1343 quasar: QuasarConsensus::new(config),
1344 chains: HashMap::new(),
1345 height: 0,
1346 }
1347 }
1348
1349 pub fn register_chain(&mut self, chain_id: String) {
1351 self.chains.entry(chain_id).or_default();
1352 }
1353
1354 pub fn accept_block(&mut self, chain_id: &str, block_id: ID) {
1356 if let Some(blocks) = self.chains.get_mut(chain_id) {
1357 blocks.push(block_id);
1358 self.height += 1;
1359 }
1360 }
1361
1362 pub fn height(&self) -> u64 {
1364 self.height
1365 }
1366
1367 pub fn quasar(&self) -> &QuasarConsensus {
1369 &self.quasar
1370 }
1371
1372 pub fn quasar_mut(&mut self) -> &mut QuasarConsensus {
1374 &mut self.quasar
1375 }
1376 }
1377}
1378
1379pub mod engine {
1382 use super::*;
1383
1384 pub trait Engine {
1386 fn add(&mut self, block: Block) -> Result<()>;
1387 fn record_vote(&mut self, vote: Vote) -> Result<()>;
1388 fn record_votes_batch(&mut self, votes: Vec<Vote>) -> usize;
1389 fn is_accepted(&self, id: &ID) -> bool;
1390 fn get_status(&self, id: &ID) -> Status;
1391 fn start(&mut self) -> Result<()>;
1392 fn stop(&mut self) -> Result<()>;
1393 }
1394
1395 pub struct QuasarEngine {
1400 config: QuasarConfig,
1401 wave: Wave,
1402 quasar: QuasarConsensus,
1403 blocks: Arc<RwLock<HashMap<ID, Block>>>,
1404 status: Arc<RwLock<HashMap<ID, Status>>>,
1405 started: Arc<RwLock<bool>>,
1406 height: Arc<RwLock<u64>>,
1407 }
1408
1409 impl QuasarEngine {
1410 pub fn new(config: QuasarConfig) -> Self {
1412 let wave = Wave::new(config.clone());
1413 let quasar = QuasarConsensus::new(&config);
1414
1415 QuasarEngine {
1416 config,
1417 wave,
1418 quasar,
1419 blocks: Arc::new(RwLock::new(HashMap::new())),
1420 status: Arc::new(RwLock::new(HashMap::new())),
1421 started: Arc::new(RwLock::new(false)),
1422 height: Arc::new(RwLock::new(0)),
1423 }
1424 }
1425
1426 pub fn testnet() -> Self {
1428 QuasarEngine::new(QuasarConfig::testnet())
1429 }
1430
1431 pub fn mainnet() -> Self {
1433 QuasarEngine::new(QuasarConfig::mainnet())
1434 }
1435
1436 pub fn add_validator(&mut self, id: NodeID, weight: u64) -> Result<()> {
1439 self.quasar.add_validator(id, weight)
1440 }
1441
1442 pub fn config(&self) -> &QuasarConfig {
1444 &self.config
1445 }
1446
1447 pub fn height(&self) -> u64 {
1449 *self.height.read().unwrap()
1450 }
1451
1452 fn accept_block(&mut self, block_id: &ID) {
1454 let mut status = self.status.write().unwrap();
1455 status.insert(block_id.clone(), Status::Accepted);
1456
1457 let blocks = self.blocks.read().unwrap();
1458 if let Some(block) = blocks.get(block_id) {
1459 let mut height = self.height.write().unwrap();
1460 if block.height > *height {
1461 *height = block.height;
1462 }
1463 }
1464
1465 let position = {
1469 let blocks = self.blocks.read().unwrap();
1470 blocks.get(block_id).map(|block| Position {
1471 height: block.height,
1472 block_id: *block_id.as_bytes(),
1473 parent_id: *block.parent_id.as_bytes(),
1474 ..Position::default()
1475 })
1476 };
1477 if let (Some(position), Some(votes)) =
1478 (position, self.wave.state(block_id).map(|s| s.votes.clone()))
1479 {
1480 let _ = self.quasar.create_certificate(position, &votes);
1481 }
1482 }
1483 }
1484
1485 impl Default for QuasarEngine {
1488 fn default() -> Self {
1489 QuasarEngine::new(QuasarConfig::default())
1490 }
1491 }
1492
1493 impl Engine for QuasarEngine {
1494 fn add(&mut self, block: Block) -> Result<()> {
1495 if !*self.started.read().unwrap() {
1496 return Err(ConsensusError::NotInitialized);
1497 }
1498
1499 let id = block.id.clone();
1500
1501 {
1502 let mut blocks = self.blocks.write().unwrap();
1503 blocks.insert(id.clone(), block);
1504 }
1505
1506 {
1507 let mut status = self.status.write().unwrap();
1508 status.insert(id.clone(), Status::Processing);
1509 }
1510
1511 self.wave.get_or_create_state(&id);
1513
1514 Ok(())
1515 }
1516
1517 fn record_vote(&mut self, vote: Vote) -> Result<()> {
1518 if !*self.started.read().unwrap() {
1519 return Err(ConsensusError::NotInitialized);
1520 }
1521
1522 {
1524 let blocks = self.blocks.read().unwrap();
1525 if !blocks.contains_key(&vote.block_id) {
1526 return Err(ConsensusError::BlockNotFound);
1527 }
1528 }
1529
1530 if !self.quasar.is_validator(&vote.voter) {
1536 return Err(ConsensusError::NotValidator);
1537 }
1538
1539 let block_id = vote.block_id.clone();
1540
1541 let decided = self.wave.record_vote(vote);
1543
1544 if decided {
1546 let decision = self.wave.decision(&block_id);
1547 match decision {
1548 Decision::Accept => self.accept_block(&block_id),
1549 Decision::Reject => {
1550 let mut status = self.status.write().unwrap();
1551 status.insert(block_id, Status::Rejected);
1552 }
1553 Decision::Undecided => {}
1554 }
1555 }
1556
1557 Ok(())
1558 }
1559
1560 fn record_votes_batch(&mut self, votes: Vec<Vote>) -> usize {
1561 let mut success_count = 0;
1562 for vote in votes {
1563 if self.record_vote(vote).is_ok() {
1564 success_count += 1;
1565 }
1566 }
1567 success_count
1568 }
1569
1570 fn is_accepted(&self, id: &ID) -> bool {
1571 self.status.read().unwrap()
1572 .get(id)
1573 .is_some_and(|s| *s == Status::Accepted)
1574 }
1575
1576 fn get_status(&self, id: &ID) -> Status {
1577 self.status.read().unwrap()
1578 .get(id)
1579 .copied()
1580 .unwrap_or(Status::Unknown)
1581 }
1582
1583 fn start(&mut self) -> Result<()> {
1584 let mut started = self.started.write().unwrap();
1585 if *started {
1586 return Err(ConsensusError::AlreadyStarted);
1587 }
1588
1589 let genesis = Block::genesis();
1591 {
1592 let mut blocks = self.blocks.write().unwrap();
1593 blocks.insert(genesis.id.clone(), genesis.clone());
1594 }
1595 {
1596 let mut status = self.status.write().unwrap();
1597 status.insert(genesis.id, Status::Accepted);
1598 }
1599
1600 *started = true;
1601 Ok(())
1602 }
1603
1604 fn stop(&mut self) -> Result<()> {
1605 let mut started = self.started.write().unwrap();
1606 *started = false;
1607 Ok(())
1608 }
1609 }
1610
1611}
1612
1613pub fn quick_start() -> Result<QuasarEngine> {
1617 let mut engine = QuasarEngine::default();
1618 engine.start()?;
1619 Ok(engine)
1620}
1621
1622pub fn new_block(id: ID, parent_id: ID, height: u64, payload: Vec<u8>) -> Block {
1624 Block::new(id, parent_id, height, payload)
1625}
1626
1627pub fn new_vote(block_id: ID, vote_type: VoteType, voter: NodeID) -> Vote {
1629 Vote::new(block_id, vote_type, voter)
1630}
1631
1632pub fn generate_block_id() -> ID {
1634 let now = SystemTime::now()
1636 .duration_since(SystemTime::UNIX_EPOCH)
1637 .unwrap_or_default();
1638 let seed = now.as_nanos() as u64;
1639
1640 let mut state = seed;
1641 let mut bytes = [0u8; 32];
1642 for i in 0..4 {
1643 state ^= state << 13;
1644 state ^= state >> 7;
1645 state ^= state << 17;
1646 let chunk = state.to_le_bytes();
1647 bytes[i*8..(i+1)*8].copy_from_slice(&chunk);
1648 }
1649
1650 ID::new(bytes)
1651}
1652
1653pub fn version() -> &'static str {
1655 env!("CARGO_PKG_VERSION")
1656}
1657
1658#[cfg(test)]
1661mod tests {
1662 use super::*;
1663
1664 #[test]
1665 fn test_fpc_selector() {
1666 let fpc = FpcSelector::default();
1667
1668 let theta1 = fpc.theta(100);
1670 let theta2 = fpc.theta(100);
1671 assert_eq!(theta1, theta2);
1672
1673 let theta3 = fpc.theta(101);
1675 assert_ne!(theta1, theta3);
1676
1677 for phase in 0..1000 {
1679 let theta = fpc.theta(phase);
1680 assert!((0.5..=0.8).contains(&theta), "theta {} out of range", theta);
1681 }
1682 }
1683
1684 #[test]
1685 fn test_fpc_threshold() {
1686 let fpc = FpcSelector::new(0.5, 0.8, *b"test-seed-0000000000000000000000");
1687 let k = 20;
1688
1689 let threshold = fpc.select_threshold(0, k);
1690 assert!((10..=16).contains(&threshold));
1692 }
1693
1694 #[test]
1695 fn test_luminance() {
1696 let config = QuasarConfig::testnet();
1697 let mut luminance = photon::Luminance::new(&config);
1698
1699 let node = NodeID::from([1u8; 20]);
1700
1701 assert_eq!(luminance.brightness(&node), 1.0);
1703
1704 luminance.illuminate(&node, true);
1706 assert!(luminance.brightness(&node) > 1.0);
1707
1708 let bright_before = luminance.brightness(&node);
1710 luminance.illuminate(&node, false);
1711 assert!(luminance.brightness(&node) < bright_before);
1712 }
1713
1714 #[test]
1715 fn test_focus_confidence() {
1716 let mut focus: focus::Focus<ID> = focus::Focus::new(5, 0.6);
1717 let block_id = ID::from([1u8; 32]);
1718
1719 assert!(!focus.is_decided(&block_id));
1721
1722 for _ in 0..5 {
1724 focus.update(block_id.clone(), 7, 10); }
1726
1727 assert!(focus.is_decided(&block_id));
1728 assert_eq!(focus.decision(&block_id), Decision::Accept);
1729 }
1730
1731 #[test]
1732 fn test_wave_voting() {
1733 let config = QuasarConfig::testnet(); let mut wave = wave::Wave::new(config);
1735
1736 let block_id = ID::from([1u8; 32]);
1737
1738 for i in 0..5 {
1740 let vote = Vote::new(
1741 block_id.clone(),
1742 VoteType::Preference,
1743 NodeID::from([i; 20]),
1744 );
1745 wave.record_vote(vote);
1746 }
1747
1748 let state = wave.state(&block_id).unwrap();
1750 assert_eq!(state.yes_count, 5);
1751 }
1752
1753 #[test]
1754 fn test_quasar_engine() {
1755 let config = QuasarConfig::testnet();
1756 let mut engine = QuasarEngine::new(config);
1757
1758 engine.start().unwrap();
1760
1761 for i in 0..5 {
1763 engine.add_validator(NodeID::from([i; 20]), 1).unwrap();
1764 }
1765
1766 let block = Block::new(
1768 ID::from([1u8; 32]),
1769 ID::zero(),
1770 1,
1771 b"test".to_vec(),
1772 );
1773 engine.add(block.clone()).unwrap();
1774
1775 for i in 0..5 {
1777 let vote = Vote::new(
1778 block.id.clone(),
1779 VoteType::Preference,
1780 NodeID::from([i; 20]),
1781 );
1782 engine.record_vote(vote).unwrap();
1783 }
1784
1785 let status = engine.get_status(&block.id);
1787 assert!(status == Status::Processing || status == Status::Accepted);
1788
1789 engine.stop().unwrap();
1790 }
1791
1792 #[test]
1793 fn test_full_consensus_flow() {
1794 let config = QuasarConfig::testnet();
1795 let mut engine = QuasarEngine::new(config.clone());
1796 engine.start().unwrap();
1797
1798 for i in 0..10 {
1800 engine.add_validator(NodeID::from([i; 20]), 1).unwrap();
1801 }
1802
1803 let blocks: Vec<Block> = (1..=3).map(|height| {
1805 let mut id = [0u8; 32];
1806 id[0] = height as u8;
1807 let mut parent_id = [0u8; 32];
1808 if height > 1 {
1809 parent_id[0] = (height - 1) as u8;
1810 }
1811 Block::new(ID::from(id), ID::from(parent_id), height, vec![])
1812 }).collect();
1813
1814 for block in &blocks {
1816 engine.add(block.clone()).unwrap();
1817 }
1818
1819 for block in &blocks {
1821 for i in 0..5 {
1822 let vote = Vote::new(
1823 block.id.clone(),
1824 VoteType::Preference,
1825 NodeID::from([i; 20]),
1826 );
1827 engine.record_vote(vote).unwrap();
1828 }
1829 }
1830
1831 for block in &blocks {
1833 let status = engine.get_status(&block.id);
1834 assert!(
1835 status == Status::Accepted || status == Status::Processing,
1836 "Block {} has unexpected status {:?}",
1837 block.height,
1838 status
1839 );
1840 }
1841
1842 engine.stop().unwrap();
1843 }
1844
1845 #[test]
1846 fn test_configs() {
1847 let default = QuasarConfig::default();
1848 assert_eq!(default.alpha, 0.69);
1849 assert_eq!(default.k, 20);
1850 assert_eq!(default.beta, 20);
1851 assert!(default.quantum_resistant);
1852
1853 let testnet = QuasarConfig::testnet();
1854 assert_eq!(testnet.alpha, 0.6);
1855 assert_eq!(testnet.k, 5);
1856 assert!(!testnet.quantum_resistant);
1857
1858 let mainnet = QuasarConfig::mainnet();
1859 assert_eq!(mainnet.alpha, 0.69);
1860 assert_eq!(mainnet.k, 21);
1861 assert!(mainnet.quantum_resistant);
1862 }
1863}