aethex_consensus/
validator_set.rs1use axiom_core::types::ValidatorId;
2use serde::{Deserialize, Serialize};
3
4#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
6pub struct Validator {
7 pub id: ValidatorId,
8 pub voting_power: u64,
9}
10
11#[derive(Clone, Debug, Default, Serialize, Deserialize)]
13pub struct ValidatorSet {
14 validators: Vec<Validator>,
15 total_power: u64,
16}
17
18impl ValidatorSet {
19 pub fn new(mut validators: Vec<Validator>) -> Self {
20 validators.sort_by(|a, b| b.voting_power.cmp(&a.voting_power));
21 let total_power = validators.iter().map(|v| v.voting_power).sum();
22 ValidatorSet { validators, total_power }
23 }
24
25 pub fn is_empty(&self) -> bool {
26 self.validators.is_empty()
27 }
28
29 pub fn len(&self) -> usize {
30 self.validators.len()
31 }
32
33 pub fn total_power(&self) -> u64 {
34 self.total_power
35 }
36
37 pub fn get(&self, id: &ValidatorId) -> Option<&Validator> {
38 self.validators.iter().find(|v| &v.id == id)
39 }
40
41 pub fn contains(&self, id: &ValidatorId) -> bool {
42 self.get(id).is_some()
43 }
44
45 pub fn voting_power_of(&self, id: &ValidatorId) -> u64 {
46 self.get(id).map(|v| v.voting_power).unwrap_or(0)
47 }
48
49 pub fn proposer(&self, height: u64, round: u32) -> Option<&Validator> {
51 if self.validators.is_empty() {
52 return None;
53 }
54 let idx = ((height + round as u64) % self.validators.len() as u64) as usize;
55 Some(&self.validators[idx])
56 }
57
58 pub fn iter(&self) -> impl Iterator<Item = &Validator> {
59 self.validators.iter()
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66 use axiom_crypto::Keypair;
67
68 fn make_vset(n: usize) -> ValidatorSet {
69 let validators: Vec<Validator> = (0..n as u8)
70 .map(|i| Validator {
71 id: Keypair::from_bytes(&[i + 1; 32]).public_key(),
72 voting_power: 100,
73 })
74 .collect();
75 ValidatorSet::new(validators)
76 }
77
78 #[test]
79 fn total_power() {
80 let vs = make_vset(4);
81 assert_eq!(vs.total_power(), 400);
82 }
83
84 #[test]
85 fn proposer_rotates() {
86 let vs = make_vset(3);
87 let p0 = vs.proposer(0, 0).unwrap().id;
88 let p1 = vs.proposer(1, 0).unwrap().id;
89 let p2 = vs.proposer(2, 0).unwrap().id;
90 assert_ne!(p0, p1);
92 assert_ne!(p1, p2);
93 }
94}