diff --git a/core/src/consensus.rs b/core/src/consensus.rs index 283ed6b1fe..bd926b8110 100644 --- a/core/src/consensus.rs +++ b/core/src/consensus.rs @@ -1,5 +1,4 @@ use crate::bank_forks::BankForks; -use crate::staking_utils; use solana_metrics::datapoint_info; use solana_runtime::bank::Bank; use solana_sdk::account::Account; @@ -13,15 +12,6 @@ pub const VOTE_THRESHOLD_DEPTH: usize = 8; pub const VOTE_THRESHOLD_SIZE: f64 = 2f64 / 3f64; pub const MAX_RECENT_VOTES: usize = 16; -#[derive(Default)] -pub struct EpochStakes { - epoch: u64, - stakes: HashMap, - self_staked: u64, - total_staked: u64, - delegate_pubkey: Pubkey, -} - #[derive(Default, Debug)] pub struct StakeLockout { lockout: u64, @@ -39,89 +29,49 @@ impl StakeLockout { #[derive(Default)] pub struct Tower { - epoch_stakes: EpochStakes, + node_pubkey: Pubkey, threshold_depth: usize, threshold_size: f64, lockouts: VoteState, recent_votes: VecDeque, } -impl EpochStakes { - pub fn new(epoch: u64, stakes: HashMap, delegate_pubkey: &Pubkey) -> Self { - let total_staked = stakes.values().sum(); - let self_staked = *stakes.get(&delegate_pubkey).unwrap_or(&0); - Self { - epoch, - stakes, - total_staked, - self_staked, - delegate_pubkey: *delegate_pubkey, - } - } - pub fn new_for_tests(lamports: u64) -> Self { - Self::new( - 0, - vec![(Pubkey::default(), lamports)].into_iter().collect(), - &Pubkey::default(), - ) - } - pub fn new_from_stakes(epoch: u64, accounts: &[(Pubkey, (u64, Account))]) -> Self { - let stakes = accounts.iter().map(|(k, (v, _))| (*k, *v)).collect(); - Self::new(epoch, stakes, &accounts[0].0) - } - pub fn new_from_bank(bank: &Bank, my_pubkey: &Pubkey) -> Self { - let bank_epoch = bank.get_epoch_and_slot_index(bank.slot()).0; - let stakes = staking_utils::vote_account_stakes_at_epoch(bank, bank_epoch) - .expect("voting require a bank with stakes"); - Self::new(bank_epoch, stakes, my_pubkey) - } -} - impl Tower { - pub fn new_from_forks(bank_forks: &BankForks, my_pubkey: &Pubkey) -> Self { - let mut frozen_banks: Vec<_> = bank_forks.frozen_banks().values().cloned().collect(); - frozen_banks.sort_by_key(|b| (b.parents().len(), b.slot())); - let epoch_stakes = { - if let Some(bank) = frozen_banks.last() { - EpochStakes::new_from_bank(bank, my_pubkey) - } else { - return Self::default(); - } - }; - + pub fn new(node_pubkey: &Pubkey, vote_account_pubkey: &Pubkey, bank_forks: &BankForks) -> Self { let mut tower = Self { - epoch_stakes, + node_pubkey: *node_pubkey, threshold_depth: VOTE_THRESHOLD_DEPTH, threshold_size: VOTE_THRESHOLD_SIZE, lockouts: VoteState::default(), recent_votes: VecDeque::default(), }; - let bank = tower.find_heaviest_bank(bank_forks).unwrap(); - tower.lockouts = Self::initialize_lockouts_from_bank(&bank, tower.epoch_stakes.epoch); + tower.initialize_lockouts_from_bank_forks(&bank_forks, vote_account_pubkey); + tower } - pub fn new(epoch_stakes: EpochStakes, threshold_depth: usize, threshold_size: f64) -> Self { + + #[cfg(test)] + pub fn new_for_tests(threshold_depth: usize, threshold_size: f64) -> Self { Self { - epoch_stakes, threshold_depth, threshold_size, - lockouts: VoteState::default(), - recent_votes: VecDeque::default(), + ..Tower::default() } } + pub fn collect_vote_lockouts( &self, bank_slot: u64, vote_accounts: F, ancestors: &HashMap>, - ) -> HashMap + ) -> (HashMap, u64) where F: Iterator, { let mut stake_lockouts = HashMap::new(); - for (key, (_, account)) in vote_accounts { - let lamports: u64 = *self.epoch_stakes.stakes.get(&key).unwrap_or(&0); + let mut total_stake = 0; + for (key, (lamports, account)) in vote_accounts { if lamports == 0 { continue; } @@ -139,9 +89,7 @@ impl Tower { } let mut vote_state = vote_state.unwrap(); - if key == self.epoch_stakes.delegate_pubkey - || vote_state.node_pubkey == self.epoch_stakes.delegate_pubkey - { + if key == self.node_pubkey || vote_state.node_pubkey == self.node_pubkey { debug!("vote state {:?}", vote_state); debug!( "observed slot {}", @@ -200,50 +148,23 @@ impl Tower { // Update all the parents of this last vote with the stake of this vote account Self::update_ancestor_stakes(&mut stake_lockouts, vote.slot, lamports, ancestors); } + total_stake += lamports; } - stake_lockouts + (stake_lockouts, total_stake) } - pub fn is_slot_confirmed(&self, slot: u64, lockouts: &HashMap) -> bool { + pub fn is_slot_confirmed( + &self, + slot: u64, + lockouts: &HashMap, + total_staked: u64, + ) -> bool { lockouts .get(&slot) - .map(|lockout| { - (lockout.stake as f64 / self.epoch_stakes.total_staked as f64) > self.threshold_size - }) + .map(|lockout| (lockout.stake as f64 / total_staked as f64) > self.threshold_size) .unwrap_or(false) } - pub fn is_recent_epoch(&self, bank: &Bank) -> bool { - bank.epoch() >= self.epoch_stakes.epoch - } - - pub fn update_epoch(&mut self, bank: &Bank) { - trace!( - "updating bank epoch slot: {} epoch: {}", - bank.slot(), - self.epoch_stakes.epoch - ); - if bank.epoch() != self.epoch_stakes.epoch { - assert!( - self.is_recent_epoch(bank), - "epoch_stakes cannot move backwards" - ); - info!( - "Tower updated epoch bank slot: {} epoch: {}", - bank.slot(), - self.epoch_stakes.epoch - ); - self.epoch_stakes = - EpochStakes::new_from_bank(bank, &self.epoch_stakes.delegate_pubkey); - datapoint_info!( - "tower-epoch", - ("epoch", self.epoch_stakes.epoch, i64), - ("self_staked", self.epoch_stakes.self_staked, i64), - ("total_staked", self.epoch_stakes.total_staked, i64) - ); - } - } - pub fn record_vote(&mut self, slot: u64, hash: Hash) -> Option { let root_slot = self.lockouts.root_slot; let vote = Vote { slot, hash }; @@ -281,10 +202,6 @@ impl Tower { self.lockouts.root_slot } - pub fn total_epoch_stakes(&self) -> u64 { - self.epoch_stakes.total_staked - } - pub fn calculate_weight(&self, stake_lockouts: &HashMap) -> u128 { let mut sum = 0u128; let root_slot = self.lockouts.root_slot.unwrap_or(0); @@ -328,14 +245,14 @@ impl Tower { &self, slot: u64, stake_lockouts: &HashMap, + total_staked: u64, ) -> bool { let mut lockouts = self.lockouts.clone(); lockouts.process_slot_vote_unchecked(slot); let vote = lockouts.nth_recent_vote(self.threshold_depth); if let Some(vote) = vote { if let Some(fork_stake) = stake_lockouts.get(&vote.slot) { - (fork_stake.stake as f64 / self.epoch_stakes.total_staked as f64) - > self.threshold_size + (fork_stake.stake as f64 / total_staked as f64) > self.threshold_size } else { false } @@ -396,7 +313,7 @@ impl Tower { } fn bank_weight(&self, bank: &Bank, ancestors: &HashMap>) -> u128 { - let stake_lockouts = + let (stake_lockouts, _) = self.collect_vote_lockouts(bank.slot(), bank.vote_accounts().into_iter(), ancestors); self.calculate_weight(&stake_lockouts) } @@ -418,19 +335,22 @@ impl Tower { bank_weights.pop().map(|b| b.2) } - fn initialize_lockouts_from_bank(bank: &Bank, current_epoch: u64) -> VoteState { - let mut lockouts = VoteState::default(); - if let Some(iter) = bank.epoch_vote_accounts(current_epoch) { - for (delegate_pubkey, (_, account)) in iter { - if delegate_pubkey == bank.collector_id() { - let state = VoteState::deserialize(&account.data).expect("votes"); - if lockouts.votes.len() < state.votes.len() { - lockouts = state; - } - } + fn initialize_lockouts_from_bank_forks( + &mut self, + bank_forks: &BankForks, + vote_account_pubkey: &Pubkey, + ) { + if let Some(bank) = self.find_heaviest_bank(bank_forks) { + if let Some((_stake, vote_account)) = bank.vote_accounts().get(vote_account_pubkey) { + let vote_state = VoteState::deserialize(&vote_account.data) + .expect("vote_account isn't a VoteState?"); + assert_eq!( + vote_state.node_pubkey, self.node_pubkey, + "vote account's node_pubkey doesn't match", + ); + self.lockouts = vote_state; } - }; - lockouts + } } } @@ -456,32 +376,19 @@ mod test { stakes } - #[test] - fn test_collect_vote_lockouts_no_epoch_stakes() { - let accounts = gen_stakes(&[(1, &[0])]); - let epoch_stakes = EpochStakes::new_for_tests(2); - let tower = Tower::new(epoch_stakes, 0, 0.67); - let ancestors = vec![(1, vec![0].into_iter().collect()), (0, HashSet::new())] - .into_iter() - .collect(); - let staked_lockouts = tower.collect_vote_lockouts(1, accounts.into_iter(), &ancestors); - assert!(staked_lockouts.is_empty()); - assert_eq!(tower.epoch_stakes.total_staked, 2); - } - #[test] fn test_collect_vote_lockouts_sums() { //two accounts voting for slot 0 with 1 token staked let accounts = gen_stakes(&[(1, &[0]), (1, &[0])]); - let epoch_stakes = EpochStakes::new_from_stakes(0, &accounts); - let tower = Tower::new(epoch_stakes, 0, 0.67); + let tower = Tower::new_for_tests(0, 0.67); let ancestors = vec![(1, vec![0].into_iter().collect()), (0, HashSet::new())] .into_iter() .collect(); - let staked_lockouts = tower.collect_vote_lockouts(1, accounts.into_iter(), &ancestors); + let (staked_lockouts, total_staked) = + tower.collect_vote_lockouts(1, accounts.into_iter(), &ancestors); assert_eq!(staked_lockouts[&0].stake, 2); assert_eq!(staked_lockouts[&0].lockout, 2 + 2 + 4 + 4); - assert_eq!(tower.epoch_stakes.total_staked, 2); + assert_eq!(total_staked, 2); } #[test] @@ -489,15 +396,14 @@ mod test { let votes: Vec = (0..MAX_LOCKOUT_HISTORY as u64).into_iter().collect(); //two accounts voting for slot 0 with 1 token staked let accounts = gen_stakes(&[(1, &votes), (1, &votes)]); - let epoch_stakes = EpochStakes::new_from_stakes(0, &accounts); - let mut tower = Tower::new(epoch_stakes, 0, 0.67); + let mut tower = Tower::new_for_tests(0, 0.67); let mut ancestors = HashMap::new(); for i in 0..(MAX_LOCKOUT_HISTORY + 1) { tower.record_vote(i as u64, Hash::default()); ancestors.insert(i as u64, (0..i as u64).into_iter().collect()); } assert_eq!(tower.lockouts.root_slot, Some(0)); - let staked_lockouts = tower.collect_vote_lockouts( + let (staked_lockouts, _total_staked) = tower.collect_vote_lockouts( MAX_LOCKOUT_HISTORY as u64, accounts.into_iter(), &ancestors, @@ -511,7 +417,7 @@ mod test { #[test] fn test_calculate_weight_skips_root() { - let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67); + let mut tower = Tower::new_for_tests(0, 0.67); tower.lockouts.root_slot = Some(1); let stakes = vec![ ( @@ -536,7 +442,7 @@ mod test { #[test] fn test_calculate_weight() { - let tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67); + let tower = Tower::new_for_tests(0, 0.67); let stakes = vec![( 0, StakeLockout { @@ -551,7 +457,7 @@ mod test { #[test] fn test_check_vote_threshold_without_votes() { - let tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67); + let tower = Tower::new_for_tests(1, 0.67); let stakes = vec![( 0, StakeLockout { @@ -561,12 +467,12 @@ mod test { )] .into_iter() .collect(); - assert!(tower.check_vote_stake_threshold(0, &stakes)); + assert!(tower.check_vote_stake_threshold(0, &stakes, 2)); } #[test] fn test_aggregate_stake_lockouts() { - let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67); + let mut tower = Tower::new_for_tests(0, 0.67); tower.lockouts.root_slot = Some(1); let stakes = vec![ ( @@ -619,7 +525,7 @@ mod test { #[test] fn test_is_slot_confirmed_not_enough_stake_failure() { - let tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67); + let tower = Tower::new_for_tests(1, 0.67); let stakes = vec![( 0, StakeLockout { @@ -629,19 +535,19 @@ mod test { )] .into_iter() .collect(); - assert!(!tower.is_slot_confirmed(0, &stakes)); + assert!(!tower.is_slot_confirmed(0, &stakes, 2)); } #[test] fn test_is_slot_confirmed_unknown_slot() { - let tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67); + let tower = Tower::new_for_tests(1, 0.67); let stakes = HashMap::new(); - assert!(!tower.is_slot_confirmed(0, &stakes)); + assert!(!tower.is_slot_confirmed(0, &stakes, 2)); } #[test] fn test_is_slot_confirmed_pass() { - let tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67); + let tower = Tower::new_for_tests(1, 0.67); let stakes = vec![( 0, StakeLockout { @@ -651,19 +557,19 @@ mod test { )] .into_iter() .collect(); - assert!(tower.is_slot_confirmed(0, &stakes)); + assert!(tower.is_slot_confirmed(0, &stakes, 2)); } #[test] fn test_is_locked_out_empty() { - let tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67); + let tower = Tower::new_for_tests(0, 0.67); let descendants = HashMap::new(); assert!(!tower.is_locked_out(0, &descendants)); } #[test] fn test_is_locked_out_root_slot_child_pass() { - let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67); + let mut tower = Tower::new_for_tests(0, 0.67); let descendants = vec![(0, vec![1].into_iter().collect())] .into_iter() .collect(); @@ -673,7 +579,7 @@ mod test { #[test] fn test_is_locked_out_root_slot_sibling_fail() { - let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67); + let mut tower = Tower::new_for_tests(0, 0.67); let descendants = vec![(0, vec![1].into_iter().collect())] .into_iter() .collect(); @@ -683,7 +589,7 @@ mod test { #[test] fn test_check_already_voted() { - let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67); + let mut tower = Tower::new_for_tests(0, 0.67); tower.record_vote(0, Hash::default()); assert!(tower.has_voted(0)); assert!(!tower.has_voted(1)); @@ -691,7 +597,7 @@ mod test { #[test] fn test_is_locked_out_double_vote() { - let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67); + let mut tower = Tower::new_for_tests(0, 0.67); let descendants = vec![(0, vec![1].into_iter().collect()), (1, HashSet::new())] .into_iter() .collect(); @@ -702,7 +608,7 @@ mod test { #[test] fn test_is_locked_out_child() { - let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67); + let mut tower = Tower::new_for_tests(0, 0.67); let descendants = vec![(0, vec![1].into_iter().collect())] .into_iter() .collect(); @@ -712,7 +618,7 @@ mod test { #[test] fn test_is_locked_out_sibling() { - let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67); + let mut tower = Tower::new_for_tests(0, 0.67); let descendants = vec![ (0, vec![1, 2].into_iter().collect()), (1, HashSet::new()), @@ -727,7 +633,7 @@ mod test { #[test] fn test_is_locked_out_last_vote_expired() { - let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67); + let mut tower = Tower::new_for_tests(0, 0.67); let descendants = vec![(0, vec![1, 4].into_iter().collect()), (1, HashSet::new())] .into_iter() .collect(); @@ -743,7 +649,7 @@ mod test { #[test] fn test_check_vote_threshold_below_threshold() { - let mut tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67); + let mut tower = Tower::new_for_tests(1, 0.67); let stakes = vec![( 0, StakeLockout { @@ -754,11 +660,11 @@ mod test { .into_iter() .collect(); tower.record_vote(0, Hash::default()); - assert!(!tower.check_vote_stake_threshold(1, &stakes)); + assert!(!tower.check_vote_stake_threshold(1, &stakes, 2)); } #[test] fn test_check_vote_threshold_above_threshold() { - let mut tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67); + let mut tower = Tower::new_for_tests(1, 0.67); let stakes = vec![( 0, StakeLockout { @@ -769,12 +675,12 @@ mod test { .into_iter() .collect(); tower.record_vote(0, Hash::default()); - assert!(tower.check_vote_stake_threshold(1, &stakes)); + assert!(tower.check_vote_stake_threshold(1, &stakes, 2)); } #[test] fn test_check_vote_threshold_above_threshold_after_pop() { - let mut tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67); + let mut tower = Tower::new_for_tests(1, 0.67); let stakes = vec![( 0, StakeLockout { @@ -787,15 +693,15 @@ mod test { tower.record_vote(0, Hash::default()); tower.record_vote(1, Hash::default()); tower.record_vote(2, Hash::default()); - assert!(tower.check_vote_stake_threshold(6, &stakes)); + assert!(tower.check_vote_stake_threshold(6, &stakes, 2)); } #[test] fn test_check_vote_threshold_above_threshold_no_stake() { - let mut tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67); + let mut tower = Tower::new_for_tests(1, 0.67); let stakes = HashMap::new(); tower.record_vote(0, Hash::default()); - assert!(!tower.check_vote_stake_threshold(1, &stakes)); + assert!(!tower.check_vote_stake_threshold(1, &stakes, 2)); } #[test] @@ -875,9 +781,7 @@ mod test { ]); // Initialize tower - let stakes: HashMap<_, _> = accounts.iter().map(|(pk, (s, _))| (*pk, *s)).collect(); - let epoch_stakes = EpochStakes::new(0, stakes, &Pubkey::default()); - let mut tower = Tower::new(epoch_stakes, VOTE_THRESHOLD_DEPTH, threshold_size); + let mut tower = Tower::new_for_tests(VOTE_THRESHOLD_DEPTH, threshold_size); // CASE 1: Record the first VOTE_THRESHOLD tower votes for fork 2. We want to // evaluate a vote on slot VOTE_THRESHOLD_DEPTH. The nth most recent vote should be @@ -887,21 +791,25 @@ mod test { for vote in &tower_votes { tower.record_vote(*vote, Hash::default()); } - let staked_lockouts = + let (staked_lockouts, total_staked) = tower.collect_vote_lockouts(vote_to_evaluate, accounts.clone().into_iter(), &ancestors); - assert!(tower.check_vote_stake_threshold(vote_to_evaluate, &staked_lockouts)); + assert!(tower.check_vote_stake_threshold(vote_to_evaluate, &staked_lockouts, total_staked)); // CASE 2: Now we want to evaluate a vote for slot VOTE_THRESHOLD_DEPTH + 1. This slot // will expire the vote in one of the vote accounts, so we should have insufficient // stake to pass the threshold let vote_to_evaluate = VOTE_THRESHOLD_DEPTH as u64 + 1; - let staked_lockouts = + let (staked_lockouts, total_staked) = tower.collect_vote_lockouts(vote_to_evaluate, accounts.into_iter(), &ancestors); - assert!(!tower.check_vote_stake_threshold(vote_to_evaluate, &staked_lockouts)); + assert!(!tower.check_vote_stake_threshold( + vote_to_evaluate, + &staked_lockouts, + total_staked + )); } fn vote_and_check_recent(num_votes: usize) { - let mut tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67); + let mut tower = Tower::new_for_tests(1, 0.67); let start = num_votes.saturating_sub(MAX_RECENT_VOTES); let expected: Vec<_> = (start..num_votes) .map(|i| Vote::new(i as u64, Hash::default())) diff --git a/core/src/replay_stage.rs b/core/src/replay_stage.rs index 9d213a413e..ef67ea7aff 100644 --- a/core/src/replay_stage.rs +++ b/core/src/replay_stage.rs @@ -104,7 +104,7 @@ impl ReplayStage { let bank_forks = bank_forks.clone(); let poh_recorder = poh_recorder.clone(); let my_pubkey = *my_pubkey; - let mut tower = Tower::new_from_forks(&bank_forks.read().unwrap(), &my_pubkey); + let mut tower = Tower::new(&my_pubkey, &vote_account, &bank_forks.read().unwrap()); // Start the replay stage loop let leader_schedule_cache = leader_schedule_cache.clone(); let vote_account = *vote_account; @@ -144,7 +144,7 @@ impl ReplayStage { let votable = Self::generate_votable_banks(&bank_forks, &tower, &mut progress); - if let Some((_, bank, lockouts)) = votable.into_iter().last() { + if let Some((_, bank, lockouts, total_staked)) = votable.into_iter().last() { subscriptions.notify_subscribers(bank.slot(), &bank_forks); if let Some(votable_leader) = @@ -181,6 +181,7 @@ impl ReplayStage { &leader_schedule_cache, &root_bank_sender, lockouts, + total_staked, &lockouts_sender, &snapshot_package_sender, )?; @@ -399,6 +400,7 @@ impl ReplayStage { leader_schedule_cache: &Arc, root_bank_sender: &Sender>>, lockouts: HashMap, + total_staked: u64, lockouts_sender: &Sender, snapshot_package_sender: &Option, ) -> Result<()> @@ -435,8 +437,8 @@ impl ReplayStage { Err(e)?; } } - Self::update_confidence_cache(bank_forks, tower, lockouts, lockouts_sender); - tower.update_epoch(&bank); + Self::update_confidence_cache(bank_forks, tower, lockouts, total_staked, lockouts_sender); + if let Some(ref voting_keypair) = voting_keypair { let node_keypair = cluster_info.read().unwrap().keypair.clone(); @@ -462,21 +464,23 @@ impl ReplayStage { bank_forks: &Arc>, tower: &Tower, lockouts: HashMap, + total_staked: u64, lockouts_sender: &Sender, ) { - let total_epoch_stakes = tower.total_epoch_stakes(); - let mut w_bank_forks = bank_forks.write().unwrap(); - for (fork, stake_lockout) in lockouts.iter() { - if tower.root().is_none() || *fork >= tower.root().unwrap() { - w_bank_forks.cache_fork_confidence( - *fork, - stake_lockout.stake(), - total_epoch_stakes, - stake_lockout.lockout(), - ); + { + let mut bank_forks = bank_forks.write().unwrap(); + for (fork, stake_lockout) in lockouts.iter() { + if tower.root().is_none() || *fork >= tower.root().unwrap() { + bank_forks.cache_fork_confidence( + *fork, + stake_lockout.stake(), + total_staked, + stake_lockout.lockout(), + ); + } } } - drop(w_bank_forks); + let bank_forks_clone = bank_forks.clone(); let root = tower.root(); @@ -560,11 +564,12 @@ impl ReplayStage { did_complete_bank } + #[allow(clippy::type_complexity)] fn generate_votable_banks( bank_forks: &Arc>, tower: &Tower, progress: &mut HashMap, - ) -> Vec<(u128, Arc, HashMap)> { + ) -> Vec<(u128, Arc, HashMap, u64)> { let tower_start = Instant::now(); // Tower voting let descendants = bank_forks.read().unwrap().descendants(); @@ -572,18 +577,13 @@ impl ReplayStage { let frozen_banks = bank_forks.read().unwrap().frozen_banks(); trace!("frozen_banks {}", frozen_banks.len()); - let mut votable: Vec<(u128, Arc, HashMap)> = frozen_banks + let mut votable: Vec<(u128, Arc, HashMap, u64)> = frozen_banks .values() .filter(|b| { let is_votable = b.is_votable(); trace!("bank is votable: {} {}", b.slot(), is_votable); is_votable }) - .filter(|b| { - let is_recent_epoch = tower.is_recent_epoch(b); - trace!("bank is is_recent_epoch: {} {}", b.slot(), is_recent_epoch); - is_recent_epoch - }) .filter(|b| { let has_voted = tower.has_voted(b.slot()); trace!("bank is has_voted: {} {}", b.slot(), has_voted); @@ -604,17 +604,19 @@ impl ReplayStage { ), ) }) - .filter(|(b, stake_lockouts)| { - let vote_threshold = tower.check_vote_stake_threshold(b.slot(), &stake_lockouts); - Self::confirm_forks(tower, stake_lockouts, progress, bank_forks); + .filter(|(b, (stake_lockouts, total_staked))| { + let vote_threshold = + tower.check_vote_stake_threshold(b.slot(), &stake_lockouts, *total_staked); + Self::confirm_forks(tower, &stake_lockouts, *total_staked, progress, bank_forks); debug!("bank vote_threshold: {} {}", b.slot(), vote_threshold); vote_threshold }) - .map(|(b, stake_lockouts)| { + .map(|(b, (stake_lockouts, total_staked))| { ( tower.calculate_weight(&stake_lockouts), b.clone(), stake_lockouts, + total_staked, ) }) .collect(); @@ -641,12 +643,13 @@ impl ReplayStage { fn confirm_forks( tower: &Tower, stake_lockouts: &HashMap, + total_staked: u64, progress: &mut HashMap, bank_forks: &Arc>, ) { progress.retain(|slot, prog| { let duration = timing::timestamp() - prog.started_ms; - if tower.is_slot_confirmed(*slot, stake_lockouts) + if tower.is_slot_confirmed(*slot, stake_lockouts, total_staked) && bank_forks .read() .unwrap() @@ -1062,13 +1065,19 @@ mod test { vec![0], ))); let pubkey = Pubkey::new_rand(); - let mut tower = Tower::new_from_forks(&bank_forks.read().unwrap(), &pubkey); + let mut tower = Tower::new(&pubkey, &Pubkey::new_rand(), &bank_forks.read().unwrap()); let mut progress = HashMap::new(); leader_vote(&arc_bank0, &leader_voting_pubkey); let votable = ReplayStage::generate_votable_banks(&bank_forks, &tower, &mut progress); - if let Some((_, _, lockouts)) = votable.into_iter().last() { - ReplayStage::update_confidence_cache(&bank_forks, &tower, lockouts, &lockouts_sender); + if let Some((_, _, lockouts, total_staked)) = votable.into_iter().last() { + ReplayStage::update_confidence_cache( + &bank_forks, + &tower, + lockouts, + total_staked, + &lockouts_sender, + ); } assert_eq!( @@ -1089,8 +1098,14 @@ mod test { let arc_bank1 = bank_forks.read().unwrap().get(1).unwrap().clone(); leader_vote(&arc_bank1, &leader_voting_pubkey); let votable = ReplayStage::generate_votable_banks(&bank_forks, &tower, &mut progress); - if let Some((_, _, lockouts)) = votable.into_iter().last() { - ReplayStage::update_confidence_cache(&bank_forks, &tower, lockouts, &lockouts_sender); + if let Some((_, _, lockouts, total_staked)) = votable.into_iter().last() { + ReplayStage::update_confidence_cache( + &bank_forks, + &tower, + lockouts, + total_staked, + &lockouts_sender, + ); } tower.record_vote(arc_bank1.slot(), arc_bank1.hash()); @@ -1105,8 +1120,14 @@ mod test { let arc_bank2 = bank_forks.read().unwrap().get(2).unwrap().clone(); leader_vote(&arc_bank2, &leader_voting_pubkey); let votable = ReplayStage::generate_votable_banks(&bank_forks, &tower, &mut progress); - if let Some((_, _, lockouts)) = votable.into_iter().last() { - ReplayStage::update_confidence_cache(&bank_forks, &tower, lockouts, &lockouts_sender); + if let Some((_, _, lockouts, total_staked)) = votable.into_iter().last() { + ReplayStage::update_confidence_cache( + &bank_forks, + &tower, + lockouts, + total_staked, + &lockouts_sender, + ); } thread::sleep(Duration::from_millis(200)); diff --git a/core/src/staking_utils.rs b/core/src/staking_utils.rs index 0c6e06c52b..a3c0bd70d6 100644 --- a/core/src/staking_utils.rs +++ b/core/src/staking_utils.rs @@ -29,20 +29,6 @@ pub fn staked_nodes(bank: &Bank) -> HashMap { to_staked_nodes(to_vote_states(bank.vote_accounts().into_iter())) } -/// At the specified epoch, collect the node account balance and vote states for nodes that -/// have non-zero balance in their corresponding staking accounts -pub fn vote_account_stakes_at_epoch( - bank: &Bank, - epoch_height: u64, -) -> Option> { - bank.epoch_vote_accounts(epoch_height).map(|accounts| { - accounts - .iter() - .map(|(id, (stake, _))| (*id, *stake)) - .collect() - }) -} - /// At the specified epoch, collect the delegate account balance and vote states for delegates /// that have non-zero balance in any of their managed staking accounts pub fn staked_nodes_at_epoch(bank: &Bank, epoch_height: u64) -> Option> { @@ -112,10 +98,7 @@ where #[cfg(test)] pub(crate) mod tests { use super::*; - use crate::genesis_utils::{ - create_genesis_block, create_genesis_block_with_leader, GenesisBlockInfo, - BOOTSTRAP_LEADER_LAMPORTS, - }; + use crate::genesis_utils::{create_genesis_block, GenesisBlockInfo, BOOTSTRAP_LEADER_LAMPORTS}; use solana_sdk::instruction::Instruction; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::{Keypair, KeypairUtil}; @@ -130,39 +113,6 @@ pub(crate) mod tests { Bank::new_from_parent(parent, &Pubkey::default(), slot) } - #[test] - fn test_vote_account_stakes_at_epoch() { - let GenesisBlockInfo { - genesis_block, - voting_keypair, - .. - } = create_genesis_block_with_leader(1, &Pubkey::new_rand(), BOOTSTRAP_LEADER_LAMPORTS); - - let bank = Bank::new(&genesis_block); - - // Epoch doesn't exist - let mut expected = HashMap::new(); - assert_eq!(vote_account_stakes_at_epoch(&bank, 10), None); - - let leader_stake = Stake { - stake: BOOTSTRAP_LEADER_LAMPORTS, - activated: std::u64::MAX, // exempt from warmup - ..Stake::default() - }; - - // First epoch has the bootstrap leader - expected.insert(voting_keypair.pubkey(), leader_stake.stake(0, None)); - - // henceforth, verify that we have snapshots of stake at epoch 0 - let expected = Some(expected); - assert_eq!(vote_account_stakes_at_epoch(&bank, 0), expected); - - // Second epoch carries same information - let bank = new_from_parent(&Arc::new(bank), 1); - assert_eq!(vote_account_stakes_at_epoch(&bank, 0), expected); - assert_eq!(vote_account_stakes_at_epoch(&bank, 1), expected); - } - pub(crate) fn setup_vote_and_stake_accounts( bank: &Bank, from_account: &Keypair,