Distinguish max replayed and max observed vote (#16936)

This commit is contained in:
carllin 2021-04-29 14:43:28 -07:00 committed by GitHub
parent 94edd6140c
commit 5981399612
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 243 additions and 146 deletions

View File

@ -295,6 +295,7 @@ impl Tower {
key, key,
last_landed_voted_slot, last_landed_voted_slot,
get_frozen_hash(last_landed_voted_slot), get_frozen_hash(last_landed_voted_slot),
true,
); );
} }

View File

@ -5,7 +5,8 @@ use std::collections::{hash_map::Entry, HashMap};
#[derive(Default)] #[derive(Default)]
pub(crate) struct LatestValidatorVotesForFrozenBanks { pub(crate) struct LatestValidatorVotesForFrozenBanks {
// TODO: Clean outdated/unstaked pubkeys from this list. // TODO: Clean outdated/unstaked pubkeys from this list.
max_frozen_votes: HashMap<Pubkey, (Slot, Vec<Hash>)>, max_gossip_frozen_votes: HashMap<Pubkey, (Slot, Vec<Hash>)>,
max_replay_frozen_votes: HashMap<Pubkey, (Slot, Vec<Hash>)>,
// Pubkeys that had their `max_frozen_votes` updated since the last // Pubkeys that had their `max_frozen_votes` updated since the last
// fork choice update // fork choice update
fork_choice_dirty_set: HashMap<Pubkey, (Slot, Vec<Hash>)>, fork_choice_dirty_set: HashMap<Pubkey, (Slot, Vec<Hash>)>,
@ -19,26 +20,42 @@ impl LatestValidatorVotesForFrozenBanks {
vote_pubkey: Pubkey, vote_pubkey: Pubkey,
vote_slot: Slot, vote_slot: Slot,
frozen_hash: Option<Hash>, frozen_hash: Option<Hash>,
is_replay_vote: bool,
) -> (bool, Option<Slot>) { ) -> (bool, Option<Slot>) {
let max_frozen_votes_entry = self.max_frozen_votes.entry(vote_pubkey); 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);
if let Some(frozen_hash) = frozen_hash { if let Some(frozen_hash) = frozen_hash {
match max_frozen_votes_entry { match pubkey_max_frozen_votes {
Entry::Occupied(mut occupied_entry) => { Entry::Occupied(mut occupied_entry) => {
let (latest_frozen_vote_slot, latest_frozen_vote_hashes) = let (latest_frozen_vote_slot, latest_frozen_vote_hashes) =
occupied_entry.get_mut(); occupied_entry.get_mut();
if vote_slot > *latest_frozen_vote_slot { if vote_slot > *latest_frozen_vote_slot {
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 self.fork_choice_dirty_set
.insert(vote_pubkey, (vote_slot, vec![frozen_hash])); .insert(vote_pubkey, (vote_slot, vec![frozen_hash]));
}
*latest_frozen_vote_slot = vote_slot; *latest_frozen_vote_slot = vote_slot;
*latest_frozen_vote_hashes = vec![frozen_hash]; *latest_frozen_vote_hashes = vec![frozen_hash];
return (true, Some(vote_slot)); return (true, Some(vote_slot));
} else if vote_slot == *latest_frozen_vote_slot } else if vote_slot == *latest_frozen_vote_slot
&& !latest_frozen_vote_hashes.contains(&frozen_hash) && !latest_frozen_vote_hashes.contains(&frozen_hash)
{ {
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) = let (_, dirty_frozen_hashes) =
self.fork_choice_dirty_set.entry(vote_pubkey).or_default(); self.fork_choice_dirty_set.entry(vote_pubkey).or_default();
assert!(!dirty_frozen_hashes.contains(&frozen_hash)); assert!(!dirty_frozen_hashes.contains(&frozen_hash));
dirty_frozen_hashes.push(frozen_hash); dirty_frozen_hashes.push(frozen_hash);
}
latest_frozen_vote_hashes.push(frozen_hash); latest_frozen_vote_hashes.push(frozen_hash);
return (true, Some(vote_slot)); return (true, Some(vote_slot));
} else { } else {
@ -49,8 +66,10 @@ impl LatestValidatorVotesForFrozenBanks {
Entry::Vacant(vacant_entry) => { Entry::Vacant(vacant_entry) => {
vacant_entry.insert((vote_slot, vec![frozen_hash])); vacant_entry.insert((vote_slot, vec![frozen_hash]));
if is_replay_vote {
self.fork_choice_dirty_set self.fork_choice_dirty_set
.insert(vote_pubkey, (vote_slot, vec![frozen_hash])); .insert(vote_pubkey, (vote_slot, vec![frozen_hash]));
}
return (true, Some(vote_slot)); return (true, Some(vote_slot));
} }
} }
@ -60,7 +79,7 @@ impl LatestValidatorVotesForFrozenBanks {
// struct // struct
( (
false, false,
match max_frozen_votes_entry { match pubkey_max_frozen_votes {
Entry::Occupied(occupied_entry) => Some(occupied_entry.get().0), Entry::Occupied(occupied_entry) => Some(occupied_entry.get().0),
Entry::Vacant(_) => None, Entry::Vacant(_) => None,
}, },
@ -80,14 +99,23 @@ impl LatestValidatorVotesForFrozenBanks {
}) })
.collect() .collect()
} }
#[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)
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[test] fn run_test_latest_validator_votes_for_frozen_banks_check_add_vote(is_replay_vote: bool) {
fn test_latest_validator_votes_for_frozen_banks_check_add_vote() {
let mut latest_validator_votes_for_frozen_banks = let mut latest_validator_votes_for_frozen_banks =
LatestValidatorVotesForFrozenBanks::default(); LatestValidatorVotesForFrozenBanks::default();
@ -100,13 +128,17 @@ mod tests {
vote_pubkey, vote_pubkey,
vote_slot, vote_slot,
frozen_hash, frozen_hash,
is_replay_vote,
), ),
// Non-frozen bank isn't inserted, so should return None for // Non-frozen bank isn't inserted, so should return None for
// the highest voted frozen slot // the highest voted frozen slot
(false, None) (false, None)
); );
assert!(latest_validator_votes_for_frozen_banks assert!(latest_validator_votes_for_frozen_banks
.max_frozen_votes .max_replay_frozen_votes
.is_empty());
assert!(latest_validator_votes_for_frozen_banks
.max_gossip_frozen_votes
.is_empty()); .is_empty());
assert!(latest_validator_votes_for_frozen_banks assert!(latest_validator_votes_for_frozen_banks
.fork_choice_dirty_set .fork_choice_dirty_set
@ -127,16 +159,17 @@ mod tests {
vote_pubkey, vote_pubkey,
vote_slot, vote_slot,
frozen_hash, frozen_hash,
is_replay_vote,
), ),
expected_result expected_result
); );
assert_eq!( assert_eq!(
*latest_validator_votes_for_frozen_banks *latest_validator_votes_for_frozen_banks
.max_frozen_votes .latest_vote(&vote_pubkey, is_replay_vote)
.get(&vote_pubkey)
.unwrap(), .unwrap(),
(vote_slot, vec![frozen_hash.unwrap()]) (vote_slot, vec![frozen_hash.unwrap()])
); );
if is_replay_vote {
assert_eq!( assert_eq!(
*latest_validator_votes_for_frozen_banks *latest_validator_votes_for_frozen_banks
.fork_choice_dirty_set .fork_choice_dirty_set
@ -144,6 +177,12 @@ mod tests {
.unwrap(), .unwrap(),
(vote_slot, vec![frozen_hash.unwrap()]) (vote_slot, vec![frozen_hash.unwrap()])
); );
} else {
assert!(latest_validator_votes_for_frozen_banks
.fork_choice_dirty_set
.get(&vote_pubkey)
.is_none());
}
} }
// Case 3: Adding duplicate vote for same slot should update the state // Case 3: Adding duplicate vote for same slot should update the state
@ -154,16 +193,17 @@ mod tests {
vote_pubkey, vote_pubkey,
vote_slot, vote_slot,
duplicate_frozen_hash, duplicate_frozen_hash,
is_replay_vote,
), ),
(true, Some(vote_slot)) (true, Some(vote_slot))
); );
assert_eq!( assert_eq!(
*latest_validator_votes_for_frozen_banks *latest_validator_votes_for_frozen_banks
.max_frozen_votes .latest_vote(&vote_pubkey, is_replay_vote)
.get(&vote_pubkey)
.unwrap(), .unwrap(),
(vote_slot, all_frozen_hashes.clone()) (vote_slot, all_frozen_hashes.clone())
); );
if is_replay_vote {
assert_eq!( assert_eq!(
*latest_validator_votes_for_frozen_banks *latest_validator_votes_for_frozen_banks
.fork_choice_dirty_set .fork_choice_dirty_set
@ -171,6 +211,12 @@ mod tests {
.unwrap(), .unwrap(),
(vote_slot, all_frozen_hashes.clone()) (vote_slot, all_frozen_hashes.clone())
); );
} else {
assert!(latest_validator_votes_for_frozen_banks
.fork_choice_dirty_set
.get(&vote_pubkey)
.is_none());
}
// Case 4: Adding duplicate vote that is not frozen should not update the state // Case 4: Adding duplicate vote that is not frozen should not update the state
let frozen_hash = None; let frozen_hash = None;
@ -179,16 +225,17 @@ mod tests {
vote_pubkey, vote_pubkey,
vote_slot, vote_slot,
frozen_hash, frozen_hash,
is_replay_vote,
), ),
(false, Some(vote_slot)) (false, Some(vote_slot))
); );
assert_eq!( assert_eq!(
*latest_validator_votes_for_frozen_banks *latest_validator_votes_for_frozen_banks
.max_frozen_votes .latest_vote(&vote_pubkey, is_replay_vote)
.get(&vote_pubkey)
.unwrap(), .unwrap(),
(vote_slot, all_frozen_hashes.clone()) (vote_slot, all_frozen_hashes.clone())
); );
if is_replay_vote {
assert_eq!( assert_eq!(
*latest_validator_votes_for_frozen_banks *latest_validator_votes_for_frozen_banks
.fork_choice_dirty_set .fork_choice_dirty_set
@ -196,6 +243,12 @@ mod tests {
.unwrap(), .unwrap(),
(vote_slot, all_frozen_hashes.clone()) (vote_slot, all_frozen_hashes.clone())
); );
} else {
assert!(latest_validator_votes_for_frozen_banks
.fork_choice_dirty_set
.get(&vote_pubkey)
.is_none());
}
// Case 5: Adding a vote for a new higher slot that is not yet frozen // Case 5: Adding a vote for a new higher slot that is not yet frozen
// should not update the state // should not update the state
@ -207,16 +260,17 @@ mod tests {
vote_pubkey, vote_pubkey,
vote_slot, vote_slot,
frozen_hash, frozen_hash,
is_replay_vote,
), ),
(false, Some(old_vote_slot)) (false, Some(old_vote_slot))
); );
assert_eq!( assert_eq!(
*latest_validator_votes_for_frozen_banks *latest_validator_votes_for_frozen_banks
.max_frozen_votes .latest_vote(&vote_pubkey, is_replay_vote)
.get(&vote_pubkey)
.unwrap(), .unwrap(),
(old_vote_slot, all_frozen_hashes.clone()) (old_vote_slot, all_frozen_hashes.clone())
); );
if is_replay_vote {
assert_eq!( assert_eq!(
*latest_validator_votes_for_frozen_banks *latest_validator_votes_for_frozen_banks
.fork_choice_dirty_set .fork_choice_dirty_set
@ -224,6 +278,12 @@ mod tests {
.unwrap(), .unwrap(),
(old_vote_slot, all_frozen_hashes) (old_vote_slot, all_frozen_hashes)
); );
} else {
assert!(latest_validator_votes_for_frozen_banks
.fork_choice_dirty_set
.get(&vote_pubkey)
.is_none());
}
// Case 6: Adding a vote for a new higher slot that *is* frozen // Case 6: Adding a vote for a new higher slot that *is* frozen
// should upate the state // should upate the state
@ -233,16 +293,17 @@ mod tests {
vote_pubkey, vote_pubkey,
vote_slot, vote_slot,
frozen_hash, frozen_hash,
is_replay_vote,
), ),
(true, Some(vote_slot)) (true, Some(vote_slot))
); );
assert_eq!( assert_eq!(
*latest_validator_votes_for_frozen_banks *latest_validator_votes_for_frozen_banks
.max_frozen_votes .latest_vote(&vote_pubkey, is_replay_vote)
.get(&vote_pubkey)
.unwrap(), .unwrap(),
(vote_slot, vec![frozen_hash.unwrap()]) (vote_slot, vec![frozen_hash.unwrap()])
); );
if is_replay_vote {
assert_eq!( assert_eq!(
*latest_validator_votes_for_frozen_banks *latest_validator_votes_for_frozen_banks
.fork_choice_dirty_set .fork_choice_dirty_set
@ -250,6 +311,12 @@ mod tests {
.unwrap(), .unwrap(),
(vote_slot, vec![frozen_hash.unwrap()]) (vote_slot, vec![frozen_hash.unwrap()])
); );
} else {
assert!(latest_validator_votes_for_frozen_banks
.fork_choice_dirty_set
.get(&vote_pubkey)
.is_none());
}
// Case 7: Adding a vote for a new pubkey should also update the state // Case 7: Adding a vote for a new pubkey should also update the state
vote_slot += 1; vote_slot += 1;
@ -260,16 +327,17 @@ mod tests {
vote_pubkey, vote_pubkey,
vote_slot, vote_slot,
frozen_hash, frozen_hash,
is_replay_vote,
), ),
(true, Some(vote_slot)) (true, Some(vote_slot))
); );
assert_eq!( assert_eq!(
*latest_validator_votes_for_frozen_banks *latest_validator_votes_for_frozen_banks
.max_frozen_votes .latest_vote(&vote_pubkey, is_replay_vote)
.get(&vote_pubkey)
.unwrap(), .unwrap(),
(vote_slot, vec![frozen_hash.unwrap()]) (vote_slot, vec![frozen_hash.unwrap()])
); );
if is_replay_vote {
assert_eq!( assert_eq!(
*latest_validator_votes_for_frozen_banks *latest_validator_votes_for_frozen_banks
.fork_choice_dirty_set .fork_choice_dirty_set
@ -277,10 +345,25 @@ mod tests {
.unwrap(), .unwrap(),
(vote_slot, vec![frozen_hash.unwrap()]) (vote_slot, vec![frozen_hash.unwrap()])
); );
} else {
assert!(latest_validator_votes_for_frozen_banks
.fork_choice_dirty_set
.get(&vote_pubkey)
.is_none());
}
} }
#[test] #[test]
fn test_latest_validator_votes_for_frozen_banks_take_votes_dirty_set() { 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)
}
#[test]
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) {
let mut latest_validator_votes_for_frozen_banks = let mut latest_validator_votes_for_frozen_banks =
LatestValidatorVotesForFrozenBanks::default(); LatestValidatorVotesForFrozenBanks::default();
let num_validators = 10; let num_validators = 10;
@ -296,6 +379,7 @@ mod tests {
vote_pubkey, vote_pubkey,
vote_slot, vote_slot,
Some(frozen_hash1), Some(frozen_hash1),
is_replay
), ),
// This vote slot was frozen, and is the highest slot inserted thus far, // This vote slot was frozen, and is the highest slot inserted thus far,
// so the highest vote should be Some(vote_slot) // so the highest vote should be Some(vote_slot)
@ -308,15 +392,21 @@ mod tests {
vote_pubkey, vote_pubkey,
vote_slot, vote_slot,
Some(frozen_hash2), Some(frozen_hash2),
is_replay
), ),
// This vote slot was frozen, and is for a duplicate version of the highest slot // 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). // inserted thus far, so the highest vote should be Some(vote_slot).
(true, Some(vote_slot)) (true, Some(vote_slot))
); );
if is_replay {
// Only replayed vote should modify the dirty set, which is used for fork fork choice.
vec![ vec![
(vote_pubkey, (vote_slot, frozen_hash1)), (vote_pubkey, (vote_slot, frozen_hash1)),
(vote_pubkey, (vote_slot, frozen_hash2)), (vote_pubkey, (vote_slot, frozen_hash2)),
] ]
} else {
vec![]
}
}) })
.collect() .collect()
}; };
@ -334,11 +424,12 @@ mod tests {
.take_votes_dirty_set(0) .take_votes_dirty_set(0)
.is_empty()); .is_empty());
// Taking all the firty votes >= num_validators - 1 will only return the last vote // Taking all the dirty votes >= num_validators - 1 will only return the last vote
let root = num_validators - 1; let root = num_validators - 1;
let dirty_set = setup_dirty_set(&mut latest_validator_votes_for_frozen_banks); let dirty_set = setup_dirty_set(&mut latest_validator_votes_for_frozen_banks);
let mut expected_dirty_set: Vec<(Pubkey, SlotHashKey)> = let mut expected_dirty_set: Vec<(Pubkey, SlotHashKey)> =
dirty_set[dirty_set.len() - 2..dirty_set.len()].to_vec(); // 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();
let mut votes_dirty_set_output = let mut votes_dirty_set_output =
latest_validator_votes_for_frozen_banks.take_votes_dirty_set(root); latest_validator_votes_for_frozen_banks.take_votes_dirty_set(root);
votes_dirty_set_output.sort(); votes_dirty_set_output.sort();
@ -348,4 +439,79 @@ mod tests {
.take_votes_dirty_set(0) .take_votes_dirty_set(0)
.is_empty()); .is_empty());
} }
#[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))]
);
}
} }

