Account for duplicate before a bank is frozen or replayed (#17866)
This commit is contained in:
parent
191519188d
commit
afafa624a3
|
@ -3,8 +3,9 @@ use crate::{
|
||||||
progress_map::ProgressMap,
|
progress_map::ProgressMap,
|
||||||
};
|
};
|
||||||
use solana_sdk::{clock::Slot, hash::Hash};
|
use solana_sdk::{clock::Slot, hash::Hash};
|
||||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||||
|
|
||||||
|
pub(crate) type DuplicateSlotsTracker = BTreeSet<Slot>;
|
||||||
pub(crate) type GossipDuplicateConfirmedSlots = BTreeMap<Slot, Hash>;
|
pub(crate) type GossipDuplicateConfirmedSlots = BTreeMap<Slot, Hash>;
|
||||||
type SlotStateHandler = fn(Slot, &Hash, Option<&Hash>, bool, bool) -> Vec<ResultingStateChange>;
|
type SlotStateHandler = fn(Slot, &Hash, Option<&Hash>, bool, bool) -> Vec<ResultingStateChange>;
|
||||||
|
|
||||||
|
@ -234,10 +235,12 @@ fn apply_state_changes(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub(crate) fn check_slot_agrees_with_cluster(
|
pub(crate) fn check_slot_agrees_with_cluster(
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
root: Slot,
|
root: Slot,
|
||||||
frozen_hash: Option<Hash>,
|
frozen_hash: Option<Hash>,
|
||||||
|
duplicate_slots_tracker: &mut DuplicateSlotsTracker,
|
||||||
gossip_duplicate_confirmed_slots: &GossipDuplicateConfirmedSlots,
|
gossip_duplicate_confirmed_slots: &GossipDuplicateConfirmedSlots,
|
||||||
ancestors: &HashMap<Slot, HashSet<Slot>>,
|
ancestors: &HashMap<Slot, HashSet<Slot>>,
|
||||||
descendants: &HashMap<Slot, HashSet<Slot>>,
|
descendants: &HashMap<Slot, HashSet<Slot>>,
|
||||||
|
@ -258,6 +261,15 @@ pub(crate) fn check_slot_agrees_with_cluster(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Needs to happen before the frozen_hash.is_none() check below to account for duplicate
|
||||||
|
// signals arriving before the bank is constructed in replay.
|
||||||
|
if matches!(slot_state_update, SlotStateUpdate::Duplicate) {
|
||||||
|
// If this slot has already been processed before, return
|
||||||
|
if !duplicate_slots_tracker.insert(slot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if frozen_hash.is_none() {
|
if frozen_hash.is_none() {
|
||||||
// If the bank doesn't even exist in BankForks yet,
|
// If the bank doesn't even exist in BankForks yet,
|
||||||
// then there's nothing to do as replay of the slot
|
// then there's nothing to do as replay of the slot
|
||||||
|
@ -275,18 +287,7 @@ pub(crate) fn check_slot_agrees_with_cluster(
|
||||||
&frozen_hash,
|
&frozen_hash,
|
||||||
is_local_replay_duplicate_confirmed,
|
is_local_replay_duplicate_confirmed,
|
||||||
);
|
);
|
||||||
let mut is_slot_duplicate =
|
let is_slot_duplicate = duplicate_slots_tracker.contains(&slot);
|
||||||
progress.is_unconfirmed_duplicate(slot).expect("If the frozen hash exists, then the slot must exist in bank forks and thus in progress map");
|
|
||||||
if matches!(slot_state_update, SlotStateUpdate::Duplicate) {
|
|
||||||
if is_slot_duplicate {
|
|
||||||
// Already processed duplicate signal for this slot, no need to continue
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
// Otherwise, mark the slot as duplicate so the appropriate state changes
|
|
||||||
// will trigger
|
|
||||||
is_slot_duplicate = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let is_dead = progress.is_dead(slot).expect("If the frozen hash exists, then the slot must exist in bank forks and thus in progress map");
|
let is_dead = progress.is_dead(slot).expect("If the frozen hash exists, then the slot must exist in bank forks and thus in progress map");
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
|
@ -687,6 +688,84 @@ mod test {
|
||||||
.unwrap());
|
.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_test_state_duplicate_then_bank_frozen(initial_bank_hash: Option<Hash>) {
|
||||||
|
// Common state
|
||||||
|
let InitialState {
|
||||||
|
mut heaviest_subtree_fork_choice,
|
||||||
|
mut progress,
|
||||||
|
ancestors,
|
||||||
|
descendants,
|
||||||
|
bank_forks,
|
||||||
|
..
|
||||||
|
} = setup();
|
||||||
|
|
||||||
|
// Setup a duplicate slot state transition with the initial bank state of the duplicate slot
|
||||||
|
// determined by `initial_bank_hash`, which can be:
|
||||||
|
// 1) A default hash (unfrozen bank),
|
||||||
|
// 2) None (a slot that hasn't even started replay yet).
|
||||||
|
let root = 0;
|
||||||
|
let mut duplicate_slots_tracker = DuplicateSlotsTracker::default();
|
||||||
|
let gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default();
|
||||||
|
let duplicate_slot = 2;
|
||||||
|
check_slot_agrees_with_cluster(
|
||||||
|
duplicate_slot,
|
||||||
|
root,
|
||||||
|
initial_bank_hash,
|
||||||
|
&mut duplicate_slots_tracker,
|
||||||
|
&gossip_duplicate_confirmed_slots,
|
||||||
|
&ancestors,
|
||||||
|
&descendants,
|
||||||
|
&mut progress,
|
||||||
|
&mut heaviest_subtree_fork_choice,
|
||||||
|
SlotStateUpdate::Duplicate,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Now freeze the bank
|
||||||
|
let frozen_duplicate_slot_hash = bank_forks
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(duplicate_slot)
|
||||||
|
.unwrap()
|
||||||
|
.hash();
|
||||||
|
check_slot_agrees_with_cluster(
|
||||||
|
duplicate_slot,
|
||||||
|
root,
|
||||||
|
Some(frozen_duplicate_slot_hash),
|
||||||
|
&mut duplicate_slots_tracker,
|
||||||
|
&gossip_duplicate_confirmed_slots,
|
||||||
|
&ancestors,
|
||||||
|
&descendants,
|
||||||
|
&mut progress,
|
||||||
|
&mut heaviest_subtree_fork_choice,
|
||||||
|
SlotStateUpdate::Frozen,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Progress map should have the correct updates, fork choice should mark duplicate
|
||||||
|
// as unvotable
|
||||||
|
assert!(progress.is_unconfirmed_duplicate(duplicate_slot).unwrap());
|
||||||
|
|
||||||
|
// The ancestor of the duplicate slot should be the best slot now
|
||||||
|
let (duplicate_ancestor, duplicate_parent_hash) = {
|
||||||
|
let r_bank_forks = bank_forks.read().unwrap();
|
||||||
|
let parent_bank = r_bank_forks.get(duplicate_slot).unwrap().parent().unwrap();
|
||||||
|
(parent_bank.slot(), parent_bank.hash())
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
heaviest_subtree_fork_choice.best_overall_slot(),
|
||||||
|
(duplicate_ancestor, duplicate_parent_hash)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_state_unfrozen_bank_duplicate_then_bank_frozen() {
|
||||||
|
run_test_state_duplicate_then_bank_frozen(Some(Hash::default()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_state_unreplayed_bank_duplicate_then_bank_frozen() {
|
||||||
|
run_test_state_duplicate_then_bank_frozen(None);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_state_ancestor_confirmed_descendant_duplicate() {
|
fn test_state_ancestor_confirmed_descendant_duplicate() {
|
||||||
// Common state
|
// Common state
|
||||||
|
@ -705,6 +784,7 @@ mod test {
|
||||||
(3, slot3_hash)
|
(3, slot3_hash)
|
||||||
);
|
);
|
||||||
let root = 0;
|
let root = 0;
|
||||||
|
let mut duplicate_slots_tracker = DuplicateSlotsTracker::default();
|
||||||
let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default();
|
let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default();
|
||||||
|
|
||||||
// Mark slot 2 as duplicate confirmed
|
// Mark slot 2 as duplicate confirmed
|
||||||
|
@ -714,6 +794,7 @@ mod test {
|
||||||
2,
|
2,
|
||||||
root,
|
root,
|
||||||
Some(slot2_hash),
|
Some(slot2_hash),
|
||||||
|
&mut duplicate_slots_tracker,
|
||||||
&gossip_duplicate_confirmed_slots,
|
&gossip_duplicate_confirmed_slots,
|
||||||
&ancestors,
|
&ancestors,
|
||||||
&descendants,
|
&descendants,
|
||||||
|
@ -727,11 +808,13 @@ mod test {
|
||||||
(3, slot3_hash)
|
(3, slot3_hash)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Mark 3 as duplicate, should not remove slot 2 from fork choice
|
// Mark 3 as duplicate, should not remove the duplicate confirmed slot 2 from
|
||||||
|
// fork choice
|
||||||
check_slot_agrees_with_cluster(
|
check_slot_agrees_with_cluster(
|
||||||
3,
|
3,
|
||||||
root,
|
root,
|
||||||
Some(slot3_hash),
|
Some(slot3_hash),
|
||||||
|
&mut duplicate_slots_tracker,
|
||||||
&gossip_duplicate_confirmed_slots,
|
&gossip_duplicate_confirmed_slots,
|
||||||
&ancestors,
|
&ancestors,
|
||||||
&descendants,
|
&descendants,
|
||||||
|
@ -764,12 +847,15 @@ mod test {
|
||||||
(3, slot3_hash)
|
(3, slot3_hash)
|
||||||
);
|
);
|
||||||
let root = 0;
|
let root = 0;
|
||||||
|
let mut duplicate_slots_tracker = DuplicateSlotsTracker::default();
|
||||||
let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default();
|
let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default();
|
||||||
// Mark 2 as duplicate confirmed
|
|
||||||
|
// Mark 2 as duplicate
|
||||||
check_slot_agrees_with_cluster(
|
check_slot_agrees_with_cluster(
|
||||||
2,
|
2,
|
||||||
root,
|
root,
|
||||||
Some(bank_forks.read().unwrap().get(2).unwrap().hash()),
|
Some(bank_forks.read().unwrap().get(2).unwrap().hash()),
|
||||||
|
&mut duplicate_slots_tracker,
|
||||||
&gossip_duplicate_confirmed_slots,
|
&gossip_duplicate_confirmed_slots,
|
||||||
&ancestors,
|
&ancestors,
|
||||||
&descendants,
|
&descendants,
|
||||||
|
@ -790,6 +876,7 @@ mod test {
|
||||||
3,
|
3,
|
||||||
root,
|
root,
|
||||||
Some(slot3_hash),
|
Some(slot3_hash),
|
||||||
|
&mut duplicate_slots_tracker,
|
||||||
&gossip_duplicate_confirmed_slots,
|
&gossip_duplicate_confirmed_slots,
|
||||||
&ancestors,
|
&ancestors,
|
||||||
&descendants,
|
&descendants,
|
||||||
|
|
|
@ -1358,7 +1358,7 @@ pub mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
cluster_info_vote_listener::VoteTracker,
|
cluster_info_vote_listener::VoteTracker,
|
||||||
cluster_slot_state_verifier::GossipDuplicateConfirmedSlots,
|
cluster_slot_state_verifier::{DuplicateSlotsTracker, GossipDuplicateConfirmedSlots},
|
||||||
cluster_slots::ClusterSlots,
|
cluster_slots::ClusterSlots,
|
||||||
fork_choice::SelectVoteAndResetForkResult,
|
fork_choice::SelectVoteAndResetForkResult,
|
||||||
heaviest_subtree_fork_choice::{HeaviestSubtreeForkChoice, SlotHashKey},
|
heaviest_subtree_fork_choice::{HeaviestSubtreeForkChoice, SlotHashKey},
|
||||||
|
@ -1554,6 +1554,7 @@ pub mod test {
|
||||||
&AbsRequestSender::default(),
|
&AbsRequestSender::default(),
|
||||||
None,
|
None,
|
||||||
&mut self.heaviest_subtree_fork_choice,
|
&mut self.heaviest_subtree_fork_choice,
|
||||||
|
&mut DuplicateSlotsTracker::default(),
|
||||||
&mut GossipDuplicateConfirmedSlots::default(),
|
&mut GossipDuplicateConfirmedSlots::default(),
|
||||||
&mut UnfrozenGossipVerifiedVoteHashes::default(),
|
&mut UnfrozenGossipVerifiedVoteHashes::default(),
|
||||||
&mut true,
|
&mut true,
|
||||||
|
|
|
@ -458,6 +458,7 @@ impl ProgressMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
pub fn is_unconfirmed_duplicate(&self, slot: Slot) -> Option<bool> {
|
pub fn is_unconfirmed_duplicate(&self, slot: Slot) -> Option<bool> {
|
||||||
self.get(&slot).map(|p| {
|
self.get(&slot).map(|p| {
|
||||||
p.duplicate_stats
|
p.duplicate_stats
|
||||||
|
|
|
@ -341,6 +341,7 @@ impl ReplayStage {
|
||||||
let mut partition_exists = false;
|
let mut partition_exists = false;
|
||||||
let mut skipped_slots_info = SkippedSlotsInfo::default();
|
let mut skipped_slots_info = SkippedSlotsInfo::default();
|
||||||
let mut replay_timing = ReplayTiming::default();
|
let mut replay_timing = ReplayTiming::default();
|
||||||
|
let mut duplicate_slots_tracker = DuplicateSlotsTracker::default();
|
||||||
let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default();
|
let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default();
|
||||||
let mut unfrozen_gossip_verified_vote_hashes = UnfrozenGossipVerifiedVoteHashes::default();
|
let mut unfrozen_gossip_verified_vote_hashes = UnfrozenGossipVerifiedVoteHashes::default();
|
||||||
let mut latest_validator_votes_for_frozen_banks = LatestValidatorVotesForFrozenBanks::default();
|
let mut latest_validator_votes_for_frozen_banks = LatestValidatorVotesForFrozenBanks::default();
|
||||||
|
@ -386,6 +387,7 @@ impl ReplayStage {
|
||||||
&bank_notification_sender,
|
&bank_notification_sender,
|
||||||
&rewards_recorder_sender,
|
&rewards_recorder_sender,
|
||||||
&subscriptions,
|
&subscriptions,
|
||||||
|
&mut duplicate_slots_tracker,
|
||||||
&gossip_duplicate_confirmed_slots,
|
&gossip_duplicate_confirmed_slots,
|
||||||
&ancestors,
|
&ancestors,
|
||||||
&descendants,
|
&descendants,
|
||||||
|
@ -414,6 +416,7 @@ impl ReplayStage {
|
||||||
let mut process_gossip_duplicate_confirmed_slots_time = Measure::start("process_gossip_duplicate_confirmed_slots");
|
let mut process_gossip_duplicate_confirmed_slots_time = Measure::start("process_gossip_duplicate_confirmed_slots");
|
||||||
Self::process_gossip_duplicate_confirmed_slots(
|
Self::process_gossip_duplicate_confirmed_slots(
|
||||||
&gossip_duplicate_confirmed_slots_receiver,
|
&gossip_duplicate_confirmed_slots_receiver,
|
||||||
|
&mut duplicate_slots_tracker,
|
||||||
&mut gossip_duplicate_confirmed_slots,
|
&mut gossip_duplicate_confirmed_slots,
|
||||||
&bank_forks,
|
&bank_forks,
|
||||||
&mut progress,
|
&mut progress,
|
||||||
|
@ -443,6 +446,7 @@ impl ReplayStage {
|
||||||
if !tpu_has_bank {
|
if !tpu_has_bank {
|
||||||
Self::process_duplicate_slots(
|
Self::process_duplicate_slots(
|
||||||
&duplicate_slots_receiver,
|
&duplicate_slots_receiver,
|
||||||
|
&mut duplicate_slots_tracker,
|
||||||
&gossip_duplicate_confirmed_slots,
|
&gossip_duplicate_confirmed_slots,
|
||||||
&bank_forks,
|
&bank_forks,
|
||||||
&ancestors,
|
&ancestors,
|
||||||
|
@ -491,6 +495,7 @@ impl ReplayStage {
|
||||||
);
|
);
|
||||||
|
|
||||||
Self::mark_slots_confirmed(&confirmed_forks, &bank_forks, &mut progress,
|
Self::mark_slots_confirmed(&confirmed_forks, &bank_forks, &mut progress,
|
||||||
|
&mut duplicate_slots_tracker,
|
||||||
&ancestors, &descendants, &mut
|
&ancestors, &descendants, &mut
|
||||||
heaviest_subtree_fork_choice);
|
heaviest_subtree_fork_choice);
|
||||||
}
|
}
|
||||||
|
@ -584,6 +589,7 @@ impl ReplayStage {
|
||||||
&block_commitment_cache,
|
&block_commitment_cache,
|
||||||
&mut heaviest_subtree_fork_choice,
|
&mut heaviest_subtree_fork_choice,
|
||||||
&bank_notification_sender,
|
&bank_notification_sender,
|
||||||
|
&mut duplicate_slots_tracker,
|
||||||
&mut gossip_duplicate_confirmed_slots,
|
&mut gossip_duplicate_confirmed_slots,
|
||||||
&mut unfrozen_gossip_verified_vote_hashes,
|
&mut unfrozen_gossip_verified_vote_hashes,
|
||||||
&mut voted_signatures,
|
&mut voted_signatures,
|
||||||
|
@ -913,6 +919,7 @@ impl ReplayStage {
|
||||||
// for duplicate slot recovery.
|
// for duplicate slot recovery.
|
||||||
fn process_gossip_duplicate_confirmed_slots(
|
fn process_gossip_duplicate_confirmed_slots(
|
||||||
gossip_duplicate_confirmed_slots_receiver: &GossipDuplicateConfirmedSlotsReceiver,
|
gossip_duplicate_confirmed_slots_receiver: &GossipDuplicateConfirmedSlotsReceiver,
|
||||||
|
duplicate_slots_tracker: &mut DuplicateSlotsTracker,
|
||||||
gossip_duplicate_confirmed_slots: &mut GossipDuplicateConfirmedSlots,
|
gossip_duplicate_confirmed_slots: &mut GossipDuplicateConfirmedSlots,
|
||||||
bank_forks: &RwLock<BankForks>,
|
bank_forks: &RwLock<BankForks>,
|
||||||
progress: &mut ProgressMap,
|
progress: &mut ProgressMap,
|
||||||
|
@ -941,6 +948,7 @@ impl ReplayStage {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get(confirmed_slot)
|
.get(confirmed_slot)
|
||||||
.map(|b| b.hash()),
|
.map(|b| b.hash()),
|
||||||
|
duplicate_slots_tracker,
|
||||||
gossip_duplicate_confirmed_slots,
|
gossip_duplicate_confirmed_slots,
|
||||||
ancestors,
|
ancestors,
|
||||||
descendants,
|
descendants,
|
||||||
|
@ -974,6 +982,7 @@ impl ReplayStage {
|
||||||
// Checks for and handle forks with duplicate slots.
|
// Checks for and handle forks with duplicate slots.
|
||||||
fn process_duplicate_slots(
|
fn process_duplicate_slots(
|
||||||
duplicate_slots_receiver: &DuplicateSlotReceiver,
|
duplicate_slots_receiver: &DuplicateSlotReceiver,
|
||||||
|
duplicate_slots_tracker: &mut DuplicateSlotsTracker,
|
||||||
gossip_duplicate_confirmed_slots: &GossipDuplicateConfirmedSlots,
|
gossip_duplicate_confirmed_slots: &GossipDuplicateConfirmedSlots,
|
||||||
bank_forks: &RwLock<BankForks>,
|
bank_forks: &RwLock<BankForks>,
|
||||||
ancestors: &HashMap<Slot, HashSet<Slot>>,
|
ancestors: &HashMap<Slot, HashSet<Slot>>,
|
||||||
|
@ -981,24 +990,25 @@ impl ReplayStage {
|
||||||
progress: &mut ProgressMap,
|
progress: &mut ProgressMap,
|
||||||
fork_choice: &mut HeaviestSubtreeForkChoice,
|
fork_choice: &mut HeaviestSubtreeForkChoice,
|
||||||
) {
|
) {
|
||||||
let duplicate_slots: Vec<Slot> = duplicate_slots_receiver.try_iter().collect();
|
let new_duplicate_slots: Vec<Slot> = duplicate_slots_receiver.try_iter().collect();
|
||||||
let (root_slot, bank_hashes) = {
|
let (root_slot, bank_hashes) = {
|
||||||
let r_bank_forks = bank_forks.read().unwrap();
|
let r_bank_forks = bank_forks.read().unwrap();
|
||||||
let bank_hashes: Vec<Option<Hash>> = duplicate_slots
|
let bank_hashes: Vec<Option<Hash>> = new_duplicate_slots
|
||||||
.iter()
|
.iter()
|
||||||
.map(|slot| r_bank_forks.get(*slot).map(|bank| bank.hash()))
|
.map(|duplicate_slot| r_bank_forks.get(*duplicate_slot).map(|bank| bank.hash()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
(r_bank_forks.root(), bank_hashes)
|
(r_bank_forks.root(), bank_hashes)
|
||||||
};
|
};
|
||||||
|
for (duplicate_slot, bank_hash) in
|
||||||
for (duplicate_slot, bank_hash) in duplicate_slots.into_iter().zip(bank_hashes.into_iter())
|
new_duplicate_slots.into_iter().zip(bank_hashes.into_iter())
|
||||||
{
|
{
|
||||||
// WindowService should only send the signal once per slot
|
// WindowService should only send the signal once per slot
|
||||||
check_slot_agrees_with_cluster(
|
check_slot_agrees_with_cluster(
|
||||||
duplicate_slot,
|
duplicate_slot,
|
||||||
root_slot,
|
root_slot,
|
||||||
bank_hash,
|
bank_hash,
|
||||||
|
duplicate_slots_tracker,
|
||||||
gossip_duplicate_confirmed_slots,
|
gossip_duplicate_confirmed_slots,
|
||||||
ancestors,
|
ancestors,
|
||||||
descendants,
|
descendants,
|
||||||
|
@ -1247,6 +1257,7 @@ impl ReplayStage {
|
||||||
root: Slot,
|
root: Slot,
|
||||||
err: &BlockstoreProcessorError,
|
err: &BlockstoreProcessorError,
|
||||||
subscriptions: &Arc<RpcSubscriptions>,
|
subscriptions: &Arc<RpcSubscriptions>,
|
||||||
|
duplicate_slots_tracker: &mut DuplicateSlotsTracker,
|
||||||
gossip_duplicate_confirmed_slots: &GossipDuplicateConfirmedSlots,
|
gossip_duplicate_confirmed_slots: &GossipDuplicateConfirmedSlots,
|
||||||
ancestors: &HashMap<Slot, HashSet<Slot>>,
|
ancestors: &HashMap<Slot, HashSet<Slot>>,
|
||||||
descendants: &HashMap<Slot, HashSet<Slot>>,
|
descendants: &HashMap<Slot, HashSet<Slot>>,
|
||||||
|
@ -1290,6 +1301,7 @@ impl ReplayStage {
|
||||||
slot,
|
slot,
|
||||||
root,
|
root,
|
||||||
Some(bank.hash()),
|
Some(bank.hash()),
|
||||||
|
duplicate_slots_tracker,
|
||||||
gossip_duplicate_confirmed_slots,
|
gossip_duplicate_confirmed_slots,
|
||||||
ancestors,
|
ancestors,
|
||||||
descendants,
|
descendants,
|
||||||
|
@ -1319,6 +1331,7 @@ impl ReplayStage {
|
||||||
block_commitment_cache: &Arc<RwLock<BlockCommitmentCache>>,
|
block_commitment_cache: &Arc<RwLock<BlockCommitmentCache>>,
|
||||||
heaviest_subtree_fork_choice: &mut HeaviestSubtreeForkChoice,
|
heaviest_subtree_fork_choice: &mut HeaviestSubtreeForkChoice,
|
||||||
bank_notification_sender: &Option<BankNotificationSender>,
|
bank_notification_sender: &Option<BankNotificationSender>,
|
||||||
|
duplicate_slots_tracker: &mut DuplicateSlotsTracker,
|
||||||
gossip_duplicate_confirmed_slots: &mut GossipDuplicateConfirmedSlots,
|
gossip_duplicate_confirmed_slots: &mut GossipDuplicateConfirmedSlots,
|
||||||
unfrozen_gossip_verified_vote_hashes: &mut UnfrozenGossipVerifiedVoteHashes,
|
unfrozen_gossip_verified_vote_hashes: &mut UnfrozenGossipVerifiedVoteHashes,
|
||||||
vote_signatures: &mut Vec<Signature>,
|
vote_signatures: &mut Vec<Signature>,
|
||||||
|
@ -1367,6 +1380,7 @@ impl ReplayStage {
|
||||||
accounts_background_request_sender,
|
accounts_background_request_sender,
|
||||||
highest_confirmed_root,
|
highest_confirmed_root,
|
||||||
heaviest_subtree_fork_choice,
|
heaviest_subtree_fork_choice,
|
||||||
|
duplicate_slots_tracker,
|
||||||
gossip_duplicate_confirmed_slots,
|
gossip_duplicate_confirmed_slots,
|
||||||
unfrozen_gossip_verified_vote_hashes,
|
unfrozen_gossip_verified_vote_hashes,
|
||||||
has_new_vote_been_rooted,
|
has_new_vote_been_rooted,
|
||||||
|
@ -1660,6 +1674,7 @@ impl ReplayStage {
|
||||||
bank_notification_sender: &Option<BankNotificationSender>,
|
bank_notification_sender: &Option<BankNotificationSender>,
|
||||||
rewards_recorder_sender: &Option<RewardsRecorderSender>,
|
rewards_recorder_sender: &Option<RewardsRecorderSender>,
|
||||||
subscriptions: &Arc<RpcSubscriptions>,
|
subscriptions: &Arc<RpcSubscriptions>,
|
||||||
|
duplicate_slots_tracker: &mut DuplicateSlotsTracker,
|
||||||
gossip_duplicate_confirmed_slots: &GossipDuplicateConfirmedSlots,
|
gossip_duplicate_confirmed_slots: &GossipDuplicateConfirmedSlots,
|
||||||
ancestors: &HashMap<Slot, HashSet<Slot>>,
|
ancestors: &HashMap<Slot, HashSet<Slot>>,
|
||||||
descendants: &HashMap<Slot, HashSet<Slot>>,
|
descendants: &HashMap<Slot, HashSet<Slot>>,
|
||||||
|
@ -1738,6 +1753,7 @@ impl ReplayStage {
|
||||||
root_slot,
|
root_slot,
|
||||||
&err,
|
&err,
|
||||||
subscriptions,
|
subscriptions,
|
||||||
|
duplicate_slots_tracker,
|
||||||
gossip_duplicate_confirmed_slots,
|
gossip_duplicate_confirmed_slots,
|
||||||
ancestors,
|
ancestors,
|
||||||
descendants,
|
descendants,
|
||||||
|
@ -1774,6 +1790,7 @@ impl ReplayStage {
|
||||||
bank.slot(),
|
bank.slot(),
|
||||||
bank_forks.read().unwrap().root(),
|
bank_forks.read().unwrap().root(),
|
||||||
Some(bank.hash()),
|
Some(bank.hash()),
|
||||||
|
duplicate_slots_tracker,
|
||||||
gossip_duplicate_confirmed_slots,
|
gossip_duplicate_confirmed_slots,
|
||||||
ancestors,
|
ancestors,
|
||||||
descendants,
|
descendants,
|
||||||
|
@ -2310,6 +2327,7 @@ impl ReplayStage {
|
||||||
confirmed_forks: &[Slot],
|
confirmed_forks: &[Slot],
|
||||||
bank_forks: &RwLock<BankForks>,
|
bank_forks: &RwLock<BankForks>,
|
||||||
progress: &mut ProgressMap,
|
progress: &mut ProgressMap,
|
||||||
|
duplicate_slots_tracker: &mut DuplicateSlotsTracker,
|
||||||
ancestors: &HashMap<Slot, HashSet<Slot>>,
|
ancestors: &HashMap<Slot, HashSet<Slot>>,
|
||||||
descendants: &HashMap<Slot, HashSet<Slot>>,
|
descendants: &HashMap<Slot, HashSet<Slot>>,
|
||||||
fork_choice: &mut HeaviestSubtreeForkChoice,
|
fork_choice: &mut HeaviestSubtreeForkChoice,
|
||||||
|
@ -2334,6 +2352,7 @@ impl ReplayStage {
|
||||||
*slot,
|
*slot,
|
||||||
root_slot,
|
root_slot,
|
||||||
bank_hash,
|
bank_hash,
|
||||||
|
duplicate_slots_tracker,
|
||||||
// Don't need to pass the gossip confirmed slots since `slot`
|
// Don't need to pass the gossip confirmed slots since `slot`
|
||||||
// is already marked as confirmed in progress
|
// is already marked as confirmed in progress
|
||||||
&BTreeMap::new(),
|
&BTreeMap::new(),
|
||||||
|
@ -2389,6 +2408,7 @@ impl ReplayStage {
|
||||||
accounts_background_request_sender: &AbsRequestSender,
|
accounts_background_request_sender: &AbsRequestSender,
|
||||||
highest_confirmed_root: Option<Slot>,
|
highest_confirmed_root: Option<Slot>,
|
||||||
heaviest_subtree_fork_choice: &mut HeaviestSubtreeForkChoice,
|
heaviest_subtree_fork_choice: &mut HeaviestSubtreeForkChoice,
|
||||||
|
duplicate_slots_tracker: &mut DuplicateSlotsTracker,
|
||||||
gossip_duplicate_confirmed_slots: &mut GossipDuplicateConfirmedSlots,
|
gossip_duplicate_confirmed_slots: &mut GossipDuplicateConfirmedSlots,
|
||||||
unfrozen_gossip_verified_vote_hashes: &mut UnfrozenGossipVerifiedVoteHashes,
|
unfrozen_gossip_verified_vote_hashes: &mut UnfrozenGossipVerifiedVoteHashes,
|
||||||
has_new_vote_been_rooted: &mut bool,
|
has_new_vote_been_rooted: &mut bool,
|
||||||
|
@ -2414,6 +2434,10 @@ impl ReplayStage {
|
||||||
}
|
}
|
||||||
progress.handle_new_root(&r_bank_forks);
|
progress.handle_new_root(&r_bank_forks);
|
||||||
heaviest_subtree_fork_choice.set_root((new_root, r_bank_forks.root_bank().hash()));
|
heaviest_subtree_fork_choice.set_root((new_root, r_bank_forks.root_bank().hash()));
|
||||||
|
let mut slots_ge_root = duplicate_slots_tracker.split_off(&new_root);
|
||||||
|
// duplicate_slots_tracker now only contains entries >= `new_root`
|
||||||
|
std::mem::swap(duplicate_slots_tracker, &mut slots_ge_root);
|
||||||
|
|
||||||
let mut slots_ge_root = gossip_duplicate_confirmed_slots.split_off(&new_root);
|
let mut slots_ge_root = gossip_duplicate_confirmed_slots.split_off(&new_root);
|
||||||
// gossip_confirmed_slots now only contains entries >= `new_root`
|
// gossip_confirmed_slots now only contains entries >= `new_root`
|
||||||
std::mem::swap(gossip_duplicate_confirmed_slots, &mut slots_ge_root);
|
std::mem::swap(gossip_duplicate_confirmed_slots, &mut slots_ge_root);
|
||||||
|
@ -2856,6 +2880,9 @@ mod tests {
|
||||||
ForkProgress::new(Hash::default(), None, DuplicateStats::default(), None, 0, 0),
|
ForkProgress::new(Hash::default(), None, DuplicateStats::default(), None, 0, 0),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut duplicate_slots_tracker: DuplicateSlotsTracker =
|
||||||
|
vec![root - 1, root, root + 1].into_iter().collect();
|
||||||
let mut gossip_duplicate_confirmed_slots: GossipDuplicateConfirmedSlots =
|
let mut gossip_duplicate_confirmed_slots: GossipDuplicateConfirmedSlots =
|
||||||
vec![root - 1, root, root + 1]
|
vec![root - 1, root, root + 1]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -2875,6 +2902,7 @@ mod tests {
|
||||||
&AbsRequestSender::default(),
|
&AbsRequestSender::default(),
|
||||||
None,
|
None,
|
||||||
&mut heaviest_subtree_fork_choice,
|
&mut heaviest_subtree_fork_choice,
|
||||||
|
&mut duplicate_slots_tracker,
|
||||||
&mut gossip_duplicate_confirmed_slots,
|
&mut gossip_duplicate_confirmed_slots,
|
||||||
&mut unfrozen_gossip_verified_vote_hashes,
|
&mut unfrozen_gossip_verified_vote_hashes,
|
||||||
&mut true,
|
&mut true,
|
||||||
|
@ -2884,6 +2912,10 @@ mod tests {
|
||||||
assert_eq!(progress.len(), 1);
|
assert_eq!(progress.len(), 1);
|
||||||
assert!(progress.get(&root).is_some());
|
assert!(progress.get(&root).is_some());
|
||||||
// root - 1 is filtered out
|
// root - 1 is filtered out
|
||||||
|
assert_eq!(
|
||||||
|
duplicate_slots_tracker.into_iter().collect::<Vec<Slot>>(),
|
||||||
|
vec![root, root + 1]
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
gossip_duplicate_confirmed_slots
|
gossip_duplicate_confirmed_slots
|
||||||
.keys()
|
.keys()
|
||||||
|
@ -2944,6 +2976,7 @@ mod tests {
|
||||||
&AbsRequestSender::default(),
|
&AbsRequestSender::default(),
|
||||||
Some(confirmed_root),
|
Some(confirmed_root),
|
||||||
&mut heaviest_subtree_fork_choice,
|
&mut heaviest_subtree_fork_choice,
|
||||||
|
&mut DuplicateSlotsTracker::default(),
|
||||||
&mut GossipDuplicateConfirmedSlots::default(),
|
&mut GossipDuplicateConfirmedSlots::default(),
|
||||||
&mut UnfrozenGossipVerifiedVoteHashes::default(),
|
&mut UnfrozenGossipVerifiedVoteHashes::default(),
|
||||||
&mut true,
|
&mut true,
|
||||||
|
@ -3220,7 +3253,8 @@ mod tests {
|
||||||
0,
|
0,
|
||||||
err,
|
err,
|
||||||
&subscriptions,
|
&subscriptions,
|
||||||
&BTreeMap::new(),
|
&mut DuplicateSlotsTracker::default(),
|
||||||
|
&GossipDuplicateConfirmedSlots::default(),
|
||||||
&HashMap::new(),
|
&HashMap::new(),
|
||||||
&HashMap::new(),
|
&HashMap::new(),
|
||||||
&mut progress,
|
&mut progress,
|
||||||
|
@ -4592,13 +4626,15 @@ mod tests {
|
||||||
blockstore.store_duplicate_slot(4, vec![], vec![]).unwrap();
|
blockstore.store_duplicate_slot(4, vec![], vec![]).unwrap();
|
||||||
let ancestors = bank_forks.read().unwrap().ancestors();
|
let ancestors = bank_forks.read().unwrap().ancestors();
|
||||||
let descendants = bank_forks.read().unwrap().descendants().clone();
|
let descendants = bank_forks.read().unwrap().descendants().clone();
|
||||||
let mut gossip_duplicate_confirmed_slots = BTreeMap::new();
|
let mut duplicate_slots_tracker = DuplicateSlotsTracker::default();
|
||||||
|
let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default();
|
||||||
let bank4_hash = bank_forks.read().unwrap().get(4).unwrap().hash();
|
let bank4_hash = bank_forks.read().unwrap().get(4).unwrap().hash();
|
||||||
assert_ne!(bank4_hash, Hash::default());
|
assert_ne!(bank4_hash, Hash::default());
|
||||||
check_slot_agrees_with_cluster(
|
check_slot_agrees_with_cluster(
|
||||||
4,
|
4,
|
||||||
bank_forks.read().unwrap().root(),
|
bank_forks.read().unwrap().root(),
|
||||||
Some(bank4_hash),
|
Some(bank4_hash),
|
||||||
|
&mut duplicate_slots_tracker,
|
||||||
&gossip_duplicate_confirmed_slots,
|
&gossip_duplicate_confirmed_slots,
|
||||||
&ancestors,
|
&ancestors,
|
||||||
&descendants,
|
&descendants,
|
||||||
|
@ -4627,6 +4663,7 @@ mod tests {
|
||||||
2,
|
2,
|
||||||
bank_forks.read().unwrap().root(),
|
bank_forks.read().unwrap().root(),
|
||||||
Some(bank2_hash),
|
Some(bank2_hash),
|
||||||
|
&mut duplicate_slots_tracker,
|
||||||
&gossip_duplicate_confirmed_slots,
|
&gossip_duplicate_confirmed_slots,
|
||||||
&ancestors,
|
&ancestors,
|
||||||
&descendants,
|
&descendants,
|
||||||
|
@ -4655,6 +4692,7 @@ mod tests {
|
||||||
4,
|
4,
|
||||||
bank_forks.read().unwrap().root(),
|
bank_forks.read().unwrap().root(),
|
||||||
Some(bank4_hash),
|
Some(bank4_hash),
|
||||||
|
&mut duplicate_slots_tracker,
|
||||||
&gossip_duplicate_confirmed_slots,
|
&gossip_duplicate_confirmed_slots,
|
||||||
&ancestors,
|
&ancestors,
|
||||||
&descendants,
|
&descendants,
|
||||||
|
|
Loading…
Reference in New Issue