pub const NAME: &str = "Proof of Entropy (PoE)";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Validator {
pub id: String,
pub pubkey_hex: String,
}
impl Validator {
pub fn new(id: impl Into<String>, pubkey_hex: impl Into<String>) -> Self {
Self {
id: id.into(),
pubkey_hex: pubkey_hex.into(),
}
}
}
pub fn select_proposer(validators: &[Validator], beacon: &str) -> Option<usize> {
if validators.is_empty() {
return None;
}
let digest = blake3::hash(beacon.as_bytes());
let mut buf = [0u8; 8];
buf.copy_from_slice(&digest.as_bytes()[..8]);
let draw = u64::from_be_bytes(buf);
Some((draw % validators.len() as u64) as usize)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_set_selects_nobody() {
assert_eq!(select_proposer(&[], "COSMIC-abc"), None);
}
#[test]
fn selection_is_deterministic() {
let vs = vec![
Validator::new("PROBE-A", "aa"),
Validator::new("PROBE-B", "bb"),
Validator::new("PROBE-C", "cc"),
];
let a = select_proposer(&vs, "COSMIC-round-7");
let b = select_proposer(&vs, "COSMIC-round-7");
assert_eq!(a, b);
assert!(a.unwrap() < vs.len());
}
}