solana/core/src/consensus/vote_stake_tracker.rs

101 lines
3.3 KiB
Rust

use {solana_sdk::pubkey::Pubkey, std::collections::HashSet};
#[derive(Default)]
pub struct VoteStakeTracker {
voted: HashSet<Pubkey>,
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>, 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<bool> = 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<Pubkey> {
&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);
}
}
}