implements copy-on-write for vote-accounts (#19362)
Bank::vote_accounts redundantly clones vote-accounts HashMap even though an immutable reference will suffice: https://github.com/solana-labs/solana/blob/95c998a19/runtime/src/bank.rs#L5174-L5186 This commit implements copy-on-write semantics for vote-accounts by wrapping the underlying HashMap in Arc<...>.
This commit is contained in:
@ -29,7 +29,6 @@ use solana_runtime::{
|
||||
bank_forks::BankForks,
|
||||
commitment::VOTE_THRESHOLD_SIZE,
|
||||
epoch_stakes::{EpochAuthorizedVoters, EpochStakes},
|
||||
stakes::Stakes,
|
||||
vote_sender_types::{ReplayVoteReceiver, ReplayedVote},
|
||||
};
|
||||
use solana_sdk::{
|
||||
@ -601,7 +600,7 @@ impl ClusterInfoVoteListener {
|
||||
// The last vote slot, which is the greatest slot in the stack
|
||||
// of votes in a vote transaction, qualifies for optimistic confirmation.
|
||||
if slot == last_vote_slot {
|
||||
let vote_accounts = Stakes::vote_accounts(epoch_stakes.stakes());
|
||||
let vote_accounts = epoch_stakes.stakes().vote_accounts();
|
||||
let stake = vote_accounts
|
||||
.get(vote_pubkey)
|
||||
.map(|(stake, _)| *stake)
|
||||
|
@ -183,8 +183,8 @@ impl AggregateCommitmentService {
|
||||
|
||||
let mut commitment = HashMap::new();
|
||||
let mut rooted_stake: Vec<(Slot, u64)> = Vec::new();
|
||||
for (_, (lamports, account)) in bank.vote_accounts().into_iter() {
|
||||
if lamports == 0 {
|
||||
for (lamports, account) in bank.vote_accounts().values() {
|
||||
if *lamports == 0 {
|
||||
continue;
|
||||
}
|
||||
if let Ok(vote_state) = account.vote_state().as_ref() {
|
||||
@ -193,7 +193,7 @@ impl AggregateCommitmentService {
|
||||
&mut rooted_stake,
|
||||
vote_state,
|
||||
ancestors,
|
||||
lamports,
|
||||
*lamports,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -198,17 +198,14 @@ impl Tower {
|
||||
Self::new(node_pubkey, vote_account, root, &heaviest_bank)
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vote_lockouts<F>(
|
||||
pub(crate) fn collect_vote_lockouts(
|
||||
vote_account_pubkey: &Pubkey,
|
||||
bank_slot: Slot,
|
||||
vote_accounts: F,
|
||||
vote_accounts: &HashMap<Pubkey, (/*stake:*/ u64, VoteAccount)>,
|
||||
ancestors: &HashMap<Slot, HashSet<Slot>>,
|
||||
get_frozen_hash: impl Fn(Slot) -> Option<Hash>,
|
||||
latest_validator_votes_for_frozen_banks: &mut LatestValidatorVotesForFrozenBanks,
|
||||
) -> ComputedBankState
|
||||
where
|
||||
F: IntoIterator<Item = (Pubkey, (u64, VoteAccount))>,
|
||||
{
|
||||
) -> ComputedBankState {
|
||||
let mut vote_slots = HashSet::new();
|
||||
let mut voted_stakes = HashMap::new();
|
||||
let mut total_stake = 0;
|
||||
@ -217,7 +214,8 @@ impl Tower {
|
||||
// keyed by end of the range
|
||||
let mut lockout_intervals = LockoutIntervals::new();
|
||||
let mut my_latest_landed_vote = None;
|
||||
for (key, (voted_stake, account)) in vote_accounts {
|
||||
for (&key, (voted_stake, account)) in vote_accounts.iter() {
|
||||
let voted_stake = *voted_stake;
|
||||
if voted_stake == 0 {
|
||||
continue;
|
||||
}
|
||||
@ -1270,56 +1268,60 @@ pub fn reconcile_blockstore_roots_with_tower(
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use crate::{
|
||||
fork_choice::ForkChoice, heaviest_subtree_fork_choice::SlotHashKey,
|
||||
replay_stage::HeaviestForkFailures, tower_storage::FileTowerStorage,
|
||||
vote_simulator::VoteSimulator,
|
||||
use {
|
||||
super::*,
|
||||
crate::{
|
||||
fork_choice::ForkChoice, heaviest_subtree_fork_choice::SlotHashKey,
|
||||
replay_stage::HeaviestForkFailures, tower_storage::FileTowerStorage,
|
||||
vote_simulator::VoteSimulator,
|
||||
},
|
||||
itertools::Itertools,
|
||||
solana_ledger::{blockstore::make_slot_entries, get_tmp_ledger_path},
|
||||
solana_runtime::bank::Bank,
|
||||
solana_sdk::{
|
||||
account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
|
||||
clock::Slot,
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::Signer,
|
||||
slot_history::SlotHistory,
|
||||
},
|
||||
solana_vote_program::vote_state::{Vote, VoteStateVersions, MAX_LOCKOUT_HISTORY},
|
||||
std::{
|
||||
collections::HashMap,
|
||||
fs::{remove_file, OpenOptions},
|
||||
io::{Read, Seek, SeekFrom, Write},
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
},
|
||||
tempfile::TempDir,
|
||||
trees::tr,
|
||||
};
|
||||
use solana_ledger::{blockstore::make_slot_entries, get_tmp_ledger_path};
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_sdk::{
|
||||
account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
|
||||
clock::Slot,
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::Signer,
|
||||
slot_history::SlotHistory,
|
||||
};
|
||||
use solana_vote_program::vote_state::{Vote, VoteStateVersions, MAX_LOCKOUT_HISTORY};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs::{remove_file, OpenOptions},
|
||||
io::{Read, Seek, SeekFrom, Write},
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
};
|
||||
use tempfile::TempDir;
|
||||
use trees::tr;
|
||||
|
||||
fn gen_stakes(stake_votes: &[(u64, &[u64])]) -> Vec<(Pubkey, (u64, VoteAccount))> {
|
||||
let mut stakes = vec![];
|
||||
for (lamports, votes) in stake_votes {
|
||||
let mut account = AccountSharedData::from(Account {
|
||||
data: vec![0; VoteState::size_of()],
|
||||
lamports: *lamports,
|
||||
..Account::default()
|
||||
});
|
||||
let mut vote_state = VoteState::default();
|
||||
for slot in *votes {
|
||||
vote_state.process_slot_vote_unchecked(*slot);
|
||||
}
|
||||
VoteState::serialize(
|
||||
&VoteStateVersions::new_current(vote_state),
|
||||
&mut account.data_as_mut_slice(),
|
||||
)
|
||||
.expect("serialize state");
|
||||
stakes.push((
|
||||
solana_sdk::pubkey::new_rand(),
|
||||
(*lamports, VoteAccount::from(account)),
|
||||
));
|
||||
}
|
||||
stakes
|
||||
fn gen_stakes(stake_votes: &[(u64, &[u64])]) -> HashMap<Pubkey, (u64, VoteAccount)> {
|
||||
stake_votes
|
||||
.iter()
|
||||
.map(|(lamports, votes)| {
|
||||
let mut account = AccountSharedData::from(Account {
|
||||
data: vec![0; VoteState::size_of()],
|
||||
lamports: *lamports,
|
||||
..Account::default()
|
||||
});
|
||||
let mut vote_state = VoteState::default();
|
||||
for slot in *votes {
|
||||
vote_state.process_slot_vote_unchecked(*slot);
|
||||
}
|
||||
VoteState::serialize(
|
||||
&VoteStateVersions::new_current(vote_state),
|
||||
&mut account.data_as_mut_slice(),
|
||||
)
|
||||
.expect("serialize state");
|
||||
(
|
||||
solana_sdk::pubkey::new_rand(),
|
||||
(*lamports, VoteAccount::from(account)),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1964,10 +1966,10 @@ pub mod test {
|
||||
#[test]
|
||||
fn test_collect_vote_lockouts_sums() {
|
||||
//two accounts voting for slot 0 with 1 token staked
|
||||
let mut accounts = gen_stakes(&[(1, &[0]), (1, &[0])]);
|
||||
accounts.sort_by_key(|(pk, _)| *pk);
|
||||
let accounts = gen_stakes(&[(1, &[0]), (1, &[0])]);
|
||||
let account_latest_votes: Vec<(Pubkey, SlotHashKey)> = accounts
|
||||
.iter()
|
||||
.sorted_by_key(|(pk, _)| *pk)
|
||||
.map(|(pubkey, _)| (*pubkey, (0, Hash::default())))
|
||||
.collect();
|
||||
|
||||
@ -1984,7 +1986,7 @@ pub mod test {
|
||||
} = Tower::collect_vote_lockouts(
|
||||
&Pubkey::default(),
|
||||
1,
|
||||
accounts.into_iter(),
|
||||
&accounts,
|
||||
&ancestors,
|
||||
|_| Some(Hash::default()),
|
||||
&mut latest_validator_votes_for_frozen_banks,
|
||||
@ -2004,10 +2006,10 @@ pub mod test {
|
||||
fn test_collect_vote_lockouts_root() {
|
||||
let votes: Vec<u64> = (0..MAX_LOCKOUT_HISTORY as u64).collect();
|
||||
//two accounts voting for slots 0..MAX_LOCKOUT_HISTORY with 1 token staked
|
||||
let mut accounts = gen_stakes(&[(1, &votes), (1, &votes)]);
|
||||
accounts.sort_by_key(|(pk, _)| *pk);
|
||||
let accounts = gen_stakes(&[(1, &votes), (1, &votes)]);
|
||||
let account_latest_votes: Vec<(Pubkey, SlotHashKey)> = accounts
|
||||
.iter()
|
||||
.sorted_by_key(|(pk, _)| *pk)
|
||||
.map(|(pubkey, _)| {
|
||||
(
|
||||
*pubkey,
|
||||
@ -2044,7 +2046,7 @@ pub mod test {
|
||||
} = Tower::collect_vote_lockouts(
|
||||
&Pubkey::default(),
|
||||
MAX_LOCKOUT_HISTORY as u64,
|
||||
accounts.into_iter(),
|
||||
&accounts,
|
||||
&ancestors,
|
||||
|_| Some(Hash::default()),
|
||||
&mut latest_validator_votes_for_frozen_banks,
|
||||
@ -2340,7 +2342,7 @@ pub mod test {
|
||||
} = Tower::collect_vote_lockouts(
|
||||
&Pubkey::default(),
|
||||
vote_to_evaluate,
|
||||
accounts.clone().into_iter(),
|
||||
&accounts,
|
||||
&ancestors,
|
||||
|_| None,
|
||||
&mut LatestValidatorVotesForFrozenBanks::default(),
|
||||
@ -2358,7 +2360,7 @@ pub mod test {
|
||||
} = Tower::collect_vote_lockouts(
|
||||
&Pubkey::default(),
|
||||
vote_to_evaluate,
|
||||
accounts.into_iter(),
|
||||
&accounts,
|
||||
&ancestors,
|
||||
|_| None,
|
||||
&mut LatestValidatorVotesForFrozenBanks::default(),
|
||||
|
@ -2169,7 +2169,7 @@ impl ReplayStage {
|
||||
let computed_bank_state = Tower::collect_vote_lockouts(
|
||||
my_vote_pubkey,
|
||||
bank_slot,
|
||||
bank.vote_accounts().into_iter(),
|
||||
&bank.vote_accounts(),
|
||||
ancestors,
|
||||
|slot| progress.get_hash(slot),
|
||||
latest_validator_votes_for_frozen_banks,
|
||||
|
@ -1544,7 +1544,8 @@ fn get_stake_percent_in_gossip(bank: &Bank, cluster_info: &ClusterInfo, log: boo
|
||||
let my_shred_version = cluster_info.my_shred_version();
|
||||
let my_id = cluster_info.id();
|
||||
|
||||
for (_, (activated_stake, vote_account)) in bank.vote_accounts() {
|
||||
for (activated_stake, vote_account) in bank.vote_accounts().values() {
|
||||
let activated_stake = *activated_stake;
|
||||
total_activated_stake += activated_stake;
|
||||
|
||||
if activated_stake == 0 {
|
||||
|
Reference in New Issue
Block a user