1use crate::error::{ConsensusError, Result};
4use crate::leader_reputation::LeaderReputation;
5use crate::voter::Vote;
6use dashmap::DashMap;
7use serde::{Deserialize, Deserializer, Serialize};
8use std::sync::Arc;
9use tenzro_crypto::pq::ML_DSA_65_VK_LEN;
10use tenzro_crypto::PublicKey;
11
12pub const BLS_G1_COMPRESSED_LEN: usize = 48;
16use tenzro_types::primitives::{Address, Hash, Timestamp};
17use tenzro_types::tee::{AttestationReport, AttestationResult};
18
19fn deserialize_pq_verifying_key<'de, D>(deserializer: D) -> std::result::Result<Vec<u8>, D::Error>
24where
25 D: Deserializer<'de>,
26{
27 let bytes = Vec::<u8>::deserialize(deserializer)?;
28 if bytes.len() != ML_DSA_65_VK_LEN {
29 return Err(serde::de::Error::custom(format!(
30 "validator PQ verifying key has wrong length: expected {} bytes (ML-DSA-65), got {}",
31 ML_DSA_65_VK_LEN,
32 bytes.len()
33 )));
34 }
35 Ok(bytes)
36}
37
38fn deserialize_bls_verifying_key<'de, D>(deserializer: D) -> std::result::Result<Vec<u8>, D::Error>
43where
44 D: Deserializer<'de>,
45{
46 let bytes = Vec::<u8>::deserialize(deserializer)?;
47 if bytes.len() != BLS_G1_COMPRESSED_LEN {
48 return Err(serde::de::Error::custom(format!(
49 "validator BLS verifying key has wrong length: expected {} bytes (BLS12-381 G1 compressed), got {}",
50 BLS_G1_COMPRESSED_LEN,
51 bytes.len()
52 )));
53 }
54 Ok(bytes)
55}
56
57#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
59pub struct ValidatorInfo {
60 pub address: Address,
62
63 pub public_key: PublicKey,
65
66 #[serde(deserialize_with = "deserialize_pq_verifying_key")]
70 pub pq_public_key: Vec<u8>,
71
72 #[serde(deserialize_with = "deserialize_bls_verifying_key")]
81 pub bls_public_key: Vec<u8>,
82
83 pub stake: u128,
85
86 pub tee_attestation: Option<AttestationReport>,
88
89 pub tee_attestation_result: Option<AttestationResult>,
91
92 pub status: ValidatorStatus,
94
95 pub registered_at: Timestamp,
97
98 pub last_attestation_update: Option<Timestamp>,
100}
101
102impl ValidatorInfo {
103 pub fn new(
117 address: Address,
118 public_key: PublicKey,
119 pq_public_key: Vec<u8>,
120 bls_public_key: Vec<u8>,
121 stake: u128,
122 ) -> Self {
123 assert_eq!(
124 pq_public_key.len(),
125 ML_DSA_65_VK_LEN,
126 "validator PQ verifying key has wrong length: expected {} bytes (ML-DSA-65), got {}",
127 ML_DSA_65_VK_LEN,
128 pq_public_key.len()
129 );
130 assert_eq!(
131 bls_public_key.len(),
132 BLS_G1_COMPRESSED_LEN,
133 "validator BLS verifying key has wrong length: expected {} bytes (BLS12-381 G1 compressed), got {}",
134 BLS_G1_COMPRESSED_LEN,
135 bls_public_key.len()
136 );
137 Self {
138 address,
139 public_key,
140 pq_public_key,
141 bls_public_key,
142 stake,
143 tee_attestation: None,
144 tee_attestation_result: None,
145 status: ValidatorStatus::Active,
146 registered_at: Timestamp::now(),
147 last_attestation_update: None,
148 }
149 }
150
151 pub fn composite_public_key(&self) -> tenzro_crypto::composite::CompositePublicKey {
154 tenzro_crypto::composite::CompositePublicKey::new(
155 self.public_key.clone(),
156 self.pq_public_key.clone(),
157 )
158 }
159
160 pub fn with_tee_attestation(
162 mut self,
163 attestation: AttestationReport,
164 result: AttestationResult,
165 ) -> Self {
166 self.tee_attestation = Some(attestation);
167 self.tee_attestation_result = Some(result);
168 self.last_attestation_update = Some(Timestamp::now());
169 self
170 }
171
172 pub fn has_valid_tee_attestation(&self) -> bool {
174 self.tee_attestation_result
175 .as_ref()
176 .map(|r| r.valid)
177 .unwrap_or(false)
178 }
179
180 pub fn is_active(&self) -> bool {
182 self.status == ValidatorStatus::Active
183 }
184
185 pub fn voting_power(&self) -> u128 {
187 if self.is_active() {
188 self.stake
189 } else {
190 0
191 }
192 }
193
194 pub fn leader_priority(&self) -> u128 {
202 self.voting_power()
203 }
204}
205
206#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
215pub enum ValidatorStatus {
216 Active,
218 Inactive,
220 Jailed,
222 Unbonding,
224}
225
226#[derive(Debug, Clone, Serialize, Deserialize)]
228pub struct ValidatorSet {
229 pub epoch: u64,
231
232 validators: Vec<ValidatorInfo>,
234
235 total_stake: u128,
237
238 pub epoch_start: Timestamp,
240}
241
242impl ValidatorSet {
243 pub fn new(epoch: u64, validators: Vec<ValidatorInfo>) -> Result<Self> {
245 if validators.is_empty() {
246 return Err(ConsensusError::InvalidValidatorSet(
247 "Validator set cannot be empty".to_string(),
248 ));
249 }
250
251 let total_stake = validators.iter().map(|v| v.voting_power()).sum();
252
253 Ok(Self {
254 epoch,
255 validators,
256 total_stake,
257 epoch_start: Timestamp::now(),
258 })
259 }
260
261 pub fn get(&self, index: usize) -> Option<&ValidatorInfo> {
263 self.validators.get(index)
264 }
265
266 pub fn get_by_address(&self, address: &Address) -> Option<&ValidatorInfo> {
268 self.validators.iter().find(|v| &v.address == address)
269 }
270
271 pub fn index_of(&self, address: &Address) -> Option<usize> {
279 self.validators.iter().position(|v| &v.address == address)
280 }
281
282 pub fn active_validators(&self) -> &[ValidatorInfo] {
287 &self.validators
288 }
289
290 pub fn len(&self) -> usize {
292 self.validators.len()
293 }
294
295 pub fn is_empty(&self) -> bool {
297 self.validators.is_empty()
298 }
299
300 pub fn iter(&self) -> std::slice::Iter<'_, ValidatorInfo> {
302 self.validators.iter()
303 }
304
305 pub fn total_stake(&self) -> u128 {
307 self.total_stake
308 }
309
310 pub fn total_voting_power(&self) -> u128 {
312 self.validators.iter().map(|v| v.voting_power()).sum()
313 }
314
315 pub fn is_validator(&self, address: &Address) -> bool {
317 self.get_by_address(address).is_some()
318 }
319
320 pub fn select_leader_round_robin(&self, view: u64) -> Result<&ValidatorInfo> {
327 if self.validators.is_empty() {
328 return Err(ConsensusError::InvalidValidatorSet(
329 "No validators available".to_string(),
330 ));
331 }
332 let index = (view as usize) % self.validators.len();
333 Ok(&self.validators[index])
334 }
335
336 pub fn quorum_threshold(&self) -> usize {
338 let n = self.validators.len();
339 let f = (n.saturating_sub(1)) / 3;
340 2 * f + 1
341 }
342
343 pub fn tee_attested_validators(&self) -> Vec<&ValidatorInfo> {
345 self.validators
346 .iter()
347 .filter(|v| v.has_valid_tee_attestation())
348 .collect()
349 }
350
351 pub fn tee_attestation_rate(&self) -> f64 {
353 if self.validators.is_empty() {
354 return 0.0;
355 }
356 let attested = self.tee_attested_validators().len();
357 (attested as f64 / self.validators.len() as f64) * 100.0
358 }
359}
360
361#[derive(Debug, Clone, Serialize, Deserialize)]
363pub struct EquivocationEvidence {
364 pub validator: Address,
366
367 pub view: u64,
369
370 pub vote1: Vote,
372
373 pub vote2: Vote,
375
376 pub detected_at: Timestamp,
378}
379
380impl EquivocationEvidence {
381 pub fn new(validator: Address, view: u64, vote1: Vote, vote2: Vote) -> Self {
383 Self {
384 validator,
385 view,
386 vote1,
387 vote2,
388 detected_at: Timestamp::now(),
389 }
390 }
391
392 pub fn is_valid(&self) -> bool {
394 self.vote1.view == self.vote2.view
395 && self.vote1.view == self.view
396 && self.vote1.voter == self.vote2.voter
397 && self.vote1.voter == self.validator
398 && self.vote1.block_hash != self.vote2.block_hash
399 && self.vote1.vote_type == self.vote2.vote_type
400 }
401}
402
403#[derive(Debug, Clone, PartialEq, Eq, Hash)]
405struct ValidatorViewKey {
406 validator: Address,
407 view: u64,
408}
409
410pub struct EquivocationDetector {
412 votes: Arc<DashMap<ValidatorViewKey, (Hash, crate::voter::VoteType)>>,
415
416 evidence: Arc<DashMap<(Address, u64), EquivocationEvidence>>,
418
419 storage: Option<Arc<dyn tenzro_storage::KvStore>>,
426}
427
428impl EquivocationDetector {
429 pub fn new() -> Self {
432 Self {
433 votes: Arc::new(DashMap::new()),
434 evidence: Arc::new(DashMap::new()),
435 storage: None,
436 }
437 }
438
439 pub fn with_storage(storage: Arc<dyn tenzro_storage::KvStore>) -> Self {
443 let d = Self {
444 votes: Arc::new(DashMap::new()),
445 evidence: Arc::new(DashMap::new()),
446 storage: Some(storage),
447 };
448 d.hydrate();
449 d
450 }
451
452 fn vote_key(validator: &Address, view: u64) -> Vec<u8> {
453 let mut k = b"equivocation/votes/".to_vec();
454 k.extend_from_slice(validator.as_bytes());
455 k.push(b'/');
456 k.extend_from_slice(&view.to_le_bytes());
457 k
458 }
459 fn evidence_key(validator: &Address, view: u64) -> Vec<u8> {
460 let mut k = b"equivocation/evidence/".to_vec();
461 k.extend_from_slice(validator.as_bytes());
462 k.push(b'/');
463 k.extend_from_slice(&view.to_le_bytes());
464 k
465 }
466
467 fn hydrate(&self) {
468 let Some(ref storage) = self.storage else {
469 return;
470 };
471 if let Ok(entries) =
472 storage.scan_prefix(tenzro_storage::CF_AUDIT, b"equivocation/votes/")
473 {
474 for (key, value) in entries {
475 if let Some(vk) = Self::parse_vote_key(&key) {
476 if let Ok(payload) =
477 serde_json::from_slice::<(Hash, crate::voter::VoteType)>(&value)
478 {
479 self.votes.insert(vk, payload);
480 }
481 }
482 }
483 }
484 if let Ok(entries) =
485 storage.scan_prefix(tenzro_storage::CF_AUDIT, b"equivocation/evidence/")
486 {
487 for (key, value) in entries {
488 if let Some((addr, view)) = Self::parse_evidence_key(&key) {
489 if let Ok(ev) =
490 serde_json::from_slice::<EquivocationEvidence>(&value)
491 {
492 self.evidence.insert((addr, view), ev);
493 }
494 }
495 }
496 }
497 }
498
499 fn parse_vote_key(key: &[u8]) -> Option<ValidatorViewKey> {
500 let prefix = b"equivocation/votes/";
501 if !key.starts_with(prefix) {
502 return None;
503 }
504 let rest = &key[prefix.len()..];
505 if rest.len() < 9 {
506 return None;
507 }
508 let addr_end = rest.len() - 9;
509 if rest[addr_end] != b'/' {
510 return None;
511 }
512 let validator = Address::from_bytes(&rest[..addr_end])?;
513 let mut view_buf = [0u8; 8];
514 view_buf.copy_from_slice(&rest[addr_end + 1..]);
515 let view = u64::from_le_bytes(view_buf);
516 Some(ValidatorViewKey { validator, view })
517 }
518
519 fn parse_evidence_key(key: &[u8]) -> Option<(Address, u64)> {
520 let prefix = b"equivocation/evidence/";
521 if !key.starts_with(prefix) {
522 return None;
523 }
524 let rest = &key[prefix.len()..];
525 if rest.len() < 9 {
526 return None;
527 }
528 let addr_end = rest.len() - 9;
529 if rest[addr_end] != b'/' {
530 return None;
531 }
532 let validator = Address::from_bytes(&rest[..addr_end])?;
533 let mut view_buf = [0u8; 8];
534 view_buf.copy_from_slice(&rest[addr_end + 1..]);
535 let view = u64::from_le_bytes(view_buf);
536 Some((validator, view))
537 }
538
539 fn persist_vote(
540 &self,
541 vk: &ValidatorViewKey,
542 payload: &(Hash, crate::voter::VoteType),
543 ) {
544 if let Some(ref storage) = self.storage {
545 if let Ok(bytes) = serde_json::to_vec(payload) {
546 let _ = storage.put(
547 tenzro_storage::CF_AUDIT,
548 &Self::vote_key(&vk.validator, vk.view),
549 &bytes,
550 );
551 }
552 }
553 }
554
555 fn persist_evidence(&self, ev: &EquivocationEvidence) {
556 if let Some(ref storage) = self.storage {
557 if let Ok(bytes) = serde_json::to_vec(ev) {
558 let _ = storage.put(
559 tenzro_storage::CF_AUDIT,
560 &Self::evidence_key(&ev.validator, ev.view),
561 &bytes,
562 );
563 }
564 }
565 }
566
567 pub fn check_vote(&self, vote: &Vote) -> Result<Option<EquivocationEvidence>> {
572 let key = ValidatorViewKey {
573 validator: vote.voter,
574 view: vote.view,
575 };
576
577 if let Some(existing) = self.votes.get(&key) {
579 let (existing_hash, existing_type) = *existing;
580
581 if existing_hash == vote.block_hash && existing_type == vote.vote_type {
583 return Err(ConsensusError::AlreadyVoted(vote.view));
584 }
585
586 if existing_type == vote.vote_type {
588 let placeholder_sig = tenzro_crypto::composite::CompositeSignature::new(
598 Vec::new(),
599 Vec::new(),
600 );
601 let placeholder_bls = vote.bls_signature.clone();
605 let vote1 = Vote::new(
606 vote.view,
607 vote.height,
608 existing_hash,
609 vote.voter,
610 placeholder_sig,
611 vote.public_key.clone(),
612 placeholder_bls,
613 existing_type,
614 vote.high_qc_view,
615 );
616
617 let evidence = EquivocationEvidence::new(
618 vote.voter,
619 vote.view,
620 vote1,
621 vote.clone(),
622 );
623
624 self.evidence.insert((vote.voter, vote.view), evidence.clone());
626 self.persist_evidence(&evidence);
627
628 tracing::warn!(
629 validator = %vote.voter,
630 view = vote.view,
631 block1 = %existing_hash,
632 block2 = %vote.block_hash,
633 "Equivocation detected"
634 );
635
636 return Ok(Some(evidence));
637 }
638 }
639
640 let payload = (vote.block_hash, vote.vote_type);
642 self.votes.insert(key.clone(), payload);
643 self.persist_vote(&key, &payload);
644
645 Ok(None)
646 }
647
648 pub fn get_all_evidence(&self) -> Vec<EquivocationEvidence> {
650 self.evidence.iter().map(|entry| entry.value().clone()).collect()
651 }
652
653 pub fn get_evidence(&self, validator: &Address, view: u64) -> Option<EquivocationEvidence> {
655 self.evidence.get(&(*validator, view)).map(|e| e.clone())
656 }
657
658 pub fn cleanup_old_votes(&self, min_view: u64) {
670 let mut pruned: Vec<ValidatorViewKey> = Vec::new();
671 self.votes.retain(|key, _| {
672 let keep = key.view >= min_view;
673 if !keep {
674 pruned.push(key.clone());
675 }
676 keep
677 });
678 if let Some(ref storage) = self.storage {
679 for key in &pruned {
680 let _ = storage.delete(
681 tenzro_storage::CF_AUDIT,
682 &Self::vote_key(&key.validator, key.view),
683 );
684 }
685 }
686 }
687
688 pub fn vote_count(&self) -> usize {
690 self.votes.len()
691 }
692
693 pub fn evidence_count(&self) -> usize {
695 self.evidence.len()
696 }
697}
698
699impl Default for EquivocationDetector {
700 fn default() -> Self {
701 Self::new()
702 }
703}
704
705pub trait ProposerElection: Send + Sync {
727 fn select_leader(
734 &self,
735 round: u64,
736 epoch: u64,
737 prev_block_id: [u8; 32],
738 validator_set: &ValidatorSet,
739 ) -> Result<Address>;
740}
741
742#[derive(Debug, Default, Clone, Copy)]
748pub struct RoundRobinProposer;
749
750impl RoundRobinProposer {
751 pub const fn new() -> Self {
752 Self
753 }
754}
755
756impl ProposerElection for RoundRobinProposer {
757 fn select_leader(
758 &self,
759 round: u64,
760 _epoch: u64,
761 _prev_block_id: [u8; 32],
762 validator_set: &ValidatorSet,
763 ) -> Result<Address> {
764 validator_set
765 .select_leader_round_robin(round)
766 .map(|v| v.address)
767 }
768}
769
770#[derive(Clone)]
778pub struct ReputationProposer {
779 reputation: Arc<LeaderReputation>,
780}
781
782impl ReputationProposer {
783 pub fn new(reputation: Arc<LeaderReputation>) -> Self {
785 Self { reputation }
786 }
787
788 pub fn reputation(&self) -> &Arc<LeaderReputation> {
792 &self.reputation
793 }
794}
795
796impl ProposerElection for ReputationProposer {
797 fn select_leader(
798 &self,
799 round: u64,
800 epoch: u64,
801 prev_block_id: [u8; 32],
802 validator_set: &ValidatorSet,
803 ) -> Result<Address> {
804 let prev_hash = Hash::new(prev_block_id);
805 self.reputation
806 .select_leader(round, epoch, &prev_hash, validator_set)
807 .map(|v| v.address)
808 }
809}
810
811#[cfg(test)]
812mod tests {
813 use super::*;
814 use tenzro_crypto::bls::BlsKeyPair;
815 use tenzro_crypto::pq::MlDsaSigningKey;
816 use tenzro_crypto::{KeyPair, KeyType};
817
818 fn create_test_validator(stake: u128) -> ValidatorInfo {
819 let keypair = KeyPair::generate(KeyType::Ed25519).unwrap();
820 let crypto_addr = keypair.address();
822 let mut addr_bytes = [0u8; 32];
823 addr_bytes[..20].copy_from_slice(crypto_addr.as_bytes());
824 let address = Address::new(addr_bytes);
825 let pq = MlDsaSigningKey::generate();
826 let bls = BlsKeyPair::generate().unwrap();
827 ValidatorInfo::new(
828 address,
829 keypair.public_key().clone(),
830 pq.verifying_key_bytes().to_vec(),
831 bls.public_key().to_bytes().to_vec(),
832 stake,
833 )
834 }
835
836 #[test]
837 fn test_validator_info() {
838 let validator = create_test_validator(1000);
839 assert_eq!(validator.voting_power(), 1000);
840 assert!(validator.is_active());
841 assert!(!validator.has_valid_tee_attestation());
842 }
843
844 #[test]
845 fn test_validator_set_creation() {
846 let validators = vec![
847 create_test_validator(1000),
848 create_test_validator(2000),
849 create_test_validator(3000),
850 ];
851
852 let set = ValidatorSet::new(1, validators).unwrap();
853 assert_eq!(set.len(), 3);
854 assert_eq!(set.total_voting_power(), 6000);
855 assert_eq!(set.quorum_threshold(), 1); }
857
858 #[test]
859 fn test_leader_selection() {
860 let validators = vec![
861 create_test_validator(1000),
862 create_test_validator(2000),
863 create_test_validator(3000),
864 ];
865
866 let set = ValidatorSet::new(1, validators).unwrap();
867
868 let leader0 = set.select_leader_round_robin(0).unwrap();
870 let leader1 = set.select_leader_round_robin(1).unwrap();
871 let leader2 = set.select_leader_round_robin(2).unwrap();
872 let leader3 = set.select_leader_round_robin(3).unwrap();
873
874 assert_eq!(leader0.address, set.validators[0].address);
875 assert_eq!(leader1.address, set.validators[1].address);
876 assert_eq!(leader2.address, set.validators[2].address);
877 assert_eq!(leader3.address, set.validators[0].address); }
879
880 #[test]
881 fn test_empty_validator_set() {
882 let result = ValidatorSet::new(1, vec![]);
883 assert!(result.is_err());
884 }
885
886 #[test]
887 fn test_equivocation_detection() {
888 use crate::voter::{Vote, VoteType};
889 use tenzro_crypto::composite::{CompositePublicKey, CompositeSignature};
890 use tenzro_types::primitives::BlockHeight;
891
892 let detector = EquivocationDetector::new();
893 let validator = create_test_validator(1000);
894
895 let placeholder_pk = CompositePublicKey::new(
896 validator.public_key.clone(),
897 validator.pq_public_key.clone(),
898 );
899 let placeholder_sig = CompositeSignature::new(vec![0u8; 64], vec![0u8; 3309]);
900 let placeholder_bls_kp = BlsKeyPair::generate().unwrap();
904 let placeholder_bls = placeholder_bls_kp.sign(b"__placeholder__");
905
906 let vote1 = Vote::new(
907 1,
908 BlockHeight::from(10),
909 Hash::default(),
910 validator.address,
911 placeholder_sig.clone(),
912 placeholder_pk.clone(),
913 placeholder_bls.clone(),
914 VoteType::Prepare,
915 0,
916 );
917
918 let result = detector.check_vote(&vote1);
920 assert!(result.is_ok());
921 assert!(result.unwrap().is_none());
922
923 let result = detector.check_vote(&vote1);
925 assert!(result.is_err());
926
927 let mut different_hash = [0u8; 32];
929 different_hash[0] = 1;
930 let vote2 = Vote::new(
931 1,
932 BlockHeight::from(10),
933 Hash::new(different_hash),
934 validator.address,
935 placeholder_sig.clone(),
936 placeholder_pk.clone(),
937 placeholder_bls.clone(),
938 VoteType::Prepare,
939 0,
940 );
941
942 let result = detector.check_vote(&vote2);
943 assert!(result.is_ok());
944 let evidence = result.unwrap();
945 assert!(evidence.is_some());
946
947 let evidence = evidence.unwrap();
948 assert_eq!(evidence.validator, validator.address);
949 assert_eq!(evidence.view, 1);
950 assert!(evidence.is_valid());
951 }
952
953 #[test]
954 fn test_equivocation_detector_cleanup() {
955 use crate::voter::{Vote, VoteType};
956 use tenzro_crypto::composite::{CompositePublicKey, CompositeSignature};
957 use tenzro_types::primitives::BlockHeight;
958
959 let detector = EquivocationDetector::new();
960 let validator = create_test_validator(1000);
961
962 let placeholder_pk = CompositePublicKey::new(
963 validator.public_key.clone(),
964 validator.pq_public_key.clone(),
965 );
966 let placeholder_sig = CompositeSignature::new(vec![0u8; 64], vec![0u8; 3309]);
967 let placeholder_bls_kp = BlsKeyPair::generate().unwrap();
968 let placeholder_bls = placeholder_bls_kp.sign(b"__placeholder__");
969
970 for view in 1..=3 {
972 let vote = Vote::new(
973 view,
974 BlockHeight::from(10),
975 Hash::default(),
976 validator.address,
977 placeholder_sig.clone(),
978 placeholder_pk.clone(),
979 placeholder_bls.clone(),
980 VoteType::Prepare,
981 0,
982 );
983 let _ = detector.check_vote(&vote);
984 }
985
986 assert_eq!(detector.vote_count(), 3);
987
988 detector.cleanup_old_votes(2);
990
991 assert_eq!(detector.vote_count(), 2);
993 }
994}