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:
parent
6203d1c94c
commit
e1793e5a13
|
@ -176,19 +176,15 @@ impl AggregateCommitmentService {
|
||||||
if lamports == 0 {
|
if lamports == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let vote_state = VoteState::from(&account);
|
if let Ok(vote_state) = account.vote_state().as_ref() {
|
||||||
if vote_state.is_none() {
|
Self::aggregate_commitment_for_vote_account(
|
||||||
continue;
|
&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)
|
(commitment, rooted_stake)
|
||||||
|
@ -482,9 +478,14 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_highest_confirmed_root_advance() {
|
fn test_highest_confirmed_root_advance() {
|
||||||
fn get_vote_account_root_slot(vote_pubkey: Pubkey, bank: &Arc<Bank>) -> Slot {
|
fn get_vote_account_root_slot(vote_pubkey: Pubkey, bank: &Arc<Bank>) -> Slot {
|
||||||
let account = &bank.vote_accounts()[&vote_pubkey].1;
|
let (_stake, vote_account) = bank.get_vote_account(&vote_pubkey).unwrap();
|
||||||
let vote_state = VoteState::from(account).unwrap();
|
let slot = vote_account
|
||||||
vote_state.root_slot.unwrap()
|
.vote_state()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.root_slot
|
||||||
|
.unwrap();
|
||||||
|
slot
|
||||||
}
|
}
|
||||||
|
|
||||||
let block_commitment_cache = RwLock::new(BlockCommitmentCache::new_for_tests());
|
let block_commitment_cache = RwLock::new(BlockCommitmentCache::new_for_tests());
|
||||||
|
|
|
@ -5,9 +5,11 @@ use crate::{
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use solana_ledger::{ancestor_iterator::AncestorIterator, blockstore::Blockstore, blockstore_db};
|
use solana_ledger::{ancestor_iterator::AncestorIterator, blockstore::Blockstore, blockstore_db};
|
||||||
use solana_measure::measure::Measure;
|
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::{
|
use solana_sdk::{
|
||||||
account::Account,
|
|
||||||
clock::{Slot, UnixTimestamp},
|
clock::{Slot, UnixTimestamp},
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
instruction::Instruction,
|
instruction::Instruction,
|
||||||
|
@ -214,7 +216,7 @@ impl Tower {
|
||||||
all_pubkeys: &mut PubkeyReferences,
|
all_pubkeys: &mut PubkeyReferences,
|
||||||
) -> ComputedBankState
|
) -> ComputedBankState
|
||||||
where
|
where
|
||||||
F: Iterator<Item = (Pubkey, (u64, Account))>,
|
F: IntoIterator<Item = (Pubkey, (u64, ArcVoteAccount))>,
|
||||||
{
|
{
|
||||||
let mut voted_stakes = HashMap::new();
|
let mut voted_stakes = HashMap::new();
|
||||||
let mut total_stake = 0;
|
let mut total_stake = 0;
|
||||||
|
@ -228,20 +230,20 @@ impl Tower {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
trace!("{} {} with stake {}", node_pubkey, key, voted_stake);
|
trace!("{} {} with stake {}", node_pubkey, key, voted_stake);
|
||||||
let vote_state = VoteState::from(&account);
|
let mut vote_state = match account.vote_state().as_ref() {
|
||||||
if vote_state.is_none() {
|
Err(_) => {
|
||||||
datapoint_warn!(
|
datapoint_warn!(
|
||||||
"tower_warn",
|
"tower_warn",
|
||||||
(
|
(
|
||||||
"warn",
|
"warn",
|
||||||
format!("Unable to get vote_state from account {}", key),
|
format!("Unable to get vote_state from account {}", key),
|
||||||
String
|
String
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let mut vote_state = vote_state.unwrap();
|
Ok(vote_state) => vote_state.clone(),
|
||||||
|
};
|
||||||
for vote in &vote_state.votes {
|
for vote in &vote_state.votes {
|
||||||
let key = all_pubkeys.get_or_insert(&key);
|
let key = all_pubkeys.get_or_insert(&key);
|
||||||
lockout_intervals
|
lockout_intervals
|
||||||
|
@ -376,9 +378,9 @@ impl Tower {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn last_voted_slot_in_bank(bank: &Bank, vote_account_pubkey: &Pubkey) -> Option<Slot> {
|
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 (_stake, vote_account) = bank.get_vote_account(vote_account_pubkey)?;
|
||||||
let bank_vote_state = VoteState::deserialize(&vote_account.data).ok()?;
|
let slot = vote_account.vote_state().as_ref().ok()?.last_voted_slot();
|
||||||
bank_vote_state.last_voted_slot()
|
slot
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_vote_from_bank(&self, bank: &Bank, vote_account_pubkey: &Pubkey) -> (Vote, usize) {
|
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>>,
|
descendants: &HashMap<Slot, HashSet<u64>>,
|
||||||
progress: &ProgressMap,
|
progress: &ProgressMap,
|
||||||
total_stake: u64,
|
total_stake: u64,
|
||||||
epoch_vote_accounts: &HashMap<Pubkey, (u64, Account)>,
|
epoch_vote_accounts: &HashMap<Pubkey, (u64, ArcVoteAccount)>,
|
||||||
) -> SwitchForkDecision {
|
) -> SwitchForkDecision {
|
||||||
self.last_voted_slot()
|
self.last_voted_slot()
|
||||||
.map(|last_voted_slot| {
|
.map(|last_voted_slot| {
|
||||||
|
@ -703,7 +705,7 @@ impl Tower {
|
||||||
descendants: &HashMap<Slot, HashSet<u64>>,
|
descendants: &HashMap<Slot, HashSet<u64>>,
|
||||||
progress: &ProgressMap,
|
progress: &ProgressMap,
|
||||||
total_stake: u64,
|
total_stake: u64,
|
||||||
epoch_vote_accounts: &HashMap<Pubkey, (u64, Account)>,
|
epoch_vote_accounts: &HashMap<Pubkey, (u64, ArcVoteAccount)>,
|
||||||
) -> SwitchForkDecision {
|
) -> SwitchForkDecision {
|
||||||
let decision = self.make_check_switch_threshold_decision(
|
let decision = self.make_check_switch_threshold_decision(
|
||||||
switch_slot,
|
switch_slot,
|
||||||
|
@ -1058,10 +1060,12 @@ impl Tower {
|
||||||
root: Slot,
|
root: Slot,
|
||||||
bank: &Bank,
|
bank: &Bank,
|
||||||
) {
|
) {
|
||||||
if let Some((_stake, vote_account)) = bank.vote_accounts().get(vote_account_pubkey) {
|
if let Some((_stake, vote_account)) = bank.get_vote_account(vote_account_pubkey) {
|
||||||
let vote_state = VoteState::deserialize(&vote_account.data)
|
self.lockouts = vote_account
|
||||||
.expect("vote_account isn't a VoteState?");
|
.vote_state()
|
||||||
self.lockouts = vote_state;
|
.as_ref()
|
||||||
|
.expect("vote_account isn't a VoteState?")
|
||||||
|
.clone();
|
||||||
self.initialize_root(root);
|
self.initialize_root(root);
|
||||||
self.initialize_lockouts(|v| v.slot > root);
|
self.initialize_lockouts(|v| v.slot > root);
|
||||||
trace!(
|
trace!(
|
||||||
|
@ -1286,7 +1290,8 @@ pub mod test {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use solana_sdk::{
|
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::{
|
use solana_vote_program::{
|
||||||
vote_state::{Vote, VoteStateVersions, MAX_LOCKOUT_HISTORY},
|
vote_state::{Vote, VoteStateVersions, MAX_LOCKOUT_HISTORY},
|
||||||
|
@ -1604,7 +1609,7 @@ pub mod test {
|
||||||
(bank_forks, progress, heaviest_subtree_fork_choice)
|
(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![];
|
let mut stakes = vec![];
|
||||||
for (lamports, votes) in stake_votes {
|
for (lamports, votes) in stake_votes {
|
||||||
let mut account = Account::default();
|
let mut account = Account::default();
|
||||||
|
@ -1619,7 +1624,10 @@ pub mod test {
|
||||||
&mut account.data,
|
&mut account.data,
|
||||||
)
|
)
|
||||||
.expect("serialize state");
|
.expect("serialize state");
|
||||||
stakes.push((solana_sdk::pubkey::new_rand(), (*lamports, account)));
|
stakes.push((
|
||||||
|
solana_sdk::pubkey::new_rand(),
|
||||||
|
(*lamports, ArcVoteAccount::from(account)),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
stakes
|
stakes
|
||||||
}
|
}
|
||||||
|
@ -1973,16 +1981,16 @@ pub mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("local tower: {:#?}", tower.lockouts.votes);
|
info!("local tower: {:#?}", tower.lockouts.votes);
|
||||||
let vote_accounts = vote_simulator
|
let observed = vote_simulator
|
||||||
.bank_forks
|
.bank_forks
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get(next_unlocked_slot)
|
.get(next_unlocked_slot)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.vote_accounts();
|
.get_vote_account(&vote_pubkey)
|
||||||
let observed = vote_accounts.get(&vote_pubkey).unwrap();
|
.unwrap();
|
||||||
let state = VoteState::from(&observed.1).unwrap();
|
let state = observed.1.vote_state();
|
||||||
info!("observed tower: {:#?}", state.votes);
|
info!("observed tower: {:#?}", state.as_ref().unwrap().votes);
|
||||||
|
|
||||||
let num_slots_to_try = 200;
|
let num_slots_to_try = 200;
|
||||||
cluster_votes
|
cluster_votes
|
||||||
|
|
|
@ -6,8 +6,8 @@ use crate::{
|
||||||
{consensus::Stake, consensus::VotedStakes},
|
{consensus::Stake, consensus::VotedStakes},
|
||||||
};
|
};
|
||||||
use solana_ledger::blockstore_processor::{ConfirmationProgress, ConfirmationTiming};
|
use solana_ledger::blockstore_processor::{ConfirmationProgress, ConfirmationTiming};
|
||||||
use solana_runtime::{bank::Bank, bank_forks::BankForks};
|
use solana_runtime::{bank::Bank, bank_forks::BankForks, vote_account::ArcVoteAccount};
|
||||||
use solana_sdk::{account::Account, clock::Slot, hash::Hash, pubkey::Pubkey};
|
use solana_sdk::{clock::Slot, hash::Hash, pubkey::Pubkey};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashMap, HashSet},
|
collections::{BTreeMap, HashMap, HashSet},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
|
@ -262,7 +262,7 @@ impl PropagatedStats {
|
||||||
node_pubkey: &Pubkey,
|
node_pubkey: &Pubkey,
|
||||||
all_pubkeys: &mut PubkeyReferences,
|
all_pubkeys: &mut PubkeyReferences,
|
||||||
vote_account_pubkeys: &[Pubkey],
|
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);
|
let cached_pubkey = all_pubkeys.get_or_insert(node_pubkey);
|
||||||
self.propagated_node_ids.insert(cached_pubkey);
|
self.propagated_node_ids.insert(cached_pubkey);
|
||||||
|
@ -440,7 +440,7 @@ mod test {
|
||||||
let epoch_vote_accounts: HashMap<_, _> = vote_account_pubkeys
|
let epoch_vote_accounts: HashMap<_, _> = vote_account_pubkeys
|
||||||
.iter()
|
.iter()
|
||||||
.skip(num_vote_accounts - staked_vote_accounts)
|
.skip(num_vote_accounts - staked_vote_accounts)
|
||||||
.map(|pubkey| (*pubkey, (1, Account::default())))
|
.map(|pubkey| (*pubkey, (1, ArcVoteAccount::default())))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut stats = PropagatedStats::default();
|
let mut stats = PropagatedStats::default();
|
||||||
|
@ -507,7 +507,7 @@ mod test {
|
||||||
let epoch_vote_accounts: HashMap<_, _> = vote_account_pubkeys
|
let epoch_vote_accounts: HashMap<_, _> = vote_account_pubkeys
|
||||||
.iter()
|
.iter()
|
||||||
.skip(num_vote_accounts - staked_vote_accounts)
|
.skip(num_vote_accounts - staked_vote_accounts)
|
||||||
.map(|pubkey| (*pubkey, (1, Account::default())))
|
.map(|pubkey| (*pubkey, (1, ArcVoteAccount::default())))
|
||||||
.collect();
|
.collect();
|
||||||
stats.add_node_pubkey_internal(
|
stats.add_node_pubkey_internal(
|
||||||
&node_pubkey,
|
&node_pubkey,
|
||||||
|
|
|
@ -42,10 +42,7 @@ use solana_sdk::{
|
||||||
timing::timestamp,
|
timing::timestamp,
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
use solana_vote_program::{
|
use solana_vote_program::{vote_instruction, vote_state::Vote};
|
||||||
vote_instruction,
|
|
||||||
vote_state::{Vote, VoteState},
|
|
||||||
};
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
|
@ -1132,26 +1129,27 @@ impl ReplayStage {
|
||||||
if authorized_voter_keypairs.is_empty() {
|
if authorized_voter_keypairs.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let vote_account = match bank.get_vote_account(vote_account_pubkey) {
|
||||||
let vote_state =
|
None => {
|
||||||
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 {
|
|
||||||
warn!(
|
warn!(
|
||||||
"Vote account {} does not exist. Unable to vote",
|
"Vote account {} does not exist. Unable to vote",
|
||||||
vote_account_pubkey,
|
vote_account_pubkey,
|
||||||
);
|
);
|
||||||
return;
|
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 =
|
let authorized_voter_pubkey =
|
||||||
if let Some(authorized_voter_pubkey) = vote_state.get_authorized_voter(bank.epoch()) {
|
if let Some(authorized_voter_pubkey) = vote_state.get_authorized_voter(bank.epoch()) {
|
||||||
authorized_voter_pubkey
|
authorized_voter_pubkey
|
||||||
|
|
|
@ -537,13 +537,15 @@ impl JsonRpcRequestProcessor {
|
||||||
let epoch_vote_accounts = bank
|
let epoch_vote_accounts = bank
|
||||||
.epoch_vote_accounts(bank.get_epoch_and_slot_index(bank.slot()).0)
|
.epoch_vote_accounts(bank.get_epoch_and_slot_index(bank.slot()).0)
|
||||||
.ok_or_else(Error::invalid_request)?;
|
.ok_or_else(Error::invalid_request)?;
|
||||||
|
let default_vote_state = VoteState::default();
|
||||||
let (current_vote_accounts, delinquent_vote_accounts): (
|
let (current_vote_accounts, delinquent_vote_accounts): (
|
||||||
Vec<RpcVoteAccountInfo>,
|
Vec<RpcVoteAccountInfo>,
|
||||||
Vec<RpcVoteAccountInfo>,
|
Vec<RpcVoteAccountInfo>,
|
||||||
) = vote_accounts
|
) = vote_accounts
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(pubkey, (activated_stake, account))| {
|
.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() {
|
let last_vote = if let Some(vote) = vote_state.votes.iter().last() {
|
||||||
vote.slot
|
vote.slot
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1125,33 +1125,37 @@ fn get_stake_percent_in_gossip(bank: &Bank, cluster_info: &ClusterInfo, log: boo
|
||||||
let my_id = cluster_info.id();
|
let my_id = cluster_info.id();
|
||||||
|
|
||||||
for (activated_stake, vote_account) in bank.vote_accounts().values() {
|
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;
|
total_activated_stake += activated_stake;
|
||||||
|
|
||||||
if *activated_stake == 0 {
|
if *activated_stake == 0 {
|
||||||
continue;
|
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
|
if let Some(peer) = all_tvu_peers
|
||||||
.iter()
|
.iter()
|
||||||
.find(|peer| peer.id == vote_state.node_pubkey)
|
.find(|peer| peer.id == vote_state_node_pubkey)
|
||||||
{
|
{
|
||||||
if peer.shred_version == my_shred_version {
|
if peer.shred_version == my_shred_version {
|
||||||
trace!(
|
trace!(
|
||||||
"observed {} in gossip, (activated_stake={})",
|
"observed {} in gossip, (activated_stake={})",
|
||||||
vote_state.node_pubkey,
|
vote_state_node_pubkey,
|
||||||
activated_stake
|
activated_stake
|
||||||
);
|
);
|
||||||
online_stake += activated_stake;
|
online_stake += activated_stake;
|
||||||
} else {
|
} else {
|
||||||
wrong_shred_stake += activated_stake;
|
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
|
online_stake += activated_stake; // This node is online
|
||||||
} else {
|
} else {
|
||||||
offline_stake += activated_stake;
|
offline_stake += activated_stake;
|
||||||
offline_nodes.push((*activated_stake, vote_state.node_pubkey));
|
offline_nodes.push((*activated_stake, vote_state_node_pubkey));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -188,6 +188,7 @@ example_impls! { f32, 0.0f32 }
|
||||||
example_impls! { f64, 0.0f64 }
|
example_impls! { f64, 0.0f64 }
|
||||||
example_impls! { String, String::new() }
|
example_impls! { String, String::new() }
|
||||||
example_impls! { std::time::Duration, std::time::Duration::from_secs(0) }
|
example_impls! { std::time::Duration, std::time::Duration::from_secs(0) }
|
||||||
|
example_impls! { std::sync::Once, std::sync::Once::new() }
|
||||||
|
|
||||||
use std::sync::atomic::*;
|
use std::sync::atomic::*;
|
||||||
|
|
||||||
|
|
|
@ -299,6 +299,7 @@ fn graph_forks(bank_forks: &BankForks, include_all_votes: bool) -> String {
|
||||||
|
|
||||||
// Search all forks and collect the last vote made by each validator
|
// Search all forks and collect the last vote made by each validator
|
||||||
let mut last_votes = HashMap::new();
|
let mut last_votes = HashMap::new();
|
||||||
|
let default_vote_state = VoteState::default();
|
||||||
for fork_slot in &fork_slots {
|
for fork_slot in &fork_slots {
|
||||||
let bank = &bank_forks[*fork_slot];
|
let bank = &bank_forks[*fork_slot];
|
||||||
|
|
||||||
|
@ -308,7 +309,8 @@ fn graph_forks(bank_forks: &BankForks, include_all_votes: bool) -> String {
|
||||||
.map(|(_, (stake, _))| stake)
|
.map(|(_, (stake, _))| stake)
|
||||||
.sum();
|
.sum();
|
||||||
for (_, (stake, vote_account)) in bank.vote_accounts() {
|
for (_, (stake, vote_account)) in bank.vote_accounts() {
|
||||||
let vote_state = VoteState::from(&vote_account).unwrap_or_default();
|
let vote_state = vote_account.vote_state();
|
||||||
|
let vote_state = vote_state.as_ref().unwrap_or(&default_vote_state);
|
||||||
if let Some(last_vote) = vote_state.votes.iter().last() {
|
if let Some(last_vote) = vote_state.votes.iter().last() {
|
||||||
let entry = last_votes.entry(vote_state.node_pubkey).or_insert((
|
let entry = last_votes.entry(vote_state.node_pubkey).or_insert((
|
||||||
last_vote.slot,
|
last_vote.slot,
|
||||||
|
@ -317,7 +319,7 @@ fn graph_forks(bank_forks: &BankForks, include_all_votes: bool) -> String {
|
||||||
total_stake,
|
total_stake,
|
||||||
));
|
));
|
||||||
if entry.0 < last_vote.slot {
|
if entry.0 < last_vote.slot {
|
||||||
*entry = (last_vote.slot, vote_state, stake, total_stake);
|
*entry = (last_vote.slot, vote_state.clone(), stake, total_stake);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -348,7 +350,8 @@ fn graph_forks(bank_forks: &BankForks, include_all_votes: bool) -> String {
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
loop {
|
loop {
|
||||||
for (_, (_, vote_account)) in bank.vote_accounts() {
|
for (_, (_, vote_account)) in bank.vote_accounts() {
|
||||||
let vote_state = VoteState::from(&vote_account).unwrap_or_default();
|
let vote_state = vote_account.vote_state();
|
||||||
|
let vote_state = vote_state.as_ref().unwrap_or(&default_vote_state);
|
||||||
if let Some(last_vote) = vote_state.votes.iter().last() {
|
if let Some(last_vote) = vote_state.votes.iter().last() {
|
||||||
let validator_votes = all_votes.entry(vote_state.node_pubkey).or_default();
|
let validator_votes = all_votes.entry(vote_state.node_pubkey).or_default();
|
||||||
validator_votes
|
validator_votes
|
||||||
|
|
|
@ -24,9 +24,11 @@ use rocksdb::DBRawIterator;
|
||||||
use solana_measure::measure::Measure;
|
use solana_measure::measure::Measure;
|
||||||
use solana_metrics::{datapoint_debug, datapoint_error};
|
use solana_metrics::{datapoint_debug, datapoint_error};
|
||||||
use solana_rayon_threadlimit::get_thread_count;
|
use solana_rayon_threadlimit::get_thread_count;
|
||||||
use solana_runtime::hardened_unpack::{unpack_genesis_archive, MAX_GENESIS_ARCHIVE_UNPACKED_SIZE};
|
use solana_runtime::{
|
||||||
|
hardened_unpack::{unpack_genesis_archive, MAX_GENESIS_ARCHIVE_UNPACKED_SIZE},
|
||||||
|
vote_account::ArcVoteAccount,
|
||||||
|
};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
|
||||||
clock::{Slot, UnixTimestamp, DEFAULT_TICKS_PER_SECOND, MS_PER_TICK},
|
clock::{Slot, UnixTimestamp, DEFAULT_TICKS_PER_SECOND, MS_PER_TICK},
|
||||||
genesis_config::GenesisConfig,
|
genesis_config::GenesisConfig,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
|
@ -1623,7 +1625,7 @@ impl Blockstore {
|
||||||
&self,
|
&self,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
slot_duration: Duration,
|
slot_duration: Duration,
|
||||||
stakes: &HashMap<Pubkey, (u64, Account)>,
|
stakes: &HashMap<Pubkey, (u64, ArcVoteAccount)>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if !self.is_root(slot) {
|
if !self.is_root(slot) {
|
||||||
return Err(BlockstoreError::SlotNotRooted);
|
return Err(BlockstoreError::SlotNotRooted);
|
||||||
|
@ -5817,7 +5819,7 @@ pub mod tests {
|
||||||
|
|
||||||
// Build epoch vote_accounts HashMap to test stake-weighted block time
|
// Build epoch vote_accounts HashMap to test stake-weighted block time
|
||||||
for (i, keypair) in vote_keypairs.iter().enumerate() {
|
for (i, keypair) in vote_keypairs.iter().enumerate() {
|
||||||
stakes.insert(keypair.pubkey(), (1 + i as u64, Account::default()));
|
stakes.insert(keypair.pubkey(), (1 + i as u64, ArcVoteAccount::default()));
|
||||||
}
|
}
|
||||||
for slot in &[1, 2, 3, 8] {
|
for slot in &[1, 2, 3, 8] {
|
||||||
blockstore
|
blockstore
|
||||||
|
@ -5876,7 +5878,7 @@ pub mod tests {
|
||||||
// Build epoch vote_accounts HashMap to test stake-weighted block time
|
// Build epoch vote_accounts HashMap to test stake-weighted block time
|
||||||
let mut stakes = HashMap::new();
|
let mut stakes = HashMap::new();
|
||||||
for (i, keypair) in vote_keypairs.iter().enumerate() {
|
for (i, keypair) in vote_keypairs.iter().enumerate() {
|
||||||
stakes.insert(keypair.pubkey(), (1 + i as u64, Account::default()));
|
stakes.insert(keypair.pubkey(), (1 + i as u64, ArcVoteAccount::default()));
|
||||||
}
|
}
|
||||||
let slot_duration = Duration::from_millis(400);
|
let slot_duration = Duration::from_millis(400);
|
||||||
for slot in &[1, 2, 3, 8] {
|
for slot in &[1, 2, 3, 8] {
|
||||||
|
|
|
@ -24,10 +24,10 @@ use solana_runtime::{
|
||||||
commitment::VOTE_THRESHOLD_SIZE,
|
commitment::VOTE_THRESHOLD_SIZE,
|
||||||
transaction_batch::TransactionBatch,
|
transaction_batch::TransactionBatch,
|
||||||
transaction_utils::OrderedIterator,
|
transaction_utils::OrderedIterator,
|
||||||
|
vote_account::ArcVoteAccount,
|
||||||
vote_sender_types::ReplayVoteSender,
|
vote_sender_types::ReplayVoteSender,
|
||||||
};
|
};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
|
||||||
clock::{Slot, MAX_PROCESSING_AGE},
|
clock::{Slot, MAX_PROCESSING_AGE},
|
||||||
genesis_config::GenesisConfig,
|
genesis_config::GenesisConfig,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
|
@ -36,7 +36,6 @@ use solana_sdk::{
|
||||||
timing::duration_as_ms,
|
timing::duration_as_ms,
|
||||||
transaction::{Result, Transaction, TransactionError},
|
transaction::{Result, Transaction, TransactionError},
|
||||||
};
|
};
|
||||||
use solana_vote_program::vote_state::VoteState;
|
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
|
@ -868,8 +867,11 @@ fn load_frozen_forks(
|
||||||
// for newer cluster confirmed roots
|
// for newer cluster confirmed roots
|
||||||
let new_root_bank = {
|
let new_root_bank = {
|
||||||
if *root == max_root {
|
if *root == max_root {
|
||||||
supermajority_root_from_vote_accounts(bank.slot(), bank.total_epoch_stake(), bank.vote_accounts()
|
supermajority_root_from_vote_accounts(
|
||||||
.into_iter()).and_then(|supermajority_root| {
|
bank.slot(),
|
||||||
|
bank.total_epoch_stake(),
|
||||||
|
bank.vote_accounts(),
|
||||||
|
).and_then(|supermajority_root| {
|
||||||
if supermajority_root > *root {
|
if supermajority_root > *root {
|
||||||
// If there's a cluster confirmed root greater than our last
|
// If there's a cluster confirmed root greater than our last
|
||||||
// replayed root, then beccause the cluster confirmed root should
|
// replayed root, then beccause the cluster confirmed root should
|
||||||
|
@ -960,30 +962,28 @@ fn supermajority_root(roots: &[(Slot, u64)], total_epoch_stake: u64) -> Option<S
|
||||||
fn supermajority_root_from_vote_accounts<I>(
|
fn supermajority_root_from_vote_accounts<I>(
|
||||||
bank_slot: Slot,
|
bank_slot: Slot,
|
||||||
total_epoch_stake: u64,
|
total_epoch_stake: u64,
|
||||||
vote_accounts_iter: I,
|
vote_accounts: I,
|
||||||
) -> Option<Slot>
|
) -> Option<Slot>
|
||||||
where
|
where
|
||||||
I: Iterator<Item = (Pubkey, (u64, Account))>,
|
I: IntoIterator<Item = (Pubkey, (u64, ArcVoteAccount))>,
|
||||||
{
|
{
|
||||||
let mut roots_stakes: Vec<(Slot, u64)> = vote_accounts_iter
|
let mut roots_stakes: Vec<(Slot, u64)> = vote_accounts
|
||||||
|
.into_iter()
|
||||||
.filter_map(|(key, (stake, account))| {
|
.filter_map(|(key, (stake, account))| {
|
||||||
if stake == 0 {
|
if stake == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let vote_state = VoteState::from(&account);
|
match account.vote_state().as_ref() {
|
||||||
if vote_state.is_none() {
|
Err(_) => {
|
||||||
warn!(
|
warn!(
|
||||||
"Unable to get vote_state from account {} in bank: {}",
|
"Unable to get vote_state from account {} in bank: {}",
|
||||||
key, bank_slot
|
key, bank_slot
|
||||||
);
|
);
|
||||||
return None;
|
None
|
||||||
|
}
|
||||||
|
Ok(vote_state) => vote_state.root_slot.map(|root_slot| (root_slot, stake)),
|
||||||
}
|
}
|
||||||
|
|
||||||
vote_state
|
|
||||||
.unwrap()
|
|
||||||
.root_slot
|
|
||||||
.map(|root_slot| (root_slot, stake))
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
@ -1112,6 +1112,7 @@ pub mod tests {
|
||||||
self, create_genesis_config_with_vote_accounts, ValidatorVoteKeypairs,
|
self, create_genesis_config_with_vote_accounts, ValidatorVoteKeypairs,
|
||||||
};
|
};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
|
account::Account,
|
||||||
epoch_schedule::EpochSchedule,
|
epoch_schedule::EpochSchedule,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
|
@ -1122,7 +1123,7 @@ pub mod tests {
|
||||||
};
|
};
|
||||||
use solana_vote_program::{
|
use solana_vote_program::{
|
||||||
self,
|
self,
|
||||||
vote_state::{VoteStateVersions, MAX_LOCKOUT_HISTORY},
|
vote_state::{VoteState, VoteStateVersions, MAX_LOCKOUT_HISTORY},
|
||||||
vote_transaction,
|
vote_transaction,
|
||||||
};
|
};
|
||||||
use std::{collections::BTreeSet, sync::RwLock};
|
use std::{collections::BTreeSet, sync::RwLock};
|
||||||
|
@ -3146,7 +3147,7 @@ pub mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_supermajority_root_from_vote_accounts() {
|
fn test_supermajority_root_from_vote_accounts() {
|
||||||
let convert_to_vote_accounts =
|
let convert_to_vote_accounts =
|
||||||
|roots_stakes: Vec<(Slot, u64)>| -> Vec<(Pubkey, (u64, Account))> {
|
|roots_stakes: Vec<(Slot, u64)>| -> Vec<(Pubkey, (u64, ArcVoteAccount))> {
|
||||||
roots_stakes
|
roots_stakes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(root, stake)| {
|
.map(|(root, stake)| {
|
||||||
|
@ -3156,7 +3157,10 @@ pub mod tests {
|
||||||
Account::new(1, VoteState::size_of(), &solana_vote_program::id());
|
Account::new(1, VoteState::size_of(), &solana_vote_program::id());
|
||||||
let versioned = VoteStateVersions::Current(Box::new(vote_state));
|
let versioned = VoteStateVersions::Current(Box::new(vote_state));
|
||||||
VoteState::serialize(&versioned, &mut vote_account.data).unwrap();
|
VoteState::serialize(&versioned, &mut vote_account.data).unwrap();
|
||||||
(solana_sdk::pubkey::new_rand(), (stake, vote_account))
|
(
|
||||||
|
solana_sdk::pubkey::new_rand(),
|
||||||
|
(stake, ArcVoteAccount::from(vote_account)),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.collect_vec()
|
.collect_vec()
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
use solana_runtime::bank::Bank;
|
use solana_runtime::{bank::Bank, vote_account::ArcVoteAccount};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
|
||||||
clock::{Epoch, Slot},
|
clock::{Epoch, Slot},
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
};
|
};
|
||||||
use solana_vote_program::vote_state::VoteState;
|
|
||||||
use std::{borrow::Borrow, collections::HashMap};
|
use std::{borrow::Borrow, collections::HashMap};
|
||||||
|
|
||||||
/// Looks through vote accounts, and finds the latest slot that has achieved
|
/// Looks through vote accounts, and finds the latest slot that has achieved
|
||||||
|
@ -28,48 +26,42 @@ pub fn vote_account_stakes(bank: &Bank) -> HashMap<Pubkey, u64> {
|
||||||
|
|
||||||
/// Collect the staked nodes, as named by staked vote accounts from the given bank
|
/// Collect the staked nodes, as named by staked vote accounts from the given bank
|
||||||
pub fn staked_nodes(bank: &Bank) -> HashMap<Pubkey, u64> {
|
pub fn staked_nodes(bank: &Bank) -> HashMap<Pubkey, u64> {
|
||||||
to_staked_nodes(to_vote_states(bank.vote_accounts().into_iter()))
|
to_staked_nodes(bank.vote_accounts())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// At the specified epoch, collect the delegate account balance and vote states for delegates
|
/// 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
|
/// that have non-zero balance in any of their managed staking accounts
|
||||||
pub fn staked_nodes_at_epoch(bank: &Bank, epoch: Epoch) -> Option<HashMap<Pubkey, u64>> {
|
pub fn staked_nodes_at_epoch(bank: &Bank, epoch: Epoch) -> Option<HashMap<Pubkey, u64>> {
|
||||||
bank.epoch_vote_accounts(epoch)
|
bank.epoch_vote_accounts(epoch).map(to_staked_nodes)
|
||||||
.map(|vote_accounts| to_staked_nodes(to_vote_states(vote_accounts.iter())))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// input (vote_pubkey, (stake, vote_account)) => (stake, vote_state)
|
fn to_staked_nodes<I, K, V>(
|
||||||
fn to_vote_states(
|
vote_accounts: I,
|
||||||
node_staked_accounts: impl Iterator<Item = (impl Borrow<Pubkey>, impl Borrow<(u64, Account)>)>,
|
) -> HashMap<Pubkey /*VoteState.node_pubkey*/, u64 /*stake*/>
|
||||||
) -> impl Iterator<Item = (u64, VoteState)> {
|
where
|
||||||
node_staked_accounts.filter_map(|(_, stake_account)| {
|
I: IntoIterator<Item = (K /*vote pubkey*/, V)>,
|
||||||
VoteState::deserialize(&stake_account.borrow().1.data)
|
V: Borrow<(u64 /*stake*/, ArcVoteAccount)>,
|
||||||
.ok()
|
{
|
||||||
.map(|vote_state| (stake_account.borrow().0, vote_state))
|
let mut out: HashMap<Pubkey, u64> = HashMap::new();
|
||||||
})
|
for (_ /*vote pubkey*/, stake_vote_account) in vote_accounts {
|
||||||
}
|
let (stake, vote_account) = stake_vote_account.borrow();
|
||||||
|
if let Ok(vote_state) = vote_account.vote_state().as_ref() {
|
||||||
// (stake, vote_state) => (node, stake)
|
out.entry(vote_state.node_pubkey)
|
||||||
fn to_staked_nodes(
|
.and_modify(|s| *s += *stake)
|
||||||
node_staked_accounts: impl Iterator<Item = (u64, VoteState)>,
|
.or_insert(*stake);
|
||||||
) -> HashMap<Pubkey, u64> {
|
}
|
||||||
let mut map: HashMap<Pubkey, u64> = HashMap::new();
|
}
|
||||||
node_staked_accounts.for_each(|(stake, state)| {
|
out
|
||||||
map.entry(state.node_pubkey)
|
|
||||||
.and_modify(|s| *s += stake)
|
|
||||||
.or_insert(stake);
|
|
||||||
});
|
|
||||||
map
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn epoch_stakes_and_lockouts(bank: &Bank, epoch: Epoch) -> Vec<(u64, Option<u64>)> {
|
fn epoch_stakes_and_lockouts(bank: &Bank, epoch: Epoch) -> Vec<(u64, Option<u64>)> {
|
||||||
let node_staked_accounts = bank
|
bank.epoch_vote_accounts(epoch)
|
||||||
.epoch_vote_accounts(epoch)
|
|
||||||
.expect("Bank state for epoch is missing")
|
.expect("Bank state for epoch is missing")
|
||||||
.iter();
|
.iter()
|
||||||
|
.filter_map(|(_ /*vote pubkey*/, (stake, vote_account))| {
|
||||||
to_vote_states(node_staked_accounts)
|
let root_slot = vote_account.vote_state().as_ref().ok()?.root_slot;
|
||||||
.map(|(stake, states)| (stake, states.root_slot))
|
Some((*stake, root_slot))
|
||||||
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,8 +95,9 @@ pub(crate) mod tests {
|
||||||
use crate::genesis_utils::{
|
use crate::genesis_utils::{
|
||||||
bootstrap_validator_stake_lamports, create_genesis_config, GenesisConfigInfo,
|
bootstrap_validator_stake_lamports, create_genesis_config, GenesisConfigInfo,
|
||||||
};
|
};
|
||||||
|
use rand::Rng;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::from_account,
|
account::{from_account, Account},
|
||||||
clock::Clock,
|
clock::Clock,
|
||||||
instruction::Instruction,
|
instruction::Instruction,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
|
@ -117,7 +110,10 @@ pub(crate) mod tests {
|
||||||
stake_instruction,
|
stake_instruction,
|
||||||
stake_state::{Authorized, Delegation, Lockup, Stake},
|
stake_state::{Authorized, Delegation, Lockup, Stake},
|
||||||
};
|
};
|
||||||
use solana_vote_program::{vote_instruction, vote_state::VoteInit};
|
use solana_vote_program::{
|
||||||
|
vote_instruction,
|
||||||
|
vote_state::{VoteInit, VoteState, VoteStateVersions},
|
||||||
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
fn new_from_parent(parent: &Arc<Bank>, slot: Slot) -> Bank {
|
fn new_from_parent(parent: &Arc<Bank>, slot: Slot) -> Bank {
|
||||||
|
@ -340,8 +336,18 @@ pub(crate) mod tests {
|
||||||
&Clock::default(),
|
&Clock::default(),
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
let result = to_staked_nodes(stakes.into_iter());
|
let vote_accounts = stakes.into_iter().map(|(stake, vote_state)| {
|
||||||
|
let account = Account::new_data(
|
||||||
|
rng.gen(), // lamports
|
||||||
|
&VoteStateVersions::Current(Box::new(vote_state)),
|
||||||
|
&Pubkey::new_unique(), // owner
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let vote_pubkey = Pubkey::new_unique();
|
||||||
|
(vote_pubkey, (stake, ArcVoteAccount::from(account)))
|
||||||
|
});
|
||||||
|
let result = to_staked_nodes(vote_accounts);
|
||||||
assert_eq!(result.len(), 2);
|
assert_eq!(result.len(), 2);
|
||||||
assert_eq!(result[&node1], 3);
|
assert_eq!(result[&node1], 3);
|
||||||
assert_eq!(result[&node2], 5);
|
assert_eq!(result[&node2], 5);
|
||||||
|
|
|
@ -21,6 +21,7 @@ use crate::{
|
||||||
system_instruction_processor::{get_system_account_kind, SystemAccountKind},
|
system_instruction_processor::{get_system_account_kind, SystemAccountKind},
|
||||||
transaction_batch::TransactionBatch,
|
transaction_batch::TransactionBatch,
|
||||||
transaction_utils::OrderedIterator,
|
transaction_utils::OrderedIterator,
|
||||||
|
vote_account::ArcVoteAccount,
|
||||||
};
|
};
|
||||||
use byteorder::{ByteOrder, LittleEndian};
|
use byteorder::{ByteOrder, LittleEndian};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
@ -68,7 +69,7 @@ use solana_sdk::{
|
||||||
use solana_stake_program::stake_state::{
|
use solana_stake_program::stake_state::{
|
||||||
self, Delegation, InflationPointCalculationEvent, PointValue,
|
self, Delegation, InflationPointCalculationEvent, PointValue,
|
||||||
};
|
};
|
||||||
use solana_vote_program::{vote_instruction::VoteInstruction, vote_state::VoteState};
|
use solana_vote_program::vote_instruction::VoteInstruction;
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
|
@ -379,7 +380,7 @@ pub struct TransactionBalancesSet {
|
||||||
pub post_balances: TransactionBalances,
|
pub post_balances: TransactionBalances,
|
||||||
}
|
}
|
||||||
pub struct OverwrittenVoteAccount {
|
pub struct OverwrittenVoteAccount {
|
||||||
pub account: Account,
|
pub account: ArcVoteAccount,
|
||||||
pub transaction_index: usize,
|
pub transaction_index: usize,
|
||||||
pub transaction_result_index: usize,
|
pub transaction_result_index: usize,
|
||||||
}
|
}
|
||||||
|
@ -1700,7 +1701,7 @@ impl Bank {
|
||||||
.vote_accounts()
|
.vote_accounts()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(pubkey, (_, account))| {
|
.filter_map(|(pubkey, (_, account))| {
|
||||||
VoteState::from(&account).and_then(|state| {
|
account.vote_state().as_ref().ok().and_then(|state| {
|
||||||
let timestamp_slot = state.last_timestamp.slot;
|
let timestamp_slot = state.last_timestamp.slot;
|
||||||
if (self
|
if (self
|
||||||
.feature_set
|
.feature_set
|
||||||
|
@ -2955,7 +2956,7 @@ impl Bank {
|
||||||
#[allow(clippy::needless_collect)]
|
#[allow(clippy::needless_collect)]
|
||||||
fn distribute_rent_to_validators(
|
fn distribute_rent_to_validators(
|
||||||
&self,
|
&self,
|
||||||
vote_account_hashmap: &HashMap<Pubkey, (u64, Account)>,
|
vote_account_hashmap: &HashMap<Pubkey, (u64, ArcVoteAccount)>,
|
||||||
rent_to_be_distributed: u64,
|
rent_to_be_distributed: u64,
|
||||||
) {
|
) {
|
||||||
let mut total_staked = 0;
|
let mut total_staked = 0;
|
||||||
|
@ -2970,9 +2971,8 @@ impl Bank {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
total_staked += *staked;
|
total_staked += *staked;
|
||||||
VoteState::deserialize(&account.data)
|
let node_pubkey = account.vote_state().as_ref().ok()?.node_pubkey;
|
||||||
.ok()
|
Some((node_pubkey, *staked))
|
||||||
.map(|vote_state| (vote_state.node_pubkey, *staked))
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<(Pubkey, u64)>>();
|
.collect::<Vec<(Pubkey, u64)>>();
|
||||||
|
@ -4098,10 +4098,25 @@ impl Bank {
|
||||||
|
|
||||||
/// current vote accounts for this bank along with the stake
|
/// current vote accounts for this bank along with the stake
|
||||||
/// attributed to each account
|
/// attributed to each account
|
||||||
pub fn vote_accounts(&self) -> HashMap<Pubkey, (u64, Account)> {
|
/// Note: This clones the entire vote-accounts hashmap. For a single
|
||||||
|
/// account lookup use get_vote_account instead.
|
||||||
|
pub fn vote_accounts(&self) -> HashMap<Pubkey, (u64 /*stake*/, ArcVoteAccount)> {
|
||||||
self.stakes.read().unwrap().vote_accounts().clone()
|
self.stakes.read().unwrap().vote_accounts().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Vote account for the given vote account pubkey along with the stake.
|
||||||
|
pub fn get_vote_account(
|
||||||
|
&self,
|
||||||
|
vote_account: &Pubkey,
|
||||||
|
) -> Option<(u64 /*stake*/, ArcVoteAccount)> {
|
||||||
|
self.stakes
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.vote_accounts()
|
||||||
|
.get(vote_account)
|
||||||
|
.cloned()
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the EpochStakes for a given epoch
|
/// Get the EpochStakes for a given epoch
|
||||||
pub fn epoch_stakes(&self, epoch: Epoch) -> Option<&EpochStakes> {
|
pub fn epoch_stakes(&self, epoch: Epoch) -> Option<&EpochStakes> {
|
||||||
self.epoch_stakes.get(&epoch)
|
self.epoch_stakes.get(&epoch)
|
||||||
|
@ -4113,7 +4128,10 @@ impl Bank {
|
||||||
|
|
||||||
/// vote accounts for the specific epoch along with the stake
|
/// vote accounts for the specific epoch along with the stake
|
||||||
/// attributed to each account
|
/// attributed to each account
|
||||||
pub fn epoch_vote_accounts(&self, epoch: Epoch) -> Option<&HashMap<Pubkey, (u64, Account)>> {
|
pub fn epoch_vote_accounts(
|
||||||
|
&self,
|
||||||
|
epoch: Epoch,
|
||||||
|
) -> Option<&HashMap<Pubkey, (u64, ArcVoteAccount)>> {
|
||||||
self.epoch_stakes
|
self.epoch_stakes
|
||||||
.get(&epoch)
|
.get(&epoch)
|
||||||
.map(|epoch_stakes| Stakes::vote_accounts(epoch_stakes.stakes()))
|
.map(|epoch_stakes| Stakes::vote_accounts(epoch_stakes.stakes()))
|
||||||
|
@ -7741,7 +7759,7 @@ pub(crate) mod tests {
|
||||||
accounts
|
accounts
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(pubkey, (stake, account))| {
|
.filter_map(|(pubkey, (stake, account))| {
|
||||||
if let Ok(vote_state) = VoteState::deserialize(&account.data) {
|
if let Ok(vote_state) = account.vote_state().as_ref() {
|
||||||
if vote_state.node_pubkey == leader_pubkey {
|
if vote_state.node_pubkey == leader_pubkey {
|
||||||
Some((*pubkey, *stake))
|
Some((*pubkey, *stake))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::stakes::Stakes;
|
use crate::{stakes::Stakes, vote_account::ArcVoteAccount};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use solana_sdk::{account::Account, clock::Epoch, pubkey::Pubkey};
|
use solana_sdk::{clock::Epoch, pubkey::Pubkey};
|
||||||
use solana_vote_program::vote_state::VoteState;
|
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
pub type NodeIdToVoteAccounts = HashMap<Pubkey, NodeVoteAccounts>;
|
pub type NodeIdToVoteAccounts = HashMap<Pubkey, NodeVoteAccounts>;
|
||||||
|
@ -58,7 +57,7 @@ impl EpochStakes {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_epoch_vote_accounts(
|
fn parse_epoch_vote_accounts(
|
||||||
epoch_vote_accounts: &HashMap<Pubkey, (u64, Account)>,
|
epoch_vote_accounts: &HashMap<Pubkey, (u64, ArcVoteAccount)>,
|
||||||
leader_schedule_epoch: Epoch,
|
leader_schedule_epoch: Epoch,
|
||||||
) -> (u64, NodeIdToVoteAccounts, EpochAuthorizedVoters) {
|
) -> (u64, NodeIdToVoteAccounts, EpochAuthorizedVoters) {
|
||||||
let mut node_id_to_vote_accounts: NodeIdToVoteAccounts = HashMap::new();
|
let mut node_id_to_vote_accounts: NodeIdToVoteAccounts = HashMap::new();
|
||||||
|
@ -69,19 +68,21 @@ impl EpochStakes {
|
||||||
let epoch_authorized_voters = epoch_vote_accounts
|
let epoch_authorized_voters = epoch_vote_accounts
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(key, (stake, account))| {
|
.filter_map(|(key, (stake, account))| {
|
||||||
let vote_state = VoteState::from(&account);
|
let vote_state = account.vote_state();
|
||||||
if vote_state.is_none() {
|
let vote_state = match vote_state.as_ref() {
|
||||||
datapoint_warn!(
|
Err(_) => {
|
||||||
"parse_epoch_vote_accounts",
|
datapoint_warn!(
|
||||||
(
|
"parse_epoch_vote_accounts",
|
||||||
"warn",
|
(
|
||||||
format!("Unable to get vote_state from account {}", key),
|
"warn",
|
||||||
String
|
format!("Unable to get vote_state from account {}", key),
|
||||||
),
|
String
|
||||||
);
|
),
|
||||||
return None;
|
);
|
||||||
}
|
return None;
|
||||||
let vote_state = vote_state.unwrap();
|
}
|
||||||
|
Ok(vote_state) => vote_state,
|
||||||
|
};
|
||||||
if *stake > 0 {
|
if *stake > 0 {
|
||||||
// Read out the authorized voters
|
// Read out the authorized voters
|
||||||
let authorized_voter = vote_state
|
let authorized_voter = vote_state
|
||||||
|
@ -113,6 +114,7 @@ impl EpochStakes {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use solana_sdk::account::Account;
|
||||||
use solana_vote_program::vote_state::create_account_with_authorized;
|
use solana_vote_program::vote_state::create_account_with_authorized;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
|
@ -181,9 +183,12 @@ pub(crate) mod tests {
|
||||||
let epoch_vote_accounts: HashMap<_, _> = vote_accounts_map
|
let epoch_vote_accounts: HashMap<_, _> = vote_accounts_map
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|(_, vote_accounts)| {
|
.flat_map(|(_, vote_accounts)| {
|
||||||
vote_accounts
|
vote_accounts.iter().map(|v| {
|
||||||
.iter()
|
(
|
||||||
.map(|v| (v.vote_account, (stake_per_account, v.account.clone())))
|
v.vote_account,
|
||||||
|
(stake_per_account, ArcVoteAccount::from(v.account.clone())),
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ pub mod status_cache;
|
||||||
mod system_instruction_processor;
|
mod system_instruction_processor;
|
||||||
pub mod transaction_batch;
|
pub mod transaction_batch;
|
||||||
pub mod transaction_utils;
|
pub mod transaction_utils;
|
||||||
|
pub mod vote_account;
|
||||||
pub mod vote_sender_types;
|
pub mod vote_sender_types;
|
||||||
|
|
||||||
extern crate solana_config_program;
|
extern crate solana_config_program;
|
||||||
|
|
|
@ -261,7 +261,7 @@ mod test_bank_serialize {
|
||||||
|
|
||||||
// These some what long test harness is required to freeze the ABI of
|
// These some what long test harness is required to freeze the ABI of
|
||||||
// Bank's serialization due to versioned nature
|
// Bank's serialization due to versioned nature
|
||||||
#[frozen_abi(digest = "Giao4XJq9QgW78sqmT3nRMvENt4BgHXdzphCDGFPbXqW")]
|
#[frozen_abi(digest = "8bNY87hccyDYCRar1gM3NSvpvtiUM3W3rGeJLJayz42e")]
|
||||||
#[derive(Serialize, AbiExample)]
|
#[derive(Serialize, AbiExample)]
|
||||||
pub struct BankAbiTestWrapperFuture {
|
pub struct BankAbiTestWrapperFuture {
|
||||||
#[serde(serialize_with = "wrapper_future")]
|
#[serde(serialize_with = "wrapper_future")]
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
//! Stakes serve as a cache of stake and vote accounts to derive
|
//! Stakes serve as a cache of stake and vote accounts to derive
|
||||||
//! node stakes
|
//! node stakes
|
||||||
|
use crate::vote_account::ArcVoteAccount;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account, clock::Epoch, pubkey::Pubkey, sysvar::stake_history::StakeHistory,
|
account::Account, clock::Epoch, pubkey::Pubkey, sysvar::stake_history::StakeHistory,
|
||||||
};
|
};
|
||||||
use solana_stake_program::stake_state::{new_stake_history_entry, Delegation, StakeState};
|
use solana_stake_program::stake_state::{new_stake_history_entry, Delegation, StakeState};
|
||||||
use solana_vote_program::vote_state::VoteState;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Default, Clone, PartialEq, Debug, Deserialize, Serialize, AbiExample)]
|
#[derive(Default, Clone, PartialEq, Debug, Deserialize, Serialize, AbiExample)]
|
||||||
pub struct Stakes {
|
pub struct Stakes {
|
||||||
/// vote accounts
|
/// vote accounts
|
||||||
vote_accounts: HashMap<Pubkey, (u64, Account)>,
|
vote_accounts: HashMap<Pubkey, (u64, ArcVoteAccount)>,
|
||||||
|
|
||||||
/// stake_delegations
|
/// stake_delegations
|
||||||
stake_delegations: HashMap<Pubkey, Delegation>,
|
stake_delegations: HashMap<Pubkey, Delegation>,
|
||||||
|
@ -106,7 +106,7 @@ impl Stakes {
|
||||||
+ self
|
+ self
|
||||||
.vote_accounts
|
.vote_accounts
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(_pubkey, (_staked, vote_account))| vote_account.lamports)
|
.map(|(_pubkey, (_staked, vote_account))| vote_account.lamports())
|
||||||
.sum::<u64>()
|
.sum::<u64>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ impl Stakes {
|
||||||
pubkey: &Pubkey,
|
pubkey: &Pubkey,
|
||||||
account: &Account,
|
account: &Account,
|
||||||
fix_stake_deactivate: bool,
|
fix_stake_deactivate: bool,
|
||||||
) -> Option<Account> {
|
) -> Option<ArcVoteAccount> {
|
||||||
if solana_vote_program::check_id(&account.owner) {
|
if solana_vote_program::check_id(&account.owner) {
|
||||||
let old = self.vote_accounts.remove(pubkey);
|
let old = self.vote_accounts.remove(pubkey);
|
||||||
if account.lamports != 0 {
|
if account.lamports != 0 {
|
||||||
|
@ -137,7 +137,8 @@ impl Stakes {
|
||||||
|v| v.0,
|
|v| v.0,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.vote_accounts.insert(*pubkey, (stake, account.clone()));
|
self.vote_accounts
|
||||||
|
.insert(*pubkey, (stake, ArcVoteAccount::from(account.clone())));
|
||||||
}
|
}
|
||||||
old.map(|(_, account)| account)
|
old.map(|(_, account)| account)
|
||||||
} else if solana_stake_program::check_id(&account.owner) {
|
} else if solana_stake_program::check_id(&account.owner) {
|
||||||
|
@ -191,7 +192,7 @@ impl Stakes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vote_accounts(&self) -> &HashMap<Pubkey, (u64, Account)> {
|
pub fn vote_accounts(&self) -> &HashMap<Pubkey, (u64, ArcVoteAccount)> {
|
||||||
&self.vote_accounts
|
&self.vote_accounts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,11 +201,12 @@ impl Stakes {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn highest_staked_node(&self) -> Option<Pubkey> {
|
pub fn highest_staked_node(&self) -> Option<Pubkey> {
|
||||||
self.vote_accounts
|
let (_pubkey, (_stake, vote_account)) = self
|
||||||
|
.vote_accounts
|
||||||
.iter()
|
.iter()
|
||||||
.max_by(|(_ak, av), (_bk, bv)| av.0.cmp(&bv.0))
|
.max_by(|(_ak, av), (_bk, bv)| av.0.cmp(&bv.0))?;
|
||||||
.and_then(|(_k, (_stake, account))| VoteState::from(account))
|
let node_pubkey = vote_account.vote_state().as_ref().ok()?.node_pubkey;
|
||||||
.map(|vote_state| vote_state.node_pubkey)
|
Some(node_pubkey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -523,7 +525,7 @@ pub mod tests {
|
||||||
pub fn vote_balance_and_warmed_staked(&self) -> u64 {
|
pub fn vote_balance_and_warmed_staked(&self) -> u64 {
|
||||||
self.vote_accounts
|
self.vote_accounts
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(_pubkey, (staked, account))| staked + account.lamports)
|
.map(|(_pubkey, (staked, account))| staked + account.lamports())
|
||||||
.sum()
|
.sum()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
use serde::de::{Deserialize, Deserializer};
|
||||||
|
use serde::ser::{Serialize, Serializer};
|
||||||
|
use solana_sdk::{account::Account, instruction::InstructionError};
|
||||||
|
use solana_vote_program::vote_state::VoteState;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::sync::{Arc, Once, RwLock, RwLockReadGuard};
|
||||||
|
|
||||||
|
// The value here does not matter. It will be overwritten
|
||||||
|
// at the first call to VoteAccount::vote_state().
|
||||||
|
const INVALID_VOTE_STATE: Result<VoteState, InstructionError> =
|
||||||
|
Err(InstructionError::InvalidAccountData);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, AbiExample)]
|
||||||
|
pub struct ArcVoteAccount(Arc<VoteAccount>);
|
||||||
|
|
||||||
|
#[derive(Debug, AbiExample)]
|
||||||
|
pub struct VoteAccount {
|
||||||
|
account: Account,
|
||||||
|
vote_state: RwLock<Result<VoteState, InstructionError>>,
|
||||||
|
vote_state_once: Once,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VoteAccount {
|
||||||
|
pub fn lamports(&self) -> u64 {
|
||||||
|
self.account.lamports
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vote_state(&self) -> RwLockReadGuard<Result<VoteState, InstructionError>> {
|
||||||
|
self.vote_state_once.call_once(|| {
|
||||||
|
*self.vote_state.write().unwrap() = VoteState::deserialize(&self.account.data);
|
||||||
|
});
|
||||||
|
self.vote_state.read().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for ArcVoteAccount {
|
||||||
|
type Target = VoteAccount;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.0.deref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for ArcVoteAccount {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
self.account.serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for ArcVoteAccount {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let account = Account::deserialize(deserializer)?;
|
||||||
|
Ok(Self::from(account))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Account> for ArcVoteAccount {
|
||||||
|
fn from(account: Account) -> Self {
|
||||||
|
Self(Arc::new(VoteAccount::from(account)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Account> for VoteAccount {
|
||||||
|
fn from(account: Account) -> Self {
|
||||||
|
Self {
|
||||||
|
account,
|
||||||
|
vote_state: RwLock::new(INVALID_VOTE_STATE),
|
||||||
|
vote_state_once: Once::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for VoteAccount {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
account: Account::default(),
|
||||||
|
vote_state: RwLock::new(INVALID_VOTE_STATE),
|
||||||
|
vote_state_once: Once::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<VoteAccount> for VoteAccount {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.account == other.account
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use rand::Rng;
|
||||||
|
use solana_sdk::{pubkey::Pubkey, sysvar::clock::Clock};
|
||||||
|
use solana_vote_program::vote_state::{VoteInit, VoteStateVersions};
|
||||||
|
|
||||||
|
fn new_rand_vote_account<R: Rng>(rng: &mut R) -> (Account, VoteState) {
|
||||||
|
let vote_init = VoteInit {
|
||||||
|
node_pubkey: Pubkey::new_unique(),
|
||||||
|
authorized_voter: Pubkey::new_unique(),
|
||||||
|
authorized_withdrawer: Pubkey::new_unique(),
|
||||||
|
commission: rng.gen(),
|
||||||
|
};
|
||||||
|
let clock = Clock {
|
||||||
|
slot: rng.gen(),
|
||||||
|
epoch_start_timestamp: rng.gen(),
|
||||||
|
epoch: rng.gen(),
|
||||||
|
leader_schedule_epoch: rng.gen(),
|
||||||
|
unix_timestamp: rng.gen(),
|
||||||
|
};
|
||||||
|
let vote_state = VoteState::new(&vote_init, &clock);
|
||||||
|
let account = Account::new_data(
|
||||||
|
rng.gen(), // lamports
|
||||||
|
&VoteStateVersions::Current(Box::new(vote_state.clone())),
|
||||||
|
&Pubkey::new_unique(), // owner
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
(account, vote_state)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vote_account() {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let (account, vote_state) = new_rand_vote_account(&mut rng);
|
||||||
|
let lamports = account.lamports;
|
||||||
|
let vote_account = ArcVoteAccount::from(account);
|
||||||
|
assert_eq!(lamports, vote_account.lamports());
|
||||||
|
assert_eq!(vote_state, *vote_account.vote_state().as_ref().unwrap());
|
||||||
|
// 2nd call to .vote_state() should return the cached value.
|
||||||
|
assert_eq!(vote_state, *vote_account.vote_state().as_ref().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vote_account_serialize() {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let (account, vote_state) = new_rand_vote_account(&mut rng);
|
||||||
|
let vote_account = ArcVoteAccount::from(account.clone());
|
||||||
|
assert_eq!(vote_state, *vote_account.vote_state().as_ref().unwrap());
|
||||||
|
// Assert than ArcVoteAccount has the same wire format as Account.
|
||||||
|
assert_eq!(
|
||||||
|
bincode::serialize(&account).unwrap(),
|
||||||
|
bincode::serialize(&vote_account).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vote_account_deserialize() {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let (account, vote_state) = new_rand_vote_account(&mut rng);
|
||||||
|
let data = bincode::serialize(&account).unwrap();
|
||||||
|
let vote_account = ArcVoteAccount::from(account);
|
||||||
|
assert_eq!(vote_state, *vote_account.vote_state().as_ref().unwrap());
|
||||||
|
let other_vote_account: ArcVoteAccount = bincode::deserialize(&data).unwrap();
|
||||||
|
assert_eq!(vote_account, other_vote_account);
|
||||||
|
assert_eq!(
|
||||||
|
vote_state,
|
||||||
|
*other_vote_account.vote_state().as_ref().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vote_account_round_trip() {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let (account, vote_state) = new_rand_vote_account(&mut rng);
|
||||||
|
let vote_account = ArcVoteAccount::from(account);
|
||||||
|
assert_eq!(vote_state, *vote_account.vote_state().as_ref().unwrap());
|
||||||
|
let data = bincode::serialize(&vote_account).unwrap();
|
||||||
|
let other_vote_account: ArcVoteAccount = bincode::deserialize(&data).unwrap();
|
||||||
|
// Assert that serialize->deserialized returns the same ArcVoteAccount.
|
||||||
|
assert_eq!(vote_account, other_vote_account);
|
||||||
|
assert_eq!(
|
||||||
|
vote_state,
|
||||||
|
*other_vote_account.vote_state().as_ref().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
/// A helper for calculating a stake-weighted timestamp estimate from a set of timestamps and epoch
|
/// A helper for calculating a stake-weighted timestamp estimate from a set of timestamps and epoch
|
||||||
/// stake.
|
/// stake.
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
|
||||||
clock::{Slot, UnixTimestamp},
|
clock::{Slot, UnixTimestamp},
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
};
|
};
|
||||||
|
@ -19,9 +18,9 @@ pub enum EstimateType {
|
||||||
Unbounded, // Deprecated. Remove in the Solana v1.6.0 timeframe
|
Unbounded, // Deprecated. Remove in the Solana v1.6.0 timeframe
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_stake_weighted_timestamp(
|
pub fn calculate_stake_weighted_timestamp<T>(
|
||||||
unique_timestamps: &HashMap<Pubkey, (Slot, UnixTimestamp)>,
|
unique_timestamps: &HashMap<Pubkey, (Slot, UnixTimestamp)>,
|
||||||
stakes: &HashMap<Pubkey, (u64, Account)>,
|
stakes: &HashMap<Pubkey, (u64, T /*Account|ArcVoteAccount*/)>,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
slot_duration: Duration,
|
slot_duration: Duration,
|
||||||
estimate_type: EstimateType,
|
estimate_type: EstimateType,
|
||||||
|
@ -44,9 +43,9 @@ pub fn calculate_stake_weighted_timestamp(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_unbounded_stake_weighted_timestamp(
|
fn calculate_unbounded_stake_weighted_timestamp<T>(
|
||||||
unique_timestamps: &HashMap<Pubkey, (Slot, UnixTimestamp)>,
|
unique_timestamps: &HashMap<Pubkey, (Slot, UnixTimestamp)>,
|
||||||
stakes: &HashMap<Pubkey, (u64, Account)>,
|
stakes: &HashMap<Pubkey, (u64, T /*Account|ArcVoteAccount*/)>,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
slot_duration: Duration,
|
slot_duration: Duration,
|
||||||
) -> Option<UnixTimestamp> {
|
) -> Option<UnixTimestamp> {
|
||||||
|
@ -71,9 +70,9 @@ fn calculate_unbounded_stake_weighted_timestamp(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_bounded_stake_weighted_timestamp(
|
fn calculate_bounded_stake_weighted_timestamp<T>(
|
||||||
unique_timestamps: &HashMap<Pubkey, (Slot, UnixTimestamp)>,
|
unique_timestamps: &HashMap<Pubkey, (Slot, UnixTimestamp)>,
|
||||||
stakes: &HashMap<Pubkey, (u64, Account)>,
|
stakes: &HashMap<Pubkey, (u64, T /*Account|ArcVoteAccount*/)>,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
slot_duration: Duration,
|
slot_duration: Duration,
|
||||||
epoch_start_timestamp: Option<(Slot, UnixTimestamp)>,
|
epoch_start_timestamp: Option<(Slot, UnixTimestamp)>,
|
||||||
|
@ -135,7 +134,7 @@ fn calculate_bounded_stake_weighted_timestamp(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use solana_sdk::native_token::sol_to_lamports;
|
use solana_sdk::{account::Account, native_token::sol_to_lamports};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_calculate_stake_weighted_timestamp() {
|
fn test_calculate_stake_weighted_timestamp() {
|
||||||
|
|
Loading…
Reference in New Issue