use std::{cmp::max, collections::BTreeMap, num::NonZeroU64};
use ethereum_types::U256;
use hotshot_types::{
traits::{
election::Membership,
network::Topic,
node_implementation::NodeType,
signature_key::{SignatureKey, StakeTableEntryType},
},
PeerConfig,
};
use rand::{rngs::StdRng, Rng};
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct RandomizedCommittee<T: NodeType> {
eligible_leaders: Vec<<T::SignatureKey as SignatureKey>::StakeTableEntry>,
stake_table: Vec<<T::SignatureKey as SignatureKey>::StakeTableEntry>,
indexed_stake_table:
BTreeMap<T::SignatureKey, <T::SignatureKey as SignatureKey>::StakeTableEntry>,
committee_topic: Topic,
}
impl<TYPES: NodeType> Membership<TYPES> for RandomizedCommittee<TYPES> {
fn new(
eligible_leaders: Vec<PeerConfig<<TYPES as NodeType>::SignatureKey>>,
committee_members: Vec<PeerConfig<<TYPES as NodeType>::SignatureKey>>,
committee_topic: Topic,
) -> Self {
let eligible_leaders: Vec<<TYPES::SignatureKey as SignatureKey>::StakeTableEntry> =
eligible_leaders
.iter()
.map(|member| member.stake_table_entry.clone())
.filter(|entry| entry.stake() > U256::zero())
.collect();
let members: Vec<<TYPES::SignatureKey as SignatureKey>::StakeTableEntry> =
committee_members
.iter()
.map(|member| member.stake_table_entry.clone())
.filter(|entry| entry.stake() > U256::zero())
.collect();
let indexed_stake_table: BTreeMap<
TYPES::SignatureKey,
<TYPES::SignatureKey as SignatureKey>::StakeTableEntry,
> = members
.iter()
.map(|entry| (TYPES::SignatureKey::public_key(entry), entry.clone()))
.collect();
Self {
eligible_leaders,
stake_table: members,
indexed_stake_table,
committee_topic,
}
}
fn stake_table(
&self,
) -> Vec<<<TYPES as NodeType>::SignatureKey as SignatureKey>::StakeTableEntry> {
self.stake_table.clone()
}
fn committee_members(
&self,
_view_number: <TYPES as NodeType>::Time,
) -> std::collections::BTreeSet<<TYPES as NodeType>::SignatureKey> {
self.stake_table
.iter()
.map(TYPES::SignatureKey::public_key)
.collect()
}
fn committee_leaders(
&self,
_view_number: <TYPES as NodeType>::Time,
) -> std::collections::BTreeSet<<TYPES as NodeType>::SignatureKey> {
self.eligible_leaders
.iter()
.map(TYPES::SignatureKey::public_key)
.collect()
}
fn stake(
&self,
pub_key: &<TYPES as NodeType>::SignatureKey,
) -> Option<<TYPES::SignatureKey as SignatureKey>::StakeTableEntry> {
self.indexed_stake_table.get(pub_key).cloned()
}
fn has_stake(&self, pub_key: &<TYPES as NodeType>::SignatureKey) -> bool {
self.indexed_stake_table
.get(pub_key)
.is_some_and(|x| x.stake() > U256::zero())
}
fn committee_topic(&self) -> Topic {
self.committee_topic.clone()
}
fn leader(&self, view_number: TYPES::Time) -> TYPES::SignatureKey {
let mut rng: StdRng = rand::SeedableRng::seed_from_u64(*view_number);
let randomized_view_number: u64 = rng.gen_range(0..=u64::MAX);
#[allow(clippy::cast_possible_truncation)]
let index = randomized_view_number as usize % self.eligible_leaders.len();
let res = self.eligible_leaders[index].clone();
TYPES::SignatureKey::public_key(&res)
}
fn total_nodes(&self) -> usize {
self.stake_table.len()
}
fn success_threshold(&self) -> NonZeroU64 {
NonZeroU64::new(((self.stake_table.len() as u64 * 2) / 3) + 1).unwrap()
}
fn failure_threshold(&self) -> NonZeroU64 {
NonZeroU64::new(((self.stake_table.len() as u64) / 3) + 1).unwrap()
}
fn upgrade_threshold(&self) -> NonZeroU64 {
NonZeroU64::new(max(
(self.stake_table.len() as u64 * 9) / 10,
((self.stake_table.len() as u64 * 2) / 3) + 1,
))
.unwrap()
}
}