2021-12-03 09:00:31 -08:00
|
|
|
use {
|
|
|
|
crate::heaviest_subtree_fork_choice::SlotHashKey,
|
|
|
|
solana_sdk::{clock::Slot, hash::Hash, pubkey::Pubkey},
|
|
|
|
std::collections::{hash_map::Entry, HashMap},
|
|
|
|
};
|
2021-04-21 14:40:35 -07:00
|
|
|
|
|
|
|
#[derive(Default)]
|
2021-07-08 19:07:32 -07:00
|
|
|
pub struct LatestValidatorVotesForFrozenBanks {
|
2021-04-21 14:40:35 -07:00
|
|
|
// TODO: Clean outdated/unstaked pubkeys from this list.
|
2021-04-29 14:43:28 -07:00
|
|
|
max_gossip_frozen_votes: HashMap<Pubkey, (Slot, Vec<Hash>)>,
|
|
|
|
max_replay_frozen_votes: HashMap<Pubkey, (Slot, Vec<Hash>)>,
|
2021-04-21 14:40:35 -07:00
|
|
|
// Pubkeys that had their `max_frozen_votes` updated since the last
|
|
|
|
// fork choice update
|
|
|
|
fork_choice_dirty_set: HashMap<Pubkey, (Slot, Vec<Hash>)>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LatestValidatorVotesForFrozenBanks {
|
|
|
|
// `frozen_hash.is_some()` if the bank with slot == `vote_slot` is frozen
|
|
|
|
// Returns whether the vote was actually added, and the latest voted frozen slot
|
2021-07-08 19:07:32 -07:00
|
|
|
pub fn check_add_vote(
|
2021-04-21 14:40:35 -07:00
|
|
|
&mut self,
|
|
|
|
vote_pubkey: Pubkey,
|
|
|
|
vote_slot: Slot,
|
|
|
|
frozen_hash: Option<Hash>,
|
2021-04-29 14:43:28 -07:00
|
|
|
is_replay_vote: bool,
|
2021-04-21 14:40:35 -07:00
|
|
|
) -> (bool, Option<Slot>) {
|
2021-04-29 14:43:28 -07:00
|
|
|
let vote_map = if is_replay_vote {
|
|
|
|
&mut self.max_replay_frozen_votes
|
|
|
|
} else {
|
|
|
|
&mut self.max_gossip_frozen_votes
|
|
|
|
};
|
|
|
|
let pubkey_max_frozen_votes = vote_map.entry(vote_pubkey);
|
2021-04-21 14:40:35 -07:00
|
|
|
if let Some(frozen_hash) = frozen_hash {
|
2021-04-29 14:43:28 -07:00
|
|
|
match pubkey_max_frozen_votes {
|
2021-04-21 14:40:35 -07:00
|
|
|
Entry::Occupied(mut occupied_entry) => {
|
|
|
|
let (latest_frozen_vote_slot, latest_frozen_vote_hashes) =
|
|
|
|
occupied_entry.get_mut();
|
|
|
|
if vote_slot > *latest_frozen_vote_slot {
|
2021-04-29 14:43:28 -07:00
|
|
|
if is_replay_vote {
|
|
|
|
// Only record votes detected through replaying blocks,
|
|
|
|
// because votes in gossip are not consistently observable
|
|
|
|
// if the validator is replacing them.
|
|
|
|
self.fork_choice_dirty_set
|
|
|
|
.insert(vote_pubkey, (vote_slot, vec![frozen_hash]));
|
|
|
|
}
|
2021-04-21 14:40:35 -07:00
|
|
|
*latest_frozen_vote_slot = vote_slot;
|
|
|
|
*latest_frozen_vote_hashes = vec![frozen_hash];
|
|
|
|
return (true, Some(vote_slot));
|
|
|
|
} else if vote_slot == *latest_frozen_vote_slot
|
|
|
|
&& !latest_frozen_vote_hashes.contains(&frozen_hash)
|
|
|
|
{
|
2021-04-29 14:43:28 -07:00
|
|
|
if is_replay_vote {
|
|
|
|
// Only record votes detected through replaying blocks,
|
|
|
|
// because votes in gossip are not consistently observable
|
|
|
|
// if the validator is replacing them.
|
|
|
|
let (_, dirty_frozen_hashes) =
|
|
|
|
self.fork_choice_dirty_set.entry(vote_pubkey).or_default();
|
|
|
|
assert!(!dirty_frozen_hashes.contains(&frozen_hash));
|
|
|
|
dirty_frozen_hashes.push(frozen_hash);
|
|
|
|
}
|
2021-04-21 14:40:35 -07:00
|
|
|
latest_frozen_vote_hashes.push(frozen_hash);
|
|
|
|
return (true, Some(vote_slot));
|
|
|
|
} else {
|
|
|
|
// We have newer votes for this validator, we don't care about this vote
|
|
|
|
return (false, Some(*latest_frozen_vote_slot));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Entry::Vacant(vacant_entry) => {
|
|
|
|
vacant_entry.insert((vote_slot, vec![frozen_hash]));
|
2021-04-29 14:43:28 -07:00
|
|
|
if is_replay_vote {
|
|
|
|
self.fork_choice_dirty_set
|
|
|
|
.insert(vote_pubkey, (vote_slot, vec![frozen_hash]));
|
|
|
|
}
|
2021-04-21 14:40:35 -07:00
|
|
|
return (true, Some(vote_slot));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Non-frozen banks are not inserted because we only track frozen votes in this
|
|
|
|
// struct
|
|
|
|
(
|
|
|
|
false,
|
2021-04-29 14:43:28 -07:00
|
|
|
match pubkey_max_frozen_votes {
|
2021-04-21 14:40:35 -07:00
|
|
|
Entry::Occupied(occupied_entry) => Some(occupied_entry.get().0),
|
|
|
|
Entry::Vacant(_) => None,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-07-08 19:07:32 -07:00
|
|
|
pub fn take_votes_dirty_set(&mut self, root: Slot) -> Vec<(Pubkey, SlotHashKey)> {
|
2021-04-21 14:40:35 -07:00
|
|
|
let new_votes = std::mem::take(&mut self.fork_choice_dirty_set);
|
|
|
|
new_votes
|
|
|
|
.into_iter()
|
|
|
|
.filter(|(_, (slot, _))| *slot >= root)
|
|
|
|
.flat_map(|(pk, (slot, hashes))| {
|
|
|
|
hashes
|
|
|
|
.into_iter()
|
|
|
|
.map(|hash| (pk, (slot, hash)))
|
|
|
|
.collect::<Vec<(Pubkey, SlotHashKey)>>()
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
2021-04-29 14:43:28 -07:00
|
|
|
|
2021-07-08 19:07:32 -07:00
|
|
|
pub fn max_gossip_frozen_votes(&self) -> &HashMap<Pubkey, (Slot, Vec<Hash>)> {
|
2021-05-04 00:51:42 -07:00
|
|
|
&self.max_gossip_frozen_votes
|
|
|
|
}
|
|
|
|
|
2021-04-29 14:43:28 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
fn latest_vote(&self, pubkey: &Pubkey, is_replay_vote: bool) -> Option<&(Slot, Vec<Hash>)> {
|
|
|
|
let vote_map = if is_replay_vote {
|
|
|
|
&self.max_replay_frozen_votes
|
|
|
|
} else {
|
|
|
|
&self.max_gossip_frozen_votes
|
|
|
|
};
|
|
|
|
vote_map.get(pubkey)
|
|
|
|
}
|
2021-04-21 14:40:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
2021-04-29 14:43:28 -07:00
|
|
|
fn run_test_latest_validator_votes_for_frozen_banks_check_add_vote(is_replay_vote: bool) {
|
2021-04-21 14:40:35 -07:00
|
|
|
let mut latest_validator_votes_for_frozen_banks =
|
|
|
|
LatestValidatorVotesForFrozenBanks::default();
|
|
|
|
|
|
|
|
// Case 1: Non-frozen banks shouldn't be added
|
|
|
|
let vote_pubkey = Pubkey::new_unique();
|
|
|
|
let mut vote_slot = 1;
|
|
|
|
let frozen_hash = None;
|
|
|
|
assert_eq!(
|
|
|
|
latest_validator_votes_for_frozen_banks.check_add_vote(
|
|
|
|
vote_pubkey,
|
|
|
|
vote_slot,
|
|
|
|
frozen_hash,
|
2021-04-29 14:43:28 -07:00
|
|
|
is_replay_vote,
|
2021-04-21 14:40:35 -07:00
|
|
|
),
|
|
|
|
// Non-frozen bank isn't inserted, so should return None for
|
|
|
|
// the highest voted frozen slot
|
|
|
|
(false, None)
|
|
|
|
);
|
|
|
|
assert!(latest_validator_votes_for_frozen_banks
|
2021-04-29 14:43:28 -07:00
|
|
|
.max_replay_frozen_votes
|
|
|
|
.is_empty());
|
|
|
|
assert!(latest_validator_votes_for_frozen_banks
|
|
|
|
.max_gossip_frozen_votes
|
2021-04-21 14:40:35 -07:00
|
|
|
.is_empty());
|
|
|
|
assert!(latest_validator_votes_for_frozen_banks
|
|
|
|
.fork_choice_dirty_set
|
|
|
|
.is_empty());
|
|
|
|
|
|
|
|
// Case 2: Frozen vote should be added, but the same vote added again
|
|
|
|
// shouldn't update state
|
|
|
|
let num_repeated_iterations = 3;
|
|
|
|
let frozen_hash = Some(Hash::new_unique());
|
|
|
|
for i in 0..num_repeated_iterations {
|
|
|
|
let expected_result = if i == 0 {
|
|
|
|
(true, Some(vote_slot))
|
|
|
|
} else {
|
|
|
|
(false, Some(vote_slot))
|
|
|
|
};
|
|
|
|
assert_eq!(
|
|
|
|
latest_validator_votes_for_frozen_banks.check_add_vote(
|
|
|
|
vote_pubkey,
|
|
|
|
vote_slot,
|
|
|
|
frozen_hash,
|
2021-04-29 14:43:28 -07:00
|
|
|
is_replay_vote,
|
2021-04-21 14:40:35 -07:00
|
|
|
),
|
|
|
|
expected_result
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
*latest_validator_votes_for_frozen_banks
|
2021-04-29 14:43:28 -07:00
|
|
|
.latest_vote(&vote_pubkey, is_replay_vote)
|
2021-04-21 14:40:35 -07:00
|
|
|
.unwrap(),
|
|
|
|
(vote_slot, vec![frozen_hash.unwrap()])
|
|
|
|
);
|
2021-04-29 14:43:28 -07:00
|
|
|
if is_replay_vote {
|
|
|
|
assert_eq!(
|
|
|
|
*latest_validator_votes_for_frozen_banks
|
|
|
|
.fork_choice_dirty_set
|
|
|
|
.get(&vote_pubkey)
|
|
|
|
.unwrap(),
|
|
|
|
(vote_slot, vec![frozen_hash.unwrap()])
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
assert!(latest_validator_votes_for_frozen_banks
|
2021-04-21 14:40:35 -07:00
|
|
|
.fork_choice_dirty_set
|
|
|
|
.get(&vote_pubkey)
|
2021-04-29 14:43:28 -07:00
|
|
|
.is_none());
|
|
|
|
}
|
2021-04-21 14:40:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Case 3: Adding duplicate vote for same slot should update the state
|
|
|
|
let duplicate_frozen_hash = Some(Hash::new_unique());
|
|
|
|
let all_frozen_hashes = vec![frozen_hash.unwrap(), duplicate_frozen_hash.unwrap()];
|
|
|
|
assert_eq!(
|
|
|
|
latest_validator_votes_for_frozen_banks.check_add_vote(
|
|
|
|
vote_pubkey,
|
|
|
|
vote_slot,
|
|
|
|
duplicate_frozen_hash,
|
2021-04-29 14:43:28 -07:00
|
|
|
is_replay_vote,
|
2021-04-21 14:40:35 -07:00
|
|
|
),
|
|
|
|
(true, Some(vote_slot))
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
*latest_validator_votes_for_frozen_banks
|
2021-04-29 14:43:28 -07:00
|
|
|
.latest_vote(&vote_pubkey, is_replay_vote)
|
2021-04-21 14:40:35 -07:00
|
|
|
.unwrap(),
|
|
|
|
(vote_slot, all_frozen_hashes.clone())
|
|
|
|
);
|
2021-04-29 14:43:28 -07:00
|
|
|
if is_replay_vote {
|
|
|
|
assert_eq!(
|
|
|
|
*latest_validator_votes_for_frozen_banks
|
|
|
|
.fork_choice_dirty_set
|
|
|
|
.get(&vote_pubkey)
|
|
|
|
.unwrap(),
|
|
|
|
(vote_slot, all_frozen_hashes.clone())
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
assert!(latest_validator_votes_for_frozen_banks
|
2021-04-21 14:40:35 -07:00
|
|
|
.fork_choice_dirty_set
|
|
|
|
.get(&vote_pubkey)
|
2021-04-29 14:43:28 -07:00
|
|
|
.is_none());
|
|
|
|
}
|
2021-04-21 14:40:35 -07:00
|
|
|
|
|
|
|
// Case 4: Adding duplicate vote that is not frozen should not update the state
|
|
|
|
let frozen_hash = None;
|
|
|
|
assert_eq!(
|
|
|
|
latest_validator_votes_for_frozen_banks.check_add_vote(
|
|
|
|
vote_pubkey,
|
|
|
|
vote_slot,
|
|
|
|
frozen_hash,
|
2021-04-29 14:43:28 -07:00
|
|
|
is_replay_vote,
|
2021-04-21 14:40:35 -07:00
|
|
|
),
|
|
|
|
(false, Some(vote_slot))
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
*latest_validator_votes_for_frozen_banks
|
2021-04-29 14:43:28 -07:00
|
|
|
.latest_vote(&vote_pubkey, is_replay_vote)
|
2021-04-21 14:40:35 -07:00
|
|
|
.unwrap(),
|
|
|
|
(vote_slot, all_frozen_hashes.clone())
|
|
|
|
);
|
2021-04-29 14:43:28 -07:00
|
|
|
if is_replay_vote {
|
|
|
|
assert_eq!(
|
|
|
|
*latest_validator_votes_for_frozen_banks
|
|
|
|
.fork_choice_dirty_set
|
|
|
|
.get(&vote_pubkey)
|
|
|
|
.unwrap(),
|
|
|
|
(vote_slot, all_frozen_hashes.clone())
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
assert!(latest_validator_votes_for_frozen_banks
|
2021-04-21 14:40:35 -07:00
|
|
|
.fork_choice_dirty_set
|
|
|
|
.get(&vote_pubkey)
|
2021-04-29 14:43:28 -07:00
|
|
|
.is_none());
|
|
|
|
}
|
2021-04-21 14:40:35 -07:00
|
|
|
|
|
|
|
// Case 5: Adding a vote for a new higher slot that is not yet frozen
|
|
|
|
// should not update the state
|
|
|
|
let frozen_hash = None;
|
|
|
|
let old_vote_slot = vote_slot;
|
|
|
|
vote_slot += 1;
|
|
|
|
assert_eq!(
|
|
|
|
latest_validator_votes_for_frozen_banks.check_add_vote(
|
|
|
|
vote_pubkey,
|
|
|
|
vote_slot,
|
|
|
|
frozen_hash,
|
2021-04-29 14:43:28 -07:00
|
|
|
is_replay_vote,
|
2021-04-21 14:40:35 -07:00
|
|
|
),
|
|
|
|
(false, Some(old_vote_slot))
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
*latest_validator_votes_for_frozen_banks
|
2021-04-29 14:43:28 -07:00
|
|
|
.latest_vote(&vote_pubkey, is_replay_vote)
|
2021-04-21 14:40:35 -07:00
|
|
|
.unwrap(),
|
|
|
|
(old_vote_slot, all_frozen_hashes.clone())
|
|
|
|
);
|
2021-04-29 14:43:28 -07:00
|
|
|
if is_replay_vote {
|
|
|
|
assert_eq!(
|
|
|
|
*latest_validator_votes_for_frozen_banks
|
|
|
|
.fork_choice_dirty_set
|
|
|
|
.get(&vote_pubkey)
|
|
|
|
.unwrap(),
|
|
|
|
(old_vote_slot, all_frozen_hashes)
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
assert!(latest_validator_votes_for_frozen_banks
|
2021-04-21 14:40:35 -07:00
|
|
|
.fork_choice_dirty_set
|
|
|
|
.get(&vote_pubkey)
|
2021-04-29 14:43:28 -07:00
|
|
|
.is_none());
|
|
|
|
}
|
2021-04-21 14:40:35 -07:00
|
|
|
|
|
|
|
// Case 6: Adding a vote for a new higher slot that *is* frozen
|
|
|
|
// should upate the state
|
|
|
|
let frozen_hash = Some(Hash::new_unique());
|
|
|
|
assert_eq!(
|
|
|
|
latest_validator_votes_for_frozen_banks.check_add_vote(
|
|
|
|
vote_pubkey,
|
|
|
|
vote_slot,
|
|
|
|
frozen_hash,
|
2021-04-29 14:43:28 -07:00
|
|
|
is_replay_vote,
|
2021-04-21 14:40:35 -07:00
|
|
|
),
|
|
|
|
(true, Some(vote_slot))
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
*latest_validator_votes_for_frozen_banks
|
2021-04-29 14:43:28 -07:00
|
|
|
.latest_vote(&vote_pubkey, is_replay_vote)
|
2021-04-21 14:40:35 -07:00
|
|
|
.unwrap(),
|
|
|
|
(vote_slot, vec![frozen_hash.unwrap()])
|
|
|
|
);
|
2021-04-29 14:43:28 -07:00
|
|
|
if is_replay_vote {
|
|
|
|
assert_eq!(
|
|
|
|
*latest_validator_votes_for_frozen_banks
|
|
|
|
.fork_choice_dirty_set
|
|
|
|
.get(&vote_pubkey)
|
|
|
|
.unwrap(),
|
|
|
|
(vote_slot, vec![frozen_hash.unwrap()])
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
assert!(latest_validator_votes_for_frozen_banks
|
2021-04-21 14:40:35 -07:00
|
|
|
.fork_choice_dirty_set
|
|
|
|
.get(&vote_pubkey)
|
2021-04-29 14:43:28 -07:00
|
|
|
.is_none());
|
|
|
|
}
|
2021-04-21 14:40:35 -07:00
|
|
|
|
|
|
|
// Case 7: Adding a vote for a new pubkey should also update the state
|
|
|
|
vote_slot += 1;
|
|
|
|
let frozen_hash = Some(Hash::new_unique());
|
|
|
|
let vote_pubkey = Pubkey::new_unique();
|
|
|
|
assert_eq!(
|
|
|
|
latest_validator_votes_for_frozen_banks.check_add_vote(
|
|
|
|
vote_pubkey,
|
|
|
|
vote_slot,
|
|
|
|
frozen_hash,
|
2021-04-29 14:43:28 -07:00
|
|
|
is_replay_vote,
|
2021-04-21 14:40:35 -07:00
|
|
|
),
|
|
|
|
(true, Some(vote_slot))
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
*latest_validator_votes_for_frozen_banks
|
2021-04-29 14:43:28 -07:00
|
|
|
.latest_vote(&vote_pubkey, is_replay_vote)
|
2021-04-21 14:40:35 -07:00
|
|
|
.unwrap(),
|
|
|
|
(vote_slot, vec![frozen_hash.unwrap()])
|
|
|
|
);
|
2021-04-29 14:43:28 -07:00
|
|
|
if is_replay_vote {
|
|
|
|
assert_eq!(
|
|
|
|
*latest_validator_votes_for_frozen_banks
|
|
|
|
.fork_choice_dirty_set
|
|
|
|
.get(&vote_pubkey)
|
|
|
|
.unwrap(),
|
|
|
|
(vote_slot, vec![frozen_hash.unwrap()])
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
assert!(latest_validator_votes_for_frozen_banks
|
2021-04-21 14:40:35 -07:00
|
|
|
.fork_choice_dirty_set
|
|
|
|
.get(&vote_pubkey)
|
2021-04-29 14:43:28 -07:00
|
|
|
.is_none());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_latest_validator_votes_for_frozen_banks_check_add_vote_is_replay() {
|
|
|
|
run_test_latest_validator_votes_for_frozen_banks_check_add_vote(true)
|
2021-04-21 14:40:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-29 14:43:28 -07:00
|
|
|
fn test_latest_validator_votes_for_frozen_banks_check_add_vote_is_not_replay() {
|
|
|
|
run_test_latest_validator_votes_for_frozen_banks_check_add_vote(false)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn run_test_latest_validator_votes_for_frozen_banks_take_votes_dirty_set(is_replay: bool) {
|
2021-04-21 14:40:35 -07:00
|
|
|
let mut latest_validator_votes_for_frozen_banks =
|
|
|
|
LatestValidatorVotesForFrozenBanks::default();
|
|
|
|
let num_validators = 10;
|
|
|
|
|
|
|
|
let setup_dirty_set =
|
|
|
|
|latest_validator_votes_for_frozen_banks: &mut LatestValidatorVotesForFrozenBanks| {
|
|
|
|
(0..num_validators)
|
|
|
|
.flat_map(|vote_slot| {
|
|
|
|
let vote_pubkey = Pubkey::new_unique();
|
|
|
|
let frozen_hash1 = Hash::new_unique();
|
|
|
|
assert_eq!(
|
|
|
|
latest_validator_votes_for_frozen_banks.check_add_vote(
|
|
|
|
vote_pubkey,
|
|
|
|
vote_slot,
|
|
|
|
Some(frozen_hash1),
|
2021-04-29 14:43:28 -07:00
|
|
|
is_replay
|
2021-04-21 14:40:35 -07:00
|
|
|
),
|
|
|
|
// This vote slot was frozen, and is the highest slot inserted thus far,
|
|
|
|
// so the highest vote should be Some(vote_slot)
|
|
|
|
(true, Some(vote_slot))
|
|
|
|
);
|
|
|
|
// Add a duplicate
|
|
|
|
let frozen_hash2 = Hash::new_unique();
|
|
|
|
assert_eq!(
|
|
|
|
latest_validator_votes_for_frozen_banks.check_add_vote(
|
|
|
|
vote_pubkey,
|
|
|
|
vote_slot,
|
|
|
|
Some(frozen_hash2),
|
2021-04-29 14:43:28 -07:00
|
|
|
is_replay
|
2021-04-21 14:40:35 -07:00
|
|
|
),
|
|
|
|
// This vote slot was frozen, and is for a duplicate version of the highest slot
|
|
|
|
// inserted thus far, so the highest vote should be Some(vote_slot).
|
|
|
|
(true, Some(vote_slot))
|
|
|
|
);
|
2021-04-29 14:43:28 -07:00
|
|
|
if is_replay {
|
|
|
|
// Only replayed vote should modify the dirty set, which is used for fork fork choice.
|
|
|
|
vec![
|
|
|
|
(vote_pubkey, (vote_slot, frozen_hash1)),
|
|
|
|
(vote_pubkey, (vote_slot, frozen_hash2)),
|
|
|
|
]
|
|
|
|
} else {
|
|
|
|
vec![]
|
|
|
|
}
|
2021-04-21 14:40:35 -07:00
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
};
|
|
|
|
|
|
|
|
// Taking all the dirty votes >= 0 will return everything
|
|
|
|
let root = 0;
|
|
|
|
let mut expected_dirty_set: Vec<(Pubkey, SlotHashKey)> =
|
|
|
|
setup_dirty_set(&mut latest_validator_votes_for_frozen_banks);
|
|
|
|
let mut votes_dirty_set_output =
|
|
|
|
latest_validator_votes_for_frozen_banks.take_votes_dirty_set(root);
|
|
|
|
votes_dirty_set_output.sort();
|
|
|
|
expected_dirty_set.sort();
|
|
|
|
assert_eq!(votes_dirty_set_output, expected_dirty_set);
|
|
|
|
assert!(latest_validator_votes_for_frozen_banks
|
|
|
|
.take_votes_dirty_set(0)
|
|
|
|
.is_empty());
|
|
|
|
|
2021-04-29 14:43:28 -07:00
|
|
|
// Taking all the dirty votes >= num_validators - 1 will only return the last vote
|
2021-04-21 14:40:35 -07:00
|
|
|
let root = num_validators - 1;
|
|
|
|
let dirty_set = setup_dirty_set(&mut latest_validator_votes_for_frozen_banks);
|
|
|
|
let mut expected_dirty_set: Vec<(Pubkey, SlotHashKey)> =
|
2021-04-29 14:43:28 -07:00
|
|
|
// dirty_set could be empty if `is_replay == false`, so use saturating_sub
|
|
|
|
dirty_set[dirty_set.len().saturating_sub(2)..dirty_set.len()].to_vec();
|
2021-04-21 14:40:35 -07:00
|
|
|
let mut votes_dirty_set_output =
|
|
|
|
latest_validator_votes_for_frozen_banks.take_votes_dirty_set(root);
|
|
|
|
votes_dirty_set_output.sort();
|
|
|
|
expected_dirty_set.sort();
|
|
|
|
assert_eq!(votes_dirty_set_output, expected_dirty_set);
|
|
|
|
assert!(latest_validator_votes_for_frozen_banks
|
|
|
|
.take_votes_dirty_set(0)
|
|
|
|
.is_empty());
|
|
|
|
}
|
2021-04-29 14:43:28 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_latest_validator_votes_for_frozen_banks_take_votes_dirty_set_is_replay() {
|
|
|
|
run_test_latest_validator_votes_for_frozen_banks_take_votes_dirty_set(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_latest_validator_votes_for_frozen_banks_take_votes_dirty_set_is_not_replay() {
|
|
|
|
run_test_latest_validator_votes_for_frozen_banks_take_votes_dirty_set(false)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_latest_validator_votes_for_frozen_banks_add_replay_and_gossip_vote() {
|
|
|
|
let mut latest_validator_votes_for_frozen_banks =
|
|
|
|
LatestValidatorVotesForFrozenBanks::default();
|
|
|
|
|
|
|
|
// First simulate vote from gossip
|
|
|
|
let vote_pubkey = Pubkey::new_unique();
|
|
|
|
let vote_slot = 1;
|
|
|
|
let frozen_hash = Hash::new_unique();
|
|
|
|
let mut is_replay_vote = false;
|
|
|
|
assert_eq!(
|
|
|
|
latest_validator_votes_for_frozen_banks.check_add_vote(
|
|
|
|
vote_pubkey,
|
|
|
|
vote_slot,
|
|
|
|
Some(frozen_hash),
|
|
|
|
is_replay_vote,
|
|
|
|
),
|
|
|
|
(true, Some(vote_slot))
|
|
|
|
);
|
|
|
|
|
|
|
|
// Should find the vote in the gossip votes
|
|
|
|
assert_eq!(
|
|
|
|
*latest_validator_votes_for_frozen_banks
|
|
|
|
.latest_vote(&vote_pubkey, is_replay_vote)
|
|
|
|
.unwrap(),
|
|
|
|
(vote_slot, vec![frozen_hash])
|
|
|
|
);
|
|
|
|
// Shouldn't find the vote in the replayed votes
|
|
|
|
assert!(latest_validator_votes_for_frozen_banks
|
|
|
|
.latest_vote(&vote_pubkey, !is_replay_vote)
|
|
|
|
.is_none());
|
|
|
|
assert!(latest_validator_votes_for_frozen_banks
|
|
|
|
.take_votes_dirty_set(0)
|
|
|
|
.is_empty());
|
|
|
|
|
|
|
|
// Next simulate vote from replay
|
|
|
|
is_replay_vote = true;
|
|
|
|
assert_eq!(
|
|
|
|
latest_validator_votes_for_frozen_banks.check_add_vote(
|
|
|
|
vote_pubkey,
|
|
|
|
vote_slot,
|
|
|
|
Some(frozen_hash),
|
|
|
|
is_replay_vote,
|
|
|
|
),
|
|
|
|
(true, Some(vote_slot))
|
|
|
|
);
|
|
|
|
// Should find the vote in the gossip and replay votes
|
|
|
|
assert_eq!(
|
|
|
|
*latest_validator_votes_for_frozen_banks
|
|
|
|
.latest_vote(&vote_pubkey, is_replay_vote)
|
|
|
|
.unwrap(),
|
|
|
|
(vote_slot, vec![frozen_hash])
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
*latest_validator_votes_for_frozen_banks
|
|
|
|
.latest_vote(&vote_pubkey, !is_replay_vote)
|
|
|
|
.unwrap(),
|
|
|
|
(vote_slot, vec![frozen_hash])
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
latest_validator_votes_for_frozen_banks.take_votes_dirty_set(0),
|
|
|
|
vec![(vote_pubkey, (vote_slot, frozen_hash))]
|
|
|
|
);
|
|
|
|
}
|
2021-04-21 14:40:35 -07:00
|
|
|
}
|