diff --git a/ledger/src/staking_utils.rs b/ledger/src/staking_utils.rs index 26fdaf68ad..b2d29a88ab 100644 --- a/ledger/src/staking_utils.rs +++ b/ledger/src/staking_utils.rs @@ -1,75 +1,13 @@ -use solana_runtime::bank::Bank; -use solana_sdk::{ - clock::{Epoch, Slot}, - pubkey::Pubkey, -}; -use std::collections::HashMap; - -/// Looks through vote accounts, and finds the latest slot that has achieved -/// supermajority lockout -pub fn get_supermajority_slot(bank: &Bank, epoch: Epoch) -> Option { - // Find the amount of stake needed for supermajority - let stakes_and_lockouts = epoch_stakes_and_lockouts(bank, epoch); - let total_stake: u64 = stakes_and_lockouts.iter().map(|s| s.0).sum(); - let supermajority_stake = total_stake * 2 / 3; - - // Filter out the states that don't have a max lockout - find_supermajority_slot(supermajority_stake, stakes_and_lockouts.iter()) -} - -pub fn vote_account_stakes(bank: &Bank) -> HashMap { - bank.vote_accounts() - .into_iter() - .map(|(id, (stake, _))| (id, stake)) - .collect() -} - -fn epoch_stakes_and_lockouts(bank: &Bank, epoch: Epoch) -> Vec<(u64, Option)> { - bank.epoch_vote_accounts(epoch) - .expect("Bank state for epoch is missing") - .iter() - .filter_map(|(_ /*vote pubkey*/, (stake, vote_account))| { - let root_slot = vote_account.vote_state().as_ref().ok()?.root_slot; - Some((*stake, root_slot)) - }) - .collect() -} - -fn find_supermajority_slot<'a, I>(supermajority_stake: u64, stakes_and_lockouts: I) -> Option -where - I: Iterator)>, -{ - // Filter out the states that don't have a max lockout - let mut stakes_and_lockouts: Vec<_> = stakes_and_lockouts - .filter_map(|(stake, slot)| slot.map(|s| (stake, s))) - .collect(); - - // Sort by the root slot, in descending order - stakes_and_lockouts.sort_unstable_by(|s1, s2| s1.1.cmp(&s2.1).reverse()); - - // Find if any slot has achieved sufficient votes for supermajority lockout - let mut total = 0; - for (stake, slot) in stakes_and_lockouts { - total += *stake; - if total > supermajority_stake { - return Some(slot); - } - } - - None -} - #[cfg(test)] pub(crate) mod tests { use { - super::*, - crate::genesis_utils::{ - bootstrap_validator_stake_lamports, create_genesis_config, GenesisConfigInfo, - }, rand::Rng, - solana_runtime::vote_account::{VoteAccount, VoteAccounts}, + solana_runtime::{ + bank::Bank, + vote_account::{VoteAccount, VoteAccounts}, + }, solana_sdk::{ - account::{from_account, AccountSharedData}, + account::AccountSharedData, clock::Clock, instruction::Instruction, pubkey::Pubkey, @@ -77,22 +15,16 @@ pub(crate) mod tests { signers::Signers, stake::{ instruction as stake_instruction, - state::{Authorized, Delegation, Lockup, Stake}, + state::{Authorized, Lockup}, }, - sysvar::stake_history::{self, StakeHistory}, transaction::Transaction, }, solana_vote_program::{ vote_instruction, vote_state::{VoteInit, VoteState, VoteStateVersions}, }, - std::sync::Arc, }; - fn new_from_parent(parent: &Arc, slot: Slot) -> Bank { - Bank::new_from_parent(parent, &Pubkey::default(), slot) - } - pub(crate) fn setup_vote_and_stake_accounts( bank: &Bank, from_account: &Keypair, @@ -144,140 +76,6 @@ pub(crate) mod tests { ); } - #[test] - fn test_epoch_stakes_and_lockouts() { - solana_logger::setup(); - let stake = bootstrap_validator_stake_lamports(); - let leader_stake = Stake { - delegation: Delegation { - stake: bootstrap_validator_stake_lamports(), - activation_epoch: std::u64::MAX, // mark as bootstrap - ..Delegation::default() - }, - ..Stake::default() - }; - - let validator = Keypair::new(); - - let GenesisConfigInfo { - genesis_config, - mint_keypair, - .. - } = create_genesis_config(10_000 * bootstrap_validator_stake_lamports()); - - let bank = Bank::new_for_tests(&genesis_config); - let vote_account = Keypair::new(); - - // Give the validator some stake but don't setup a staking account - // Validator has no lamports staked, so they get filtered out. Only the bootstrap validator - // created by the genesis config will get included - bank.transfer(1, &mint_keypair, &validator.pubkey()) - .unwrap(); - - // Make a mint vote account. Because the mint has nonzero stake, this - // should show up in the active set - setup_vote_and_stake_accounts(&bank, &mint_keypair, &vote_account, &mint_keypair, stake); - - // simulated stake - let other_stake = Stake { - delegation: Delegation { - stake, - activation_epoch: bank.epoch(), - ..Delegation::default() - }, - ..Stake::default() - }; - - let first_leader_schedule_epoch = bank.get_leader_schedule_epoch(bank.slot()); - // find the first slot in the next leader schedule epoch - let mut slot = bank.slot(); - loop { - slot += 1; - if bank.get_leader_schedule_epoch(slot) != first_leader_schedule_epoch { - break; - } - } - let bank = new_from_parent(&Arc::new(bank), slot); - let next_leader_schedule_epoch = bank.get_leader_schedule_epoch(slot); - - let result: Vec<_> = epoch_stakes_and_lockouts(&bank, first_leader_schedule_epoch); - assert_eq!( - result, - vec![( - leader_stake.stake(first_leader_schedule_epoch, None, true), - None - )] - ); - - // epoch stakes and lockouts are saved off for the future epoch, should - // match current bank state - let mut result: Vec<_> = epoch_stakes_and_lockouts(&bank, next_leader_schedule_epoch); - result.sort(); - let stake_history = - from_account::(&bank.get_account(&stake_history::id()).unwrap()) - .unwrap(); - let mut expected = vec![ - ( - leader_stake.stake(bank.epoch(), Some(&stake_history), true), - None, - ), - ( - other_stake.stake(bank.epoch(), Some(&stake_history), true), - None, - ), - ]; - - expected.sort(); - assert_eq!(result, expected); - } - - #[test] - fn test_find_supermajority_slot() { - let supermajority = 10; - - let stakes_and_slots = vec![]; - assert_eq!( - find_supermajority_slot(supermajority, stakes_and_slots.iter()), - None - ); - - let stakes_and_slots = vec![(5, None), (5, None)]; - assert_eq!( - find_supermajority_slot(supermajority, stakes_and_slots.iter()), - None - ); - - let stakes_and_slots = vec![(5, None), (5, None), (9, Some(2))]; - assert_eq!( - find_supermajority_slot(supermajority, stakes_and_slots.iter()), - None - ); - - let stakes_and_slots = vec![(5, None), (5, None), (9, Some(2)), (1, Some(3))]; - assert_eq!( - find_supermajority_slot(supermajority, stakes_and_slots.iter()), - None - ); - - let stakes_and_slots = vec![(5, None), (5, None), (9, Some(2)), (2, Some(3))]; - assert_eq!( - find_supermajority_slot(supermajority, stakes_and_slots.iter()), - Some(2) - ); - - let stakes_and_slots = vec![(9, Some(2)), (2, Some(3)), (9, None)]; - assert_eq!( - find_supermajority_slot(supermajority, stakes_and_slots.iter()), - Some(2) - ); - - let stakes_and_slots = vec![(9, Some(2)), (2, Some(3)), (9, Some(3))]; - assert_eq!( - find_supermajority_slot(supermajority, stakes_and_slots.iter()), - Some(3) - ); - } - #[test] fn test_to_staked_nodes() { let mut stakes = Vec::new();