use {solana_sdk::pubkey::Pubkey, std::collections::HashSet}; #[derive(Default)] pub struct VoteStakeTracker { voted: HashSet, stake: u64, } impl VoteStakeTracker { // Returns tuple (reached_threshold_results, is_new) where // Each index in `reached_threshold_results` is true if the corresponding threshold in the input // `thresholds_to_check` was newly reached by adding the stake of the input `vote_pubkey` // `is_new` is true if the vote has not been seen before pub fn add_vote_pubkey( &mut self, vote_pubkey: Pubkey, stake: u64, total_stake: u64, thresholds_to_check: &[f64], ) -> (Vec, bool) { let is_new = !self.voted.contains(&vote_pubkey); if is_new { self.voted.insert(vote_pubkey); let old_stake = self.stake; let new_stake = self.stake + stake; self.stake = new_stake; let reached_threshold_results: Vec = thresholds_to_check .iter() .map(|threshold| { let threshold_stake = (total_stake as f64 * threshold) as u64; old_stake <= threshold_stake && threshold_stake < new_stake }) .collect(); (reached_threshold_results, is_new) } else { (vec![false; thresholds_to_check.len()], is_new) } } pub fn voted(&self) -> &HashSet { &self.voted } pub fn stake(&self) -> u64 { self.stake } } #[cfg(test)] mod test { use {super::*, solana_runtime::commitment::VOTE_THRESHOLD_SIZE}; #[test] fn test_add_vote_pubkey() { let total_epoch_stake = 10; let mut vote_stake_tracker = VoteStakeTracker::default(); for i in 0..10 { let pubkey = solana_sdk::pubkey::new_rand(); let (is_confirmed_thresholds, is_new) = vote_stake_tracker.add_vote_pubkey( pubkey, 1, total_epoch_stake, &[VOTE_THRESHOLD_SIZE, 0.0], ); let stake = vote_stake_tracker.stake(); let (is_confirmed_thresholds2, is_new2) = vote_stake_tracker.add_vote_pubkey( pubkey, 1, total_epoch_stake, &[VOTE_THRESHOLD_SIZE, 0.0], ); let stake2 = vote_stake_tracker.stake(); // Stake should not change from adding same pubkey twice assert_eq!(stake, stake2); assert!(!is_confirmed_thresholds2[0]); assert!(!is_confirmed_thresholds2[1]); assert!(!is_new2); assert_eq!(is_confirmed_thresholds.len(), 2); assert_eq!(is_confirmed_thresholds2.len(), 2); // at i == 6, the voted stake is 70%, which is the first time crossing // the supermajority threshold if i == 6 { assert!(is_confirmed_thresholds[0]); } else { assert!(!is_confirmed_thresholds[0]); } // at i == 6, the voted stake is 10%, which is the first time crossing // the 0% threshold if i == 0 { assert!(is_confirmed_thresholds[1]); } else { assert!(!is_confirmed_thresholds[1]); } assert!(is_new); } } }