entropa_node/
consensus.rs1pub const NAME: &str = "Proof of Entropy (PoE)";
15
16#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct Validator {
19 pub id: String,
21 pub pubkey_hex: String,
23}
24
25impl Validator {
26 pub fn new(id: impl Into<String>, pubkey_hex: impl Into<String>) -> Self {
27 Self {
28 id: id.into(),
29 pubkey_hex: pubkey_hex.into(),
30 }
31 }
32}
33
34pub fn select_proposer(validators: &[Validator], beacon: &str) -> Option<usize> {
37 if validators.is_empty() {
38 return None;
39 }
40 let digest = blake3::hash(beacon.as_bytes());
41 let mut buf = [0u8; 8];
42 buf.copy_from_slice(&digest.as_bytes()[..8]);
43 let draw = u64::from_be_bytes(buf);
44 Some((draw % validators.len() as u64) as usize)
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50
51 #[test]
52 fn empty_set_selects_nobody() {
53 assert_eq!(select_proposer(&[], "COSMIC-abc"), None);
54 }
55
56 #[test]
57 fn selection_is_deterministic() {
58 let vs = vec![
59 Validator::new("PROBE-A", "aa"),
60 Validator::new("PROBE-B", "bb"),
61 Validator::new("PROBE-C", "cc"),
62 ];
63 let a = select_proposer(&vs, "COSMIC-round-7");
64 let b = select_proposer(&vs, "COSMIC-round-7");
65 assert_eq!(a, b);
66 assert!(a.unwrap() < vs.len());
67 }
68}