caches vote-state de-serialized from vote accounts (#13795)
Gossip and other places repeatedly de-serialize vote-state stored in vote accounts. Ideally the first de-serialization should cache the result. This commit adds new VoteAccount type which lazily de-serializes VoteState from Account data and caches the result internally. Serialize and Deserialize traits are manually implemented to match existing code. So, despite changes to frozen_abi, this commit should be backward compatible.
This commit is contained in:
@ -176,19 +176,15 @@ impl AggregateCommitmentService {
|
||||
if lamports == 0 {
|
||||
continue;
|
||||
}
|
||||
let vote_state = VoteState::from(&account);
|
||||
if vote_state.is_none() {
|
||||
continue;
|
||||
if let Ok(vote_state) = account.vote_state().as_ref() {
|
||||
Self::aggregate_commitment_for_vote_account(
|
||||
&mut commitment,
|
||||
&mut rooted_stake,
|
||||
vote_state,
|
||||
ancestors,
|
||||
lamports,
|
||||
);
|
||||
}
|
||||
|
||||
let vote_state = vote_state.unwrap();
|
||||
Self::aggregate_commitment_for_vote_account(
|
||||
&mut commitment,
|
||||
&mut rooted_stake,
|
||||
&vote_state,
|
||||
ancestors,
|
||||
lamports,
|
||||
);
|
||||
}
|
||||
|
||||
(commitment, rooted_stake)
|
||||
@ -482,9 +478,14 @@ mod tests {
|
||||
#[test]
|
||||
fn test_highest_confirmed_root_advance() {
|
||||
fn get_vote_account_root_slot(vote_pubkey: Pubkey, bank: &Arc<Bank>) -> Slot {
|
||||
let account = &bank.vote_accounts()[&vote_pubkey].1;
|
||||
let vote_state = VoteState::from(account).unwrap();
|
||||
vote_state.root_slot.unwrap()
|
||||
let (_stake, vote_account) = bank.get_vote_account(&vote_pubkey).unwrap();
|
||||
let slot = vote_account
|
||||
.vote_state()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.root_slot
|
||||
.unwrap();
|
||||
slot
|
||||
}
|
||||
|
||||
let block_commitment_cache = RwLock::new(BlockCommitmentCache::new_for_tests());
|
||||
|
@ -5,9 +5,11 @@ use crate::{
|
||||
use chrono::prelude::*;
|
||||
use solana_ledger::{ancestor_iterator::AncestorIterator, blockstore::Blockstore, blockstore_db};
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_runtime::{bank::Bank, bank_forks::BankForks, commitment::VOTE_THRESHOLD_SIZE};
|
||||
use solana_runtime::{
|
||||
bank::Bank, bank_forks::BankForks, commitment::VOTE_THRESHOLD_SIZE,
|
||||
vote_account::ArcVoteAccount,
|
||||
};
|
||||
use solana_sdk::{
|
||||
account::Account,
|
||||
clock::{Slot, UnixTimestamp},
|
||||
hash::Hash,
|
||||
instruction::Instruction,
|
||||
@ -214,7 +216,7 @@ impl Tower {
|
||||
all_pubkeys: &mut PubkeyReferences,
|
||||
) -> ComputedBankState
|
||||
where
|
||||
F: Iterator<Item = (Pubkey, (u64, Account))>,
|
||||
F: IntoIterator<Item = (Pubkey, (u64, ArcVoteAccount))>,
|
||||
{
|
||||
let mut voted_stakes = HashMap::new();
|
||||
let mut total_stake = 0;
|
||||
@ -228,20 +230,20 @@ impl Tower {
|
||||
continue;
|
||||
}
|
||||
trace!("{} {} with stake {}", node_pubkey, key, voted_stake);
|
||||
let vote_state = VoteState::from(&account);
|
||||
if vote_state.is_none() {
|
||||
datapoint_warn!(
|
||||
"tower_warn",
|
||||
(
|
||||
"warn",
|
||||
format!("Unable to get vote_state from account {}", key),
|
||||
String
|
||||
),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
let mut vote_state = vote_state.unwrap();
|
||||
|
||||
let mut vote_state = match account.vote_state().as_ref() {
|
||||
Err(_) => {
|
||||
datapoint_warn!(
|
||||
"tower_warn",
|
||||
(
|
||||
"warn",
|
||||
format!("Unable to get vote_state from account {}", key),
|
||||
String
|
||||
),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
Ok(vote_state) => vote_state.clone(),
|
||||
};
|
||||
for vote in &vote_state.votes {
|
||||
let key = all_pubkeys.get_or_insert(&key);
|
||||
lockout_intervals
|
||||
@ -376,9 +378,9 @@ impl Tower {
|
||||
}
|
||||
|
||||
fn last_voted_slot_in_bank(bank: &Bank, vote_account_pubkey: &Pubkey) -> Option<Slot> {
|
||||
let vote_account = bank.vote_accounts().get(vote_account_pubkey)?.1.clone();
|
||||
let bank_vote_state = VoteState::deserialize(&vote_account.data).ok()?;
|
||||
bank_vote_state.last_voted_slot()
|
||||
let (_stake, vote_account) = bank.get_vote_account(vote_account_pubkey)?;
|
||||
let slot = vote_account.vote_state().as_ref().ok()?.last_voted_slot();
|
||||
slot
|
||||
}
|
||||
|
||||
pub fn new_vote_from_bank(&self, bank: &Bank, vote_account_pubkey: &Pubkey) -> (Vote, usize) {
|
||||
@ -509,7 +511,7 @@ impl Tower {
|
||||
descendants: &HashMap<Slot, HashSet<u64>>,
|
||||
progress: &ProgressMap,
|
||||
total_stake: u64,
|
||||
epoch_vote_accounts: &HashMap<Pubkey, (u64, Account)>,
|
||||
epoch_vote_accounts: &HashMap<Pubkey, (u64, ArcVoteAccount)>,
|
||||
) -> SwitchForkDecision {
|
||||
self.last_voted_slot()
|
||||
.map(|last_voted_slot| {
|
||||
@ -703,7 +705,7 @@ impl Tower {
|
||||
descendants: &HashMap<Slot, HashSet<u64>>,
|
||||
progress: &ProgressMap,
|
||||
total_stake: u64,
|
||||
epoch_vote_accounts: &HashMap<Pubkey, (u64, Account)>,
|
||||
epoch_vote_accounts: &HashMap<Pubkey, (u64, ArcVoteAccount)>,
|
||||
) -> SwitchForkDecision {
|
||||
let decision = self.make_check_switch_threshold_decision(
|
||||
switch_slot,
|
||||
@ -1058,10 +1060,12 @@ impl Tower {
|
||||
root: Slot,
|
||||
bank: &Bank,
|
||||
) {
|
||||
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?");
|
||||
self.lockouts = vote_state;
|
||||
if let Some((_stake, vote_account)) = bank.get_vote_account(vote_account_pubkey) {
|
||||
self.lockouts = vote_account
|
||||
.vote_state()
|
||||
.as_ref()
|
||||
.expect("vote_account isn't a VoteState?")
|
||||
.clone();
|
||||
self.initialize_root(root);
|
||||
self.initialize_lockouts(|v| v.slot > root);
|
||||
trace!(
|
||||
@ -1286,7 +1290,8 @@ pub mod test {
|
||||
},
|
||||
};
|
||||
use solana_sdk::{
|
||||
clock::Slot, hash::Hash, pubkey::Pubkey, signature::Signer, slot_history::SlotHistory,
|
||||
account::Account, clock::Slot, hash::Hash, pubkey::Pubkey, signature::Signer,
|
||||
slot_history::SlotHistory,
|
||||
};
|
||||
use solana_vote_program::{
|
||||
vote_state::{Vote, VoteStateVersions, MAX_LOCKOUT_HISTORY},
|
||||
@ -1604,7 +1609,7 @@ pub mod test {
|
||||
(bank_forks, progress, heaviest_subtree_fork_choice)
|
||||
}
|
||||
|
||||
fn gen_stakes(stake_votes: &[(u64, &[u64])]) -> Vec<(Pubkey, (u64, Account))> {
|
||||
fn gen_stakes(stake_votes: &[(u64, &[u64])]) -> Vec<(Pubkey, (u64, ArcVoteAccount))> {
|
||||
let mut stakes = vec![];
|
||||
for (lamports, votes) in stake_votes {
|
||||
let mut account = Account::default();
|
||||
@ -1619,7 +1624,10 @@ pub mod test {
|
||||
&mut account.data,
|
||||
)
|
||||
.expect("serialize state");
|
||||
stakes.push((solana_sdk::pubkey::new_rand(), (*lamports, account)));
|
||||
stakes.push((
|
||||
solana_sdk::pubkey::new_rand(),
|
||||
(*lamports, ArcVoteAccount::from(account)),
|
||||
));
|
||||
}
|
||||
stakes
|
||||
}
|
||||
@ -1973,16 +1981,16 @@ pub mod test {
|
||||
}
|
||||
|
||||
info!("local tower: {:#?}", tower.lockouts.votes);
|
||||
let vote_accounts = vote_simulator
|
||||
let observed = vote_simulator
|
||||
.bank_forks
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(next_unlocked_slot)
|
||||
.unwrap()
|
||||
.vote_accounts();
|
||||
let observed = vote_accounts.get(&vote_pubkey).unwrap();
|
||||
let state = VoteState::from(&observed.1).unwrap();
|
||||
info!("observed tower: {:#?}", state.votes);
|
||||
.get_vote_account(&vote_pubkey)
|
||||
.unwrap();
|
||||
let state = observed.1.vote_state();
|
||||
info!("observed tower: {:#?}", state.as_ref().unwrap().votes);
|
||||
|
||||
let num_slots_to_try = 200;
|
||||
cluster_votes
|
||||
|
@ -6,8 +6,8 @@ use crate::{
|
||||
{consensus::Stake, consensus::VotedStakes},
|
||||
};
|
||||
use solana_ledger::blockstore_processor::{ConfirmationProgress, ConfirmationTiming};
|
||||
use solana_runtime::{bank::Bank, bank_forks::BankForks};
|
||||
use solana_sdk::{account::Account, clock::Slot, hash::Hash, pubkey::Pubkey};
|
||||
use solana_runtime::{bank::Bank, bank_forks::BankForks, vote_account::ArcVoteAccount};
|
||||
use solana_sdk::{clock::Slot, hash::Hash, pubkey::Pubkey};
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap, HashSet},
|
||||
rc::Rc,
|
||||
@ -262,7 +262,7 @@ impl PropagatedStats {
|
||||
node_pubkey: &Pubkey,
|
||||
all_pubkeys: &mut PubkeyReferences,
|
||||
vote_account_pubkeys: &[Pubkey],
|
||||
epoch_vote_accounts: &HashMap<Pubkey, (u64, Account)>,
|
||||
epoch_vote_accounts: &HashMap<Pubkey, (u64, ArcVoteAccount)>,
|
||||
) {
|
||||
let cached_pubkey = all_pubkeys.get_or_insert(node_pubkey);
|
||||
self.propagated_node_ids.insert(cached_pubkey);
|
||||
@ -440,7 +440,7 @@ mod test {
|
||||
let epoch_vote_accounts: HashMap<_, _> = vote_account_pubkeys
|
||||
.iter()
|
||||
.skip(num_vote_accounts - staked_vote_accounts)
|
||||
.map(|pubkey| (*pubkey, (1, Account::default())))
|
||||
.map(|pubkey| (*pubkey, (1, ArcVoteAccount::default())))
|
||||
.collect();
|
||||
|
||||
let mut stats = PropagatedStats::default();
|
||||
@ -507,7 +507,7 @@ mod test {
|
||||
let epoch_vote_accounts: HashMap<_, _> = vote_account_pubkeys
|
||||
.iter()
|
||||
.skip(num_vote_accounts - staked_vote_accounts)
|
||||
.map(|pubkey| (*pubkey, (1, Account::default())))
|
||||
.map(|pubkey| (*pubkey, (1, ArcVoteAccount::default())))
|
||||
.collect();
|
||||
stats.add_node_pubkey_internal(
|
||||
&node_pubkey,
|
||||
|
@ -42,10 +42,7 @@ use solana_sdk::{
|
||||
timing::timestamp,
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solana_vote_program::{
|
||||
vote_instruction,
|
||||
vote_state::{Vote, VoteState},
|
||||
};
|
||||
use solana_vote_program::{vote_instruction, vote_state::Vote};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
ops::Deref,
|
||||
@ -1132,26 +1129,27 @@ impl ReplayStage {
|
||||
if authorized_voter_keypairs.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let vote_state =
|
||||
if let Some((_, vote_account)) = bank.vote_accounts().get(vote_account_pubkey) {
|
||||
if let Some(vote_state) = VoteState::from(&vote_account) {
|
||||
vote_state
|
||||
} else {
|
||||
warn!(
|
||||
"Vote account {} is unreadable. Unable to vote",
|
||||
vote_account_pubkey,
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
let vote_account = match bank.get_vote_account(vote_account_pubkey) {
|
||||
None => {
|
||||
warn!(
|
||||
"Vote account {} does not exist. Unable to vote",
|
||||
vote_account_pubkey,
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
}
|
||||
Some((_stake, vote_account)) => vote_account,
|
||||
};
|
||||
let vote_state = vote_account.vote_state();
|
||||
let vote_state = match vote_state.as_ref() {
|
||||
Err(_) => {
|
||||
warn!(
|
||||
"Vote account {} is unreadable. Unable to vote",
|
||||
vote_account_pubkey,
|
||||
);
|
||||
return;
|
||||
}
|
||||
Ok(vote_state) => vote_state,
|
||||
};
|
||||
let authorized_voter_pubkey =
|
||||
if let Some(authorized_voter_pubkey) = vote_state.get_authorized_voter(bank.epoch()) {
|
||||
authorized_voter_pubkey
|
||||
|
@ -537,13 +537,15 @@ impl JsonRpcRequestProcessor {
|
||||
let epoch_vote_accounts = bank
|
||||
.epoch_vote_accounts(bank.get_epoch_and_slot_index(bank.slot()).0)
|
||||
.ok_or_else(Error::invalid_request)?;
|
||||
let default_vote_state = VoteState::default();
|
||||
let (current_vote_accounts, delinquent_vote_accounts): (
|
||||
Vec<RpcVoteAccountInfo>,
|
||||
Vec<RpcVoteAccountInfo>,
|
||||
) = vote_accounts
|
||||
.iter()
|
||||
.map(|(pubkey, (activated_stake, account))| {
|
||||
let vote_state = VoteState::from(&account).unwrap_or_default();
|
||||
let vote_state = account.vote_state();
|
||||
let vote_state = vote_state.as_ref().unwrap_or(&default_vote_state);
|
||||
let last_vote = if let Some(vote) = vote_state.votes.iter().last() {
|
||||
vote.slot
|
||||
} else {
|
||||
|
@ -1125,33 +1125,37 @@ fn get_stake_percent_in_gossip(bank: &Bank, cluster_info: &ClusterInfo, log: boo
|
||||
let my_id = cluster_info.id();
|
||||
|
||||
for (activated_stake, vote_account) in bank.vote_accounts().values() {
|
||||
let vote_state = VoteState::from(&vote_account).unwrap_or_default();
|
||||
total_activated_stake += activated_stake;
|
||||
|
||||
if *activated_stake == 0 {
|
||||
continue;
|
||||
}
|
||||
let vote_state_node_pubkey = vote_account
|
||||
.vote_state()
|
||||
.as_ref()
|
||||
.map(|vote_state| vote_state.node_pubkey)
|
||||
.unwrap_or_default();
|
||||
|
||||
if let Some(peer) = all_tvu_peers
|
||||
.iter()
|
||||
.find(|peer| peer.id == vote_state.node_pubkey)
|
||||
.find(|peer| peer.id == vote_state_node_pubkey)
|
||||
{
|
||||
if peer.shred_version == my_shred_version {
|
||||
trace!(
|
||||
"observed {} in gossip, (activated_stake={})",
|
||||
vote_state.node_pubkey,
|
||||
vote_state_node_pubkey,
|
||||
activated_stake
|
||||
);
|
||||
online_stake += activated_stake;
|
||||
} else {
|
||||
wrong_shred_stake += activated_stake;
|
||||
wrong_shred_nodes.push((*activated_stake, vote_state.node_pubkey));
|
||||
wrong_shred_nodes.push((*activated_stake, vote_state_node_pubkey));
|
||||
}
|
||||
} else if vote_state.node_pubkey == my_id {
|
||||
} else if vote_state_node_pubkey == my_id {
|
||||
online_stake += activated_stake; // This node is online
|
||||
} else {
|
||||
offline_stake += activated_stake;
|
||||
offline_nodes.push((*activated_stake, vote_state.node_pubkey));
|
||||
offline_nodes.push((*activated_stake, vote_state_node_pubkey));
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user