2020-03-26 19:57:27 -07:00
|
|
|
use crate::{
|
2020-06-23 10:30:09 +09:00
|
|
|
cluster_info_vote_listener::SlotVoteTracker,
|
|
|
|
cluster_slots::SlotPubkeys,
|
|
|
|
pubkey_references::PubkeyReferences,
|
2020-05-11 22:20:11 -07:00
|
|
|
replay_stage::SUPERMINORITY_THRESHOLD,
|
2020-06-23 10:30:09 +09:00
|
|
|
{consensus::Stake, consensus::VotedStakes},
|
2020-03-26 19:57:27 -07:00
|
|
|
};
|
2020-06-17 09:27:03 -06:00
|
|
|
use solana_ledger::blockstore_processor::{ConfirmationProgress, ConfirmationTiming};
|
2020-11-30 17:18:33 +00:00
|
|
|
use solana_runtime::{bank::Bank, bank_forks::BankForks, vote_account::ArcVoteAccount};
|
|
|
|
use solana_sdk::{clock::Slot, hash::Hash, pubkey::Pubkey};
|
2020-03-26 19:57:27 -07:00
|
|
|
use std::{
|
2020-05-11 22:20:11 -07:00
|
|
|
collections::{BTreeMap, HashMap, HashSet},
|
2020-03-26 19:57:27 -07:00
|
|
|
rc::Rc,
|
|
|
|
sync::{Arc, RwLock},
|
|
|
|
};
|
|
|
|
|
2020-07-06 17:59:17 +09:00
|
|
|
type VotedSlot = Slot;
|
|
|
|
type ExpirationSlot = Slot;
|
|
|
|
pub(crate) type LockoutIntervals = BTreeMap<ExpirationSlot, Vec<(VotedSlot, Rc<Pubkey>)>>;
|
2020-05-11 22:20:11 -07:00
|
|
|
|
2020-03-26 19:57:27 -07:00
|
|
|
#[derive(Default)]
|
|
|
|
pub(crate) struct ReplaySlotStats(ConfirmationTiming);
|
|
|
|
impl std::ops::Deref for ReplaySlotStats {
|
|
|
|
type Target = ConfirmationTiming;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl std::ops::DerefMut for ReplaySlotStats {
|
|
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
|
|
&mut self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ReplaySlotStats {
|
|
|
|
pub fn report_stats(&self, slot: Slot, num_entries: usize, num_shreds: u64) {
|
|
|
|
datapoint_info!(
|
|
|
|
"replay-slot-stats",
|
|
|
|
("slot", slot as i64, i64),
|
|
|
|
("fetch_entries_time", self.fetch_elapsed as i64, i64),
|
|
|
|
(
|
|
|
|
"fetch_entries_fail_time",
|
|
|
|
self.fetch_fail_elapsed as i64,
|
|
|
|
i64
|
|
|
|
),
|
2020-06-16 14:00:29 -07:00
|
|
|
(
|
|
|
|
"entry_poh_verification_time",
|
|
|
|
self.poh_verify_elapsed as i64,
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"entry_transaction_verification_time",
|
|
|
|
self.transaction_verify_elapsed as i64,
|
|
|
|
i64
|
|
|
|
),
|
2020-03-26 19:57:27 -07:00
|
|
|
("replay_time", self.replay_elapsed as i64, i64),
|
|
|
|
(
|
|
|
|
"replay_total_elapsed",
|
|
|
|
self.started.elapsed().as_micros() as i64,
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
("total_entries", num_entries as i64, i64),
|
|
|
|
("total_shreds", num_shreds as i64, i64),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub(crate) struct ValidatorStakeInfo {
|
|
|
|
pub validator_vote_pubkey: Pubkey,
|
|
|
|
pub stake: u64,
|
|
|
|
pub total_epoch_stake: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for ValidatorStakeInfo {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
stake: 0,
|
|
|
|
validator_vote_pubkey: Pubkey::default(),
|
|
|
|
total_epoch_stake: 1,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ValidatorStakeInfo {
|
|
|
|
pub fn new(validator_vote_pubkey: Pubkey, stake: u64, total_epoch_stake: u64) -> Self {
|
|
|
|
Self {
|
|
|
|
validator_vote_pubkey,
|
|
|
|
stake,
|
|
|
|
total_epoch_stake,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) struct ForkProgress {
|
|
|
|
pub(crate) is_dead: bool,
|
|
|
|
pub(crate) fork_stats: ForkStats,
|
|
|
|
pub(crate) propagated_stats: PropagatedStats,
|
|
|
|
pub(crate) replay_stats: ReplaySlotStats,
|
|
|
|
pub(crate) replay_progress: ConfirmationProgress,
|
2020-04-08 14:35:24 -07:00
|
|
|
// Note `num_blocks_on_fork` and `num_dropped_blocks_on_fork` only
|
|
|
|
// count new blocks replayed since last restart, which won't include
|
|
|
|
// blocks already existing in the ledger/before snapshot at start,
|
|
|
|
// so these stats do not span all of time
|
|
|
|
pub(crate) num_blocks_on_fork: u64,
|
|
|
|
pub(crate) num_dropped_blocks_on_fork: u64,
|
2020-03-26 19:57:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ForkProgress {
|
|
|
|
pub fn new(
|
|
|
|
last_entry: Hash,
|
|
|
|
prev_leader_slot: Option<Slot>,
|
|
|
|
validator_stake_info: Option<ValidatorStakeInfo>,
|
2020-04-08 14:35:24 -07:00
|
|
|
num_blocks_on_fork: u64,
|
|
|
|
num_dropped_blocks_on_fork: u64,
|
2020-03-26 19:57:27 -07:00
|
|
|
) -> Self {
|
|
|
|
let (
|
|
|
|
is_leader_slot,
|
|
|
|
propagated_validators_stake,
|
|
|
|
propagated_validators,
|
|
|
|
is_propagated,
|
|
|
|
total_epoch_stake,
|
|
|
|
) = validator_stake_info
|
|
|
|
.map(|info| {
|
|
|
|
(
|
|
|
|
true,
|
|
|
|
info.stake,
|
|
|
|
vec![Rc::new(info.validator_vote_pubkey)]
|
|
|
|
.into_iter()
|
|
|
|
.collect(),
|
|
|
|
{
|
|
|
|
if info.total_epoch_stake == 0 {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
info.stake as f64 / info.total_epoch_stake as f64
|
|
|
|
> SUPERMINORITY_THRESHOLD
|
|
|
|
}
|
|
|
|
},
|
|
|
|
info.total_epoch_stake,
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.unwrap_or((false, 0, HashSet::new(), false, 0));
|
|
|
|
Self {
|
|
|
|
is_dead: false,
|
|
|
|
fork_stats: ForkStats::default(),
|
|
|
|
replay_stats: ReplaySlotStats::default(),
|
|
|
|
replay_progress: ConfirmationProgress::new(last_entry),
|
2020-04-08 14:35:24 -07:00
|
|
|
num_blocks_on_fork,
|
|
|
|
num_dropped_blocks_on_fork,
|
2020-03-26 19:57:27 -07:00
|
|
|
propagated_stats: PropagatedStats {
|
|
|
|
prev_leader_slot,
|
|
|
|
is_leader_slot,
|
|
|
|
propagated_validators_stake,
|
|
|
|
propagated_validators,
|
|
|
|
is_propagated,
|
|
|
|
total_epoch_stake,
|
|
|
|
..PropagatedStats::default()
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new_from_bank(
|
|
|
|
bank: &Bank,
|
|
|
|
my_pubkey: &Pubkey,
|
|
|
|
voting_pubkey: &Pubkey,
|
|
|
|
prev_leader_slot: Option<Slot>,
|
2020-04-08 14:35:24 -07:00
|
|
|
num_blocks_on_fork: u64,
|
|
|
|
num_dropped_blocks_on_fork: u64,
|
2020-03-26 19:57:27 -07:00
|
|
|
) -> Self {
|
|
|
|
let validator_fork_info = {
|
2020-09-11 02:03:11 -07:00
|
|
|
if bank.collector_id() == my_pubkey {
|
2020-03-26 19:57:27 -07:00
|
|
|
let stake = bank.epoch_vote_account_stake(voting_pubkey);
|
|
|
|
Some(ValidatorStakeInfo::new(
|
|
|
|
*voting_pubkey,
|
|
|
|
stake,
|
|
|
|
bank.total_epoch_stake(),
|
|
|
|
))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-04-08 14:35:24 -07:00
|
|
|
Self::new(
|
|
|
|
bank.last_blockhash(),
|
|
|
|
prev_leader_slot,
|
|
|
|
validator_fork_info,
|
|
|
|
num_blocks_on_fork,
|
|
|
|
num_dropped_blocks_on_fork,
|
|
|
|
)
|
2020-03-26 19:57:27 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Default)]
|
|
|
|
pub(crate) struct ForkStats {
|
|
|
|
pub(crate) weight: u128,
|
|
|
|
pub(crate) fork_weight: u128,
|
2020-06-23 10:30:09 +09:00
|
|
|
pub(crate) total_stake: Stake,
|
2020-03-26 19:57:27 -07:00
|
|
|
pub(crate) block_height: u64,
|
|
|
|
pub(crate) has_voted: bool,
|
|
|
|
pub(crate) is_recent: bool,
|
|
|
|
pub(crate) is_empty: bool,
|
|
|
|
pub(crate) vote_threshold: bool,
|
|
|
|
pub(crate) is_locked_out: bool,
|
2020-06-23 10:30:09 +09:00
|
|
|
pub(crate) voted_stakes: VotedStakes,
|
2020-03-26 19:57:27 -07:00
|
|
|
pub(crate) confirmation_reported: bool,
|
|
|
|
pub(crate) computed: bool,
|
2020-05-11 22:20:11 -07:00
|
|
|
pub(crate) lockout_intervals: LockoutIntervals,
|
2020-03-26 19:57:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Default)]
|
|
|
|
pub(crate) struct PropagatedStats {
|
|
|
|
pub(crate) propagated_validators: HashSet<Rc<Pubkey>>,
|
2020-03-30 19:57:11 -07:00
|
|
|
pub(crate) propagated_node_ids: HashSet<Rc<Pubkey>>,
|
2020-03-26 19:57:27 -07:00
|
|
|
pub(crate) propagated_validators_stake: u64,
|
|
|
|
pub(crate) is_propagated: bool,
|
|
|
|
pub(crate) is_leader_slot: bool,
|
|
|
|
pub(crate) prev_leader_slot: Option<Slot>,
|
|
|
|
pub(crate) slot_vote_tracker: Option<Arc<RwLock<SlotVoteTracker>>>,
|
2020-03-30 19:57:11 -07:00
|
|
|
pub(crate) cluster_slot_pubkeys: Option<Arc<RwLock<SlotPubkeys>>>,
|
2020-03-26 19:57:27 -07:00
|
|
|
pub(crate) total_epoch_stake: u64,
|
|
|
|
}
|
|
|
|
|
2020-03-30 19:57:11 -07:00
|
|
|
impl PropagatedStats {
|
|
|
|
pub fn add_vote_pubkey(
|
|
|
|
&mut self,
|
|
|
|
vote_pubkey: &Pubkey,
|
2020-05-11 22:20:11 -07:00
|
|
|
all_pubkeys: &mut PubkeyReferences,
|
2020-03-30 19:57:11 -07:00
|
|
|
stake: u64,
|
|
|
|
) {
|
|
|
|
if !self.propagated_validators.contains(vote_pubkey) {
|
2020-05-11 22:20:11 -07:00
|
|
|
let cached_pubkey = all_pubkeys.get_or_insert(vote_pubkey);
|
|
|
|
self.propagated_validators.insert(cached_pubkey);
|
2020-03-30 19:57:11 -07:00
|
|
|
self.propagated_validators_stake += stake;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn add_node_pubkey(
|
|
|
|
&mut self,
|
|
|
|
node_pubkey: &Pubkey,
|
2020-05-11 22:20:11 -07:00
|
|
|
all_pubkeys: &mut PubkeyReferences,
|
2020-03-30 19:57:11 -07:00
|
|
|
bank: &Bank,
|
|
|
|
) {
|
|
|
|
if !self.propagated_node_ids.contains(node_pubkey) {
|
|
|
|
let node_vote_accounts = bank
|
|
|
|
.epoch_vote_accounts_for_node_id(&node_pubkey)
|
|
|
|
.map(|v| &v.vote_accounts);
|
|
|
|
|
|
|
|
if let Some(node_vote_accounts) = node_vote_accounts {
|
|
|
|
self.add_node_pubkey_internal(
|
|
|
|
node_pubkey,
|
|
|
|
all_pubkeys,
|
|
|
|
node_vote_accounts,
|
|
|
|
bank.epoch_vote_accounts(bank.epoch())
|
|
|
|
.expect("Epoch stakes for bank's own epoch must exist"),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn add_node_pubkey_internal(
|
|
|
|
&mut self,
|
|
|
|
node_pubkey: &Pubkey,
|
2020-05-11 22:20:11 -07:00
|
|
|
all_pubkeys: &mut PubkeyReferences,
|
2020-03-30 19:57:11 -07:00
|
|
|
vote_account_pubkeys: &[Pubkey],
|
2020-11-30 17:18:33 +00:00
|
|
|
epoch_vote_accounts: &HashMap<Pubkey, (u64, ArcVoteAccount)>,
|
2020-03-30 19:57:11 -07:00
|
|
|
) {
|
2020-05-11 22:20:11 -07:00
|
|
|
let cached_pubkey = all_pubkeys.get_or_insert(node_pubkey);
|
|
|
|
self.propagated_node_ids.insert(cached_pubkey);
|
2020-03-30 19:57:11 -07:00
|
|
|
for vote_account_pubkey in vote_account_pubkeys.iter() {
|
|
|
|
let stake = epoch_vote_accounts
|
|
|
|
.get(vote_account_pubkey)
|
|
|
|
.map(|(stake, _)| *stake)
|
|
|
|
.unwrap_or(0);
|
|
|
|
self.add_vote_pubkey(vote_account_pubkey, all_pubkeys, stake);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-26 19:57:27 -07:00
|
|
|
#[derive(Default)]
|
|
|
|
pub(crate) struct ProgressMap {
|
|
|
|
progress_map: HashMap<Slot, ForkProgress>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::ops::Deref for ProgressMap {
|
|
|
|
type Target = HashMap<Slot, ForkProgress>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.progress_map
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::ops::DerefMut for ProgressMap {
|
|
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
|
|
&mut self.progress_map
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ProgressMap {
|
|
|
|
pub fn insert(&mut self, slot: Slot, fork_progress: ForkProgress) {
|
|
|
|
self.progress_map.insert(slot, fork_progress);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_propagated_stats(&self, slot: Slot) -> Option<&PropagatedStats> {
|
|
|
|
self.progress_map
|
|
|
|
.get(&slot)
|
|
|
|
.map(|fork_progress| &fork_progress.propagated_stats)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_propagated_stats_mut(&mut self, slot: Slot) -> Option<&mut PropagatedStats> {
|
|
|
|
self.progress_map
|
|
|
|
.get_mut(&slot)
|
|
|
|
.map(|fork_progress| &mut fork_progress.propagated_stats)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_fork_stats(&self, slot: Slot) -> Option<&ForkStats> {
|
|
|
|
self.progress_map
|
|
|
|
.get(&slot)
|
|
|
|
.map(|fork_progress| &fork_progress.fork_stats)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_fork_stats_mut(&mut self, slot: Slot) -> Option<&mut ForkStats> {
|
|
|
|
self.progress_map
|
|
|
|
.get_mut(&slot)
|
|
|
|
.map(|fork_progress| &mut fork_progress.fork_stats)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_propagated(&self, slot: Slot) -> bool {
|
|
|
|
let leader_slot_to_check = self.get_latest_leader_slot(slot);
|
|
|
|
|
|
|
|
// prev_leader_slot doesn't exist because already rooted
|
|
|
|
// or this validator hasn't been scheduled as a leader
|
|
|
|
// yet. In both cases the latest leader is vacuously
|
|
|
|
// confirmed
|
|
|
|
leader_slot_to_check
|
|
|
|
.map(|leader_slot_to_check| {
|
|
|
|
// If the leader's stats are None (isn't in the
|
|
|
|
// progress map), this means that prev_leader slot is
|
|
|
|
// rooted, so return true
|
|
|
|
self.get_propagated_stats(leader_slot_to_check)
|
|
|
|
.map(|stats| stats.is_propagated)
|
|
|
|
.unwrap_or(true)
|
|
|
|
})
|
|
|
|
.unwrap_or(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_latest_leader_slot(&self, slot: Slot) -> Option<Slot> {
|
|
|
|
let propagated_stats = self
|
|
|
|
.get_propagated_stats(slot)
|
|
|
|
.expect("All frozen banks must exist in the Progress map");
|
|
|
|
|
|
|
|
if propagated_stats.is_leader_slot {
|
|
|
|
Some(slot)
|
|
|
|
} else {
|
|
|
|
propagated_stats.prev_leader_slot
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_bank_prev_leader_slot(&self, bank: &Bank) -> Option<Slot> {
|
|
|
|
let parent_slot = bank.parent_slot();
|
|
|
|
self.get_propagated_stats(parent_slot)
|
|
|
|
.map(|stats| {
|
|
|
|
if stats.is_leader_slot {
|
|
|
|
Some(parent_slot)
|
|
|
|
} else {
|
|
|
|
stats.prev_leader_slot
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.unwrap_or(None)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn handle_new_root(&mut self, bank_forks: &BankForks) {
|
|
|
|
self.progress_map
|
|
|
|
.retain(|k, _| bank_forks.get(*k).is_some());
|
|
|
|
}
|
2020-04-08 14:35:24 -07:00
|
|
|
|
|
|
|
pub fn log_propagated_stats(&self, slot: Slot, bank_forks: &RwLock<BankForks>) {
|
|
|
|
if let Some(stats) = self.get_propagated_stats(slot) {
|
|
|
|
info!(
|
|
|
|
"Propagated stats:
|
|
|
|
total staked: {},
|
|
|
|
observed staked: {},
|
|
|
|
vote pubkeys: {:?},
|
|
|
|
node_pubkeys: {:?},
|
|
|
|
slot: {},
|
|
|
|
epoch: {:?}",
|
|
|
|
stats.total_epoch_stake,
|
|
|
|
stats.propagated_validators_stake,
|
|
|
|
stats.propagated_validators,
|
|
|
|
stats.propagated_node_ids,
|
|
|
|
slot,
|
|
|
|
bank_forks.read().unwrap().get(slot).map(|x| x.epoch()),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2020-03-26 19:57:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
|
|
|
|
2020-03-30 19:57:11 -07:00
|
|
|
#[test]
|
|
|
|
fn test_add_vote_pubkey() {
|
|
|
|
let mut stats = PropagatedStats::default();
|
2020-05-11 22:20:11 -07:00
|
|
|
let mut all_pubkeys = PubkeyReferences::default();
|
2020-10-19 12:12:08 -07:00
|
|
|
let mut vote_pubkey = solana_sdk::pubkey::new_rand();
|
2020-05-11 22:20:11 -07:00
|
|
|
all_pubkeys.get_or_insert(&vote_pubkey);
|
2020-03-30 19:57:11 -07:00
|
|
|
|
|
|
|
// Add a vote pubkey, the number of references in all_pubkeys
|
|
|
|
// should be 2
|
|
|
|
stats.add_vote_pubkey(&vote_pubkey, &mut all_pubkeys, 1);
|
|
|
|
assert!(stats.propagated_validators.contains(&vote_pubkey));
|
|
|
|
assert_eq!(stats.propagated_validators_stake, 1);
|
2020-05-11 22:20:11 -07:00
|
|
|
assert_eq!(
|
|
|
|
Rc::strong_count(&all_pubkeys.get_or_insert(&vote_pubkey)),
|
|
|
|
3
|
|
|
|
);
|
2020-03-30 19:57:11 -07:00
|
|
|
|
|
|
|
// Adding it again should change no state since the key already existed
|
|
|
|
stats.add_vote_pubkey(&vote_pubkey, &mut all_pubkeys, 1);
|
|
|
|
assert!(stats.propagated_validators.contains(&vote_pubkey));
|
|
|
|
assert_eq!(stats.propagated_validators_stake, 1);
|
|
|
|
|
2020-06-17 21:54:52 -06:00
|
|
|
// Adding another pubkey should succeed
|
2020-10-19 12:12:08 -07:00
|
|
|
vote_pubkey = solana_sdk::pubkey::new_rand();
|
2020-03-30 19:57:11 -07:00
|
|
|
stats.add_vote_pubkey(&vote_pubkey, &mut all_pubkeys, 2);
|
|
|
|
assert!(stats.propagated_validators.contains(&vote_pubkey));
|
|
|
|
assert_eq!(stats.propagated_validators_stake, 3);
|
2020-05-11 22:20:11 -07:00
|
|
|
assert_eq!(
|
|
|
|
Rc::strong_count(&all_pubkeys.get_or_insert(&vote_pubkey)),
|
|
|
|
3
|
|
|
|
);
|
2020-03-30 19:57:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_add_node_pubkey_internal() {
|
|
|
|
let num_vote_accounts = 10;
|
|
|
|
let staked_vote_accounts = 5;
|
2020-10-19 12:12:08 -07:00
|
|
|
let vote_account_pubkeys: Vec<_> = std::iter::repeat_with(solana_sdk::pubkey::new_rand)
|
2020-03-30 19:57:11 -07:00
|
|
|
.take(num_vote_accounts)
|
|
|
|
.collect();
|
|
|
|
let epoch_vote_accounts: HashMap<_, _> = vote_account_pubkeys
|
|
|
|
.iter()
|
|
|
|
.skip(num_vote_accounts - staked_vote_accounts)
|
2020-11-30 17:18:33 +00:00
|
|
|
.map(|pubkey| (*pubkey, (1, ArcVoteAccount::default())))
|
2020-03-30 19:57:11 -07:00
|
|
|
.collect();
|
|
|
|
|
|
|
|
let mut stats = PropagatedStats::default();
|
2020-05-11 22:20:11 -07:00
|
|
|
let mut all_pubkeys = PubkeyReferences::default();
|
2020-10-19 12:12:08 -07:00
|
|
|
let mut node_pubkey = solana_sdk::pubkey::new_rand();
|
2020-05-11 22:20:11 -07:00
|
|
|
all_pubkeys.get_or_insert(&node_pubkey);
|
2020-03-30 19:57:11 -07:00
|
|
|
|
|
|
|
// Add a vote pubkey, the number of references in all_pubkeys
|
|
|
|
// should be 2
|
|
|
|
stats.add_node_pubkey_internal(
|
|
|
|
&node_pubkey,
|
|
|
|
&mut all_pubkeys,
|
|
|
|
&vote_account_pubkeys,
|
|
|
|
&epoch_vote_accounts,
|
|
|
|
);
|
|
|
|
assert!(stats.propagated_node_ids.contains(&node_pubkey));
|
|
|
|
assert_eq!(
|
|
|
|
stats.propagated_validators_stake,
|
|
|
|
staked_vote_accounts as u64
|
|
|
|
);
|
2020-05-11 22:20:11 -07:00
|
|
|
assert_eq!(
|
|
|
|
Rc::strong_count(&all_pubkeys.get_or_insert(&node_pubkey)),
|
|
|
|
3
|
|
|
|
);
|
2020-03-30 19:57:11 -07:00
|
|
|
|
|
|
|
// Adding it again should not change any state
|
|
|
|
stats.add_node_pubkey_internal(
|
|
|
|
&node_pubkey,
|
|
|
|
&mut all_pubkeys,
|
|
|
|
&vote_account_pubkeys,
|
|
|
|
&epoch_vote_accounts,
|
|
|
|
);
|
|
|
|
assert!(stats.propagated_node_ids.contains(&node_pubkey));
|
|
|
|
assert_eq!(
|
|
|
|
stats.propagated_validators_stake,
|
|
|
|
staked_vote_accounts as u64
|
|
|
|
);
|
|
|
|
|
2020-06-17 21:54:52 -06:00
|
|
|
// Adding another pubkey with same vote accounts should succeed, but stake
|
2020-03-30 19:57:11 -07:00
|
|
|
// shouldn't increase
|
2020-10-19 12:12:08 -07:00
|
|
|
node_pubkey = solana_sdk::pubkey::new_rand();
|
2020-03-30 19:57:11 -07:00
|
|
|
stats.add_node_pubkey_internal(
|
|
|
|
&node_pubkey,
|
|
|
|
&mut all_pubkeys,
|
|
|
|
&vote_account_pubkeys,
|
|
|
|
&epoch_vote_accounts,
|
|
|
|
);
|
|
|
|
assert!(stats.propagated_node_ids.contains(&node_pubkey));
|
|
|
|
assert_eq!(
|
|
|
|
stats.propagated_validators_stake,
|
|
|
|
staked_vote_accounts as u64
|
|
|
|
);
|
2020-05-11 22:20:11 -07:00
|
|
|
assert_eq!(
|
|
|
|
Rc::strong_count(&all_pubkeys.get_or_insert(&node_pubkey)),
|
|
|
|
3
|
|
|
|
);
|
2020-03-30 19:57:11 -07:00
|
|
|
|
2020-06-17 21:54:52 -06:00
|
|
|
// Adding another pubkey with different vote accounts should succeed
|
2020-03-30 19:57:11 -07:00
|
|
|
// and increase stake
|
2020-10-19 12:12:08 -07:00
|
|
|
node_pubkey = solana_sdk::pubkey::new_rand();
|
|
|
|
let vote_account_pubkeys: Vec<_> = std::iter::repeat_with(solana_sdk::pubkey::new_rand)
|
2020-03-30 19:57:11 -07:00
|
|
|
.take(num_vote_accounts)
|
|
|
|
.collect();
|
|
|
|
let epoch_vote_accounts: HashMap<_, _> = vote_account_pubkeys
|
|
|
|
.iter()
|
|
|
|
.skip(num_vote_accounts - staked_vote_accounts)
|
2020-11-30 17:18:33 +00:00
|
|
|
.map(|pubkey| (*pubkey, (1, ArcVoteAccount::default())))
|
2020-03-30 19:57:11 -07:00
|
|
|
.collect();
|
|
|
|
stats.add_node_pubkey_internal(
|
|
|
|
&node_pubkey,
|
|
|
|
&mut all_pubkeys,
|
|
|
|
&vote_account_pubkeys,
|
|
|
|
&epoch_vote_accounts,
|
|
|
|
);
|
|
|
|
assert!(stats.propagated_node_ids.contains(&node_pubkey));
|
|
|
|
assert_eq!(
|
|
|
|
stats.propagated_validators_stake,
|
|
|
|
2 * staked_vote_accounts as u64
|
|
|
|
);
|
2020-05-11 22:20:11 -07:00
|
|
|
assert_eq!(
|
|
|
|
Rc::strong_count(&all_pubkeys.get_or_insert(&node_pubkey)),
|
|
|
|
3
|
|
|
|
);
|
2020-03-30 19:57:11 -07:00
|
|
|
}
|
|
|
|
|
2020-03-26 19:57:27 -07:00
|
|
|
#[test]
|
|
|
|
fn test_is_propagated_status_on_construction() {
|
|
|
|
// If the given ValidatorStakeInfo == None, then this is not
|
|
|
|
// a leader slot and is_propagated == false
|
2020-04-08 14:35:24 -07:00
|
|
|
let progress = ForkProgress::new(Hash::default(), Some(9), None, 0, 0);
|
2020-03-26 19:57:27 -07:00
|
|
|
assert!(!progress.propagated_stats.is_propagated);
|
|
|
|
|
|
|
|
// If the stake is zero, then threshold is always achieved
|
|
|
|
let progress = ForkProgress::new(
|
|
|
|
Hash::default(),
|
|
|
|
Some(9),
|
|
|
|
Some(ValidatorStakeInfo {
|
|
|
|
total_epoch_stake: 0,
|
|
|
|
..ValidatorStakeInfo::default()
|
|
|
|
}),
|
2020-04-08 14:35:24 -07:00
|
|
|
0,
|
|
|
|
0,
|
2020-03-26 19:57:27 -07:00
|
|
|
);
|
|
|
|
assert!(progress.propagated_stats.is_propagated);
|
|
|
|
|
|
|
|
// If the stake is non zero, then threshold is not achieved unless
|
|
|
|
// validator has enough stake by itself to pass threshold
|
|
|
|
let progress = ForkProgress::new(
|
|
|
|
Hash::default(),
|
|
|
|
Some(9),
|
|
|
|
Some(ValidatorStakeInfo {
|
|
|
|
total_epoch_stake: 2,
|
|
|
|
..ValidatorStakeInfo::default()
|
|
|
|
}),
|
2020-04-08 14:35:24 -07:00
|
|
|
0,
|
|
|
|
0,
|
2020-03-26 19:57:27 -07:00
|
|
|
);
|
|
|
|
assert!(!progress.propagated_stats.is_propagated);
|
|
|
|
|
|
|
|
// Give the validator enough stake by itself to pass threshold
|
|
|
|
let progress = ForkProgress::new(
|
|
|
|
Hash::default(),
|
|
|
|
Some(9),
|
|
|
|
Some(ValidatorStakeInfo {
|
|
|
|
stake: 1,
|
|
|
|
total_epoch_stake: 2,
|
|
|
|
..ValidatorStakeInfo::default()
|
|
|
|
}),
|
2020-04-08 14:35:24 -07:00
|
|
|
0,
|
|
|
|
0,
|
2020-03-26 19:57:27 -07:00
|
|
|
);
|
|
|
|
assert!(progress.propagated_stats.is_propagated);
|
|
|
|
|
|
|
|
// Check that the default ValidatorStakeInfo::default() constructs a ForkProgress
|
|
|
|
// with is_propagated == false, otherwise propagation tests will fail to run
|
|
|
|
// the proper checks (most will auto-pass without checking anything)
|
|
|
|
let progress = ForkProgress::new(
|
|
|
|
Hash::default(),
|
|
|
|
Some(9),
|
|
|
|
Some(ValidatorStakeInfo::default()),
|
2020-04-08 14:35:24 -07:00
|
|
|
0,
|
|
|
|
0,
|
2020-03-26 19:57:27 -07:00
|
|
|
);
|
|
|
|
assert!(!progress.propagated_stats.is_propagated);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_is_propagated() {
|
|
|
|
let mut progress_map = ProgressMap::default();
|
|
|
|
|
|
|
|
// Insert new ForkProgress for slot 10 (not a leader slot) and its
|
|
|
|
// previous leader slot 9 (leader slot)
|
2020-04-08 14:35:24 -07:00
|
|
|
progress_map.insert(10, ForkProgress::new(Hash::default(), Some(9), None, 0, 0));
|
2020-03-26 19:57:27 -07:00
|
|
|
progress_map.insert(
|
|
|
|
9,
|
2020-04-08 14:35:24 -07:00
|
|
|
ForkProgress::new(
|
|
|
|
Hash::default(),
|
|
|
|
None,
|
|
|
|
Some(ValidatorStakeInfo::default()),
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
),
|
2020-03-26 19:57:27 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
// None of these slot have parents which are confirmed
|
|
|
|
assert!(!progress_map.is_propagated(9));
|
|
|
|
assert!(!progress_map.is_propagated(10));
|
|
|
|
|
|
|
|
// Insert new ForkProgress for slot 8 with no previous leader.
|
|
|
|
// The previous leader before 8, slot 7, does not exist in
|
|
|
|
// progress map, so is_propagated(8) should return true as
|
|
|
|
// this implies the parent is rooted
|
2020-04-08 14:35:24 -07:00
|
|
|
progress_map.insert(8, ForkProgress::new(Hash::default(), Some(7), None, 0, 0));
|
2020-03-26 19:57:27 -07:00
|
|
|
assert!(progress_map.is_propagated(8));
|
|
|
|
|
|
|
|
// If we set the is_propagated = true, is_propagated should return true
|
|
|
|
progress_map
|
|
|
|
.get_propagated_stats_mut(9)
|
|
|
|
.unwrap()
|
|
|
|
.is_propagated = true;
|
|
|
|
assert!(progress_map.is_propagated(9));
|
|
|
|
assert!(progress_map.get(&9).unwrap().propagated_stats.is_propagated);
|
|
|
|
|
|
|
|
// Because slot 9 is now confirmed, then slot 10 is also confirmed b/c 9
|
|
|
|
// is the last leader slot before 10
|
|
|
|
assert!(progress_map.is_propagated(10));
|
|
|
|
|
|
|
|
// If we make slot 10 a leader slot though, even though its previous
|
|
|
|
// leader slot 9 has been confirmed, slot 10 itself is not confirmed
|
|
|
|
progress_map
|
|
|
|
.get_propagated_stats_mut(10)
|
|
|
|
.unwrap()
|
|
|
|
.is_leader_slot = true;
|
|
|
|
assert!(!progress_map.is_propagated(10));
|
|
|
|
}
|
|
|
|
}
|