View File

@ -420,12 +420,12 @@ impl ReplayStage {
// included in a block, so we may not have yet observed these votes just // included in a block, so we may not have yet observed these votes just
// by replaying blocks. // by replaying blocks.
let mut process_unfrozen_gossip_verified_vote_hashes_time = Measure::start("process_gossip_duplicate_confirmed_slots"); let mut process_unfrozen_gossip_verified_vote_hashes_time = Measure::start("process_gossip_duplicate_confirmed_slots");
/*Self::process_gossip_verified_vote_hashes( Self::process_gossip_verified_vote_hashes(
&gossip_verified_vote_hash_receiver, &gossip_verified_vote_hash_receiver,
&mut unfrozen_gossip_verified_vote_hashes, &mut unfrozen_gossip_verified_vote_hashes,
&heaviest_subtree_fork_choice, &heaviest_subtree_fork_choice,
&mut latest_validator_votes_for_frozen_banks, &mut latest_validator_votes_for_frozen_banks,
);*/ );
for _ in gossip_verified_vote_hash_receiver.try_iter() {} for _ in gossip_verified_vote_hash_receiver.try_iter() {}
process_unfrozen_gossip_verified_vote_hashes_time.stop(); process_unfrozen_gossip_verified_vote_hashes_time.stop();
@ -934,7 +934,6 @@ impl ReplayStage {
} }
} }
#[cfg(test)]
fn process_gossip_verified_vote_hashes( fn process_gossip_verified_vote_hashes(
gossip_verified_vote_hash_receiver: &GossipVerifiedVoteHashReceiver, gossip_verified_vote_hash_receiver: &GossipVerifiedVoteHashReceiver,
unfrozen_gossip_verified_vote_hashes: &mut UnfrozenGossipVerifiedVoteHashes, unfrozen_gossip_verified_vote_hashes: &mut UnfrozenGossipVerifiedVoteHashes,
@ -1765,6 +1764,7 @@ impl ReplayStage {
pubkey, pubkey,
bank.slot(), bank.slot(),
Some(bank_hash), Some(bank_hash),
false,
); );
} }
} }
@ -4700,12 +4700,11 @@ pub(crate) mod tests {
} }
#[test] #[test]
fn test_gossip_vote_for_unrooted_slot() { fn test_gossip_vote_doesnt_affect_fork_choice() {
let VoteSimulator { let VoteSimulator {
bank_forks, bank_forks,
mut heaviest_subtree_fork_choice, mut heaviest_subtree_fork_choice,
mut latest_validator_votes_for_frozen_banks, mut latest_validator_votes_for_frozen_banks,
mut progress,
vote_pubkeys, vote_pubkeys,
.. ..
} = setup_forks(); } = setup_forks();
@ -4714,6 +4713,9 @@ pub(crate) mod tests {
let mut unfrozen_gossip_verified_vote_hashes = UnfrozenGossipVerifiedVoteHashes::default(); let mut unfrozen_gossip_verified_vote_hashes = UnfrozenGossipVerifiedVoteHashes::default();
let (gossip_verified_vote_hash_sender, gossip_verified_vote_hash_receiver) = unbounded(); let (gossip_verified_vote_hash_sender, gossip_verified_vote_hash_receiver) = unbounded();
// Best slot is 4
assert_eq!(heaviest_subtree_fork_choice.best_overall_slot().0, 4);
// Cast a vote for slot 3 on one fork // Cast a vote for slot 3 on one fork
let vote_slot = 3; let vote_slot = 3;
let vote_bank = bank_forks.read().unwrap().get(vote_slot).unwrap().clone(); let vote_bank = bank_forks.read().unwrap().get(vote_slot).unwrap().clone();
@ -4727,87 +4729,15 @@ pub(crate) mod tests {
&mut latest_validator_votes_for_frozen_banks, &mut latest_validator_votes_for_frozen_banks,
); );
// Pick the best fork // Pick the best fork. Gossip votes shouldn't affect fork choice
heaviest_subtree_fork_choice.compute_bank_stats( heaviest_subtree_fork_choice.compute_bank_stats(
&vote_bank, &vote_bank,
&Tower::default(), &Tower::default(),
&mut latest_validator_votes_for_frozen_banks, &mut latest_validator_votes_for_frozen_banks,
); );
assert_eq!(heaviest_subtree_fork_choice.best_overall_slot().0, 6);
// Now send another vote for a frozen bank on the other fork, where the new vote // Best slot is still 4
// is bigger than the last vote assert_eq!(heaviest_subtree_fork_choice.best_overall_slot().0, 4);
let bigger_vote_slot = 4;
let bigger_vote_bank = bank_forks
.read()
.unwrap()
.get(bigger_vote_slot)
.unwrap()
.clone();
assert!(heaviest_subtree_fork_choice
.contains_block(&(bigger_vote_slot, bigger_vote_bank.hash())));
gossip_verified_vote_hash_sender
.send((vote_pubkey, bigger_vote_slot, bigger_vote_bank.hash()))
.expect("Send should succeed");
ReplayStage::process_gossip_verified_vote_hashes(
&gossip_verified_vote_hash_receiver,
&mut unfrozen_gossip_verified_vote_hashes,
&heaviest_subtree_fork_choice,
&mut latest_validator_votes_for_frozen_banks,
);
// Now set a root for a slot on the previously voted fork thats smaller than the new vote
let new_root = 3;
ReplayStage::handle_new_root(
new_root,
&bank_forks,
&mut progress,
&AbsRequestSender::default(),
None,
&mut heaviest_subtree_fork_choice,
&mut GossipDuplicateConfirmedSlots::default(),
&mut unfrozen_gossip_verified_vote_hashes,
&mut true,
&mut vec![],
);
// Add a new bank, freeze it
let parent_bank = bank_forks.read().unwrap().get(6).unwrap().clone();
let new_bank = Bank::new_from_parent(&parent_bank, &Pubkey::default(), 7);
bank_forks.write().unwrap().insert(new_bank);
let new_bank = bank_forks.read().unwrap().get(7).unwrap().clone();
new_bank.freeze();
heaviest_subtree_fork_choice.add_new_leaf_slot(
(new_bank.slot(), new_bank.hash()),
Some((parent_bank.slot(), parent_bank.hash())),
);
// Compute bank stats on new slot
heaviest_subtree_fork_choice.compute_bank_stats(
&new_bank,
&Tower::default(),
&mut latest_validator_votes_for_frozen_banks,
);
// Even though the `bigger_vote_slot` no longer exists in the fork choice tree,
// this vote should remove the previous vote's weight because we know there
// was a later vote
let old_vote_node = (vote_slot, vote_bank.hash());
assert_eq!(
heaviest_subtree_fork_choice
.stake_voted_at(&old_vote_node)
.unwrap(),
0
);
assert_eq!(
heaviest_subtree_fork_choice
.stake_voted_subtree(&old_vote_node)
.unwrap(),
0
);
assert_eq!(
heaviest_subtree_fork_choice.best_overall_slot(),
(new_bank.slot(), new_bank.hash())
);
} }
#[test] #[test]

View File

@ -21,8 +21,8 @@ impl UnfrozenGossipVerifiedVoteHashes {
) { ) {
// If this is a frozen bank, then we need to update the `latest_validator_votes_for_frozen_banks` // If this is a frozen bank, then we need to update the `latest_validator_votes_for_frozen_banks`
let frozen_hash = if is_frozen { Some(hash) } else { None }; let frozen_hash = if is_frozen { Some(hash) } else { None };
let (was_added, latest_frozen_vote_slot) = let (was_added, latest_frozen_vote_slot) = latest_validator_votes_for_frozen_banks
latest_validator_votes_for_frozen_banks.check_add_vote(pubkey, vote_slot, frozen_hash); .check_add_vote(pubkey, vote_slot, frozen_hash, false);
if !was_added if !was_added
&& latest_frozen_vote_slot && latest_frozen_vote_slot