2020-07-28 02:33:27 -07:00
|
|
|
use solana_runtime::commitment::VOTE_THRESHOLD_SIZE;
|
|
|
|
use solana_sdk::pubkey::Pubkey;
|
2021-02-07 18:07:00 -08:00
|
|
|
use std::collections::HashSet;
|
2020-07-28 02:33:27 -07:00
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct VoteStakeTracker {
|
2021-02-07 18:07:00 -08:00
|
|
|
voted: HashSet<Pubkey>,
|
2020-07-28 02:33:27 -07:00
|
|
|
stake: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl VoteStakeTracker {
|
2020-07-29 23:17:40 -07:00
|
|
|
// Returns tuple (is_confirmed, is_new) where
|
|
|
|
// `is_confirmed` is true if the stake that has voted has just crosssed the supermajority
|
2020-07-28 02:33:27 -07:00
|
|
|
// of stake
|
2020-07-29 23:17:40 -07:00
|
|
|
// `is_new` is true if the vote has not been seen before
|
2020-07-28 02:33:27 -07:00
|
|
|
pub fn add_vote_pubkey(
|
|
|
|
&mut self,
|
2021-02-07 18:07:00 -08:00
|
|
|
vote_pubkey: Pubkey,
|
2020-07-28 02:33:27 -07:00
|
|
|
stake: u64,
|
2020-10-23 18:19:12 -07:00
|
|
|
total_stake: u64,
|
2020-07-29 23:17:40 -07:00
|
|
|
) -> (bool, bool) {
|
|
|
|
let is_new = !self.voted.contains(&vote_pubkey);
|
|
|
|
if is_new {
|
2020-07-28 02:33:27 -07:00
|
|
|
self.voted.insert(vote_pubkey);
|
2020-10-23 18:19:12 -07:00
|
|
|
let supermajority_stake = (total_stake as f64 * VOTE_THRESHOLD_SIZE) as u64;
|
|
|
|
let old_stake = self.stake;
|
|
|
|
let new_stake = self.stake + stake;
|
|
|
|
self.stake = new_stake;
|
2020-07-29 23:17:40 -07:00
|
|
|
(
|
2020-10-23 18:19:12 -07:00
|
|
|
old_stake <= supermajority_stake && supermajority_stake < new_stake,
|
2020-07-29 23:17:40 -07:00
|
|
|
is_new,
|
|
|
|
)
|
2020-07-28 02:33:27 -07:00
|
|
|
} else {
|
2020-07-29 23:17:40 -07:00
|
|
|
(false, is_new)
|
2020-07-28 02:33:27 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-07 18:07:00 -08:00
|
|
|
pub fn voted(&self) -> &HashSet<Pubkey> {
|
2020-07-28 02:33:27 -07:00
|
|
|
&self.voted
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn stake(&self) -> u64 {
|
|
|
|
self.stake
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_add_vote_pubkey() {
|
|
|
|
let total_epoch_stake = 10;
|
|
|
|
let mut vote_stake_tracker = VoteStakeTracker::default();
|
|
|
|
for i in 0..10 {
|
2021-02-07 18:07:00 -08:00
|
|
|
let pubkey = solana_sdk::pubkey::new_rand();
|
2020-07-29 23:17:40 -07:00
|
|
|
let (is_confirmed, is_new) =
|
2021-02-07 18:07:00 -08:00
|
|
|
vote_stake_tracker.add_vote_pubkey(pubkey, 1, total_epoch_stake);
|
2020-07-28 02:33:27 -07:00
|
|
|
let stake = vote_stake_tracker.stake();
|
2020-07-29 23:17:40 -07:00
|
|
|
let (is_confirmed2, is_new2) =
|
2021-02-07 18:07:00 -08:00
|
|
|
vote_stake_tracker.add_vote_pubkey(pubkey, 1, total_epoch_stake);
|
2020-07-28 02:33:27 -07:00
|
|
|
let stake2 = vote_stake_tracker.stake();
|
|
|
|
|
|
|
|
// Stake should not change from adding same pubkey twice
|
|
|
|
assert_eq!(stake, stake2);
|
2020-07-29 23:17:40 -07:00
|
|
|
assert!(!is_confirmed2);
|
|
|
|
assert!(!is_new2);
|
2020-07-28 02:33:27 -07:00
|
|
|
|
2020-07-30 02:52:27 -07:00
|
|
|
// at i == 6, the voted stake is 70%, which is the first time crossing
|
2020-07-28 02:33:27 -07:00
|
|
|
// the supermajority threshold
|
|
|
|
if i == 6 {
|
2020-07-29 23:17:40 -07:00
|
|
|
assert!(is_confirmed);
|
2020-07-28 02:33:27 -07:00
|
|
|
} else {
|
2020-07-29 23:17:40 -07:00
|
|
|
assert!(!is_confirmed);
|
2020-07-28 02:33:27 -07:00
|
|
|
}
|
2020-07-29 23:17:40 -07:00
|
|
|
assert!(is_new);
|
2020-07-28 02:33:27 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|