Add frozen hashes and marking DuplicateConfirmed in blockstore to state machine (#18648)
This commit is contained in:
parent
b9dc85a934
commit
ce467bea20
File diff suppressed because it is too large
Load Diff
|
@ -40,5 +40,10 @@ pub trait ForkChoice {
|
||||||
|
|
||||||
fn mark_fork_invalid_candidate(&mut self, invalid_slot: &Self::ForkChoiceKey);
|
fn mark_fork_invalid_candidate(&mut self, invalid_slot: &Self::ForkChoiceKey);
|
||||||
|
|
||||||
fn mark_fork_valid_candidate(&mut self, valid_slot: &Self::ForkChoiceKey);
|
/// Returns any newly duplicate confirmed ancestors of `valid_slot` up to and including
|
||||||
|
/// `valid_slot` itself
|
||||||
|
fn mark_fork_valid_candidate(
|
||||||
|
&mut self,
|
||||||
|
valid_slot: &Self::ForkChoiceKey,
|
||||||
|
) -> Vec<Self::ForkChoiceKey>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1007,11 +1007,21 @@ impl ForkChoice for HeaviestSubtreeForkChoice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_fork_valid_candidate(&mut self, valid_slot_hash_key: &SlotHashKey) {
|
fn mark_fork_valid_candidate(&mut self, valid_slot_hash_key: &SlotHashKey) -> Vec<SlotHashKey> {
|
||||||
info!(
|
info!(
|
||||||
"marking fork starting at: {:?} valid candidate",
|
"marking fork starting at: {:?} valid candidate",
|
||||||
valid_slot_hash_key
|
valid_slot_hash_key
|
||||||
);
|
);
|
||||||
|
let mut newly_duplicate_confirmed_ancestors = vec![];
|
||||||
|
|
||||||
|
for ancestor_key in std::iter::once(*valid_slot_hash_key)
|
||||||
|
.chain(self.ancestor_iterator(*valid_slot_hash_key))
|
||||||
|
{
|
||||||
|
if !self.is_duplicate_confirmed(&ancestor_key).unwrap() {
|
||||||
|
newly_duplicate_confirmed_ancestors.push(ancestor_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut update_operations = UpdateOperations::default();
|
let mut update_operations = UpdateOperations::default();
|
||||||
// Notify all the children of this node that a parent was marked as valid
|
// Notify all the children of this node that a parent was marked as valid
|
||||||
for child_hash_key in self.subtree_diff(*valid_slot_hash_key, SlotHashKey::default()) {
|
for child_hash_key in self.subtree_diff(*valid_slot_hash_key, SlotHashKey::default()) {
|
||||||
|
@ -1025,6 +1035,7 @@ impl ForkChoice for HeaviestSubtreeForkChoice {
|
||||||
// Aggregate across all ancestors to find the new best slots including this fork
|
// Aggregate across all ancestors to find the new best slots including this fork
|
||||||
self.insert_aggregate_operations(&mut update_operations, *valid_slot_hash_key);
|
self.insert_aggregate_operations(&mut update_operations, *valid_slot_hash_key);
|
||||||
self.process_update_operations(update_operations);
|
self.process_update_operations(update_operations);
|
||||||
|
newly_duplicate_confirmed_ancestors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ use solana_sdk::{
|
||||||
};
|
};
|
||||||
use solana_vote_program::vote_state::Vote;
|
use solana_vote_program::vote_state::Vote;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
result,
|
result,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
|
@ -427,6 +427,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,
|
||||||
|
&blockstore,
|
||||||
&mut duplicate_slots_tracker,
|
&mut duplicate_slots_tracker,
|
||||||
&mut gossip_duplicate_confirmed_slots,
|
&mut gossip_duplicate_confirmed_slots,
|
||||||
&bank_forks,
|
&bank_forks,
|
||||||
|
@ -455,6 +456,7 @@ impl ReplayStage {
|
||||||
let mut process_duplicate_slots_time = Measure::start("process_duplicate_slots");
|
let mut process_duplicate_slots_time = Measure::start("process_duplicate_slots");
|
||||||
if !tpu_has_bank {
|
if !tpu_has_bank {
|
||||||
Self::process_duplicate_slots(
|
Self::process_duplicate_slots(
|
||||||
|
&blockstore,
|
||||||
&duplicate_slots_receiver,
|
&duplicate_slots_receiver,
|
||||||
&mut duplicate_slots_tracker,
|
&mut duplicate_slots_tracker,
|
||||||
&gossip_duplicate_confirmed_slots,
|
&gossip_duplicate_confirmed_slots,
|
||||||
|
@ -503,7 +505,7 @@ impl ReplayStage {
|
||||||
&bank_forks,
|
&bank_forks,
|
||||||
);
|
);
|
||||||
|
|
||||||
Self::mark_slots_confirmed(&confirmed_forks, &bank_forks, &mut progress, &mut duplicate_slots_tracker, &mut heaviest_subtree_fork_choice, &mut duplicate_slots_to_repair);
|
Self::mark_slots_confirmed(&confirmed_forks, &blockstore, &bank_forks, &mut progress, &mut duplicate_slots_tracker, &mut heaviest_subtree_fork_choice, &mut duplicate_slots_to_repair);
|
||||||
}
|
}
|
||||||
compute_slot_stats_time.stop();
|
compute_slot_stats_time.stop();
|
||||||
|
|
||||||
|
@ -1048,6 +1050,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,
|
||||||
|
blockstore: &Blockstore,
|
||||||
duplicate_slots_tracker: &mut DuplicateSlotsTracker,
|
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>,
|
||||||
|
@ -1057,27 +1060,30 @@ impl ReplayStage {
|
||||||
) {
|
) {
|
||||||
let root = bank_forks.read().unwrap().root();
|
let root = bank_forks.read().unwrap().root();
|
||||||
for new_confirmed_slots in gossip_duplicate_confirmed_slots_receiver.try_iter() {
|
for new_confirmed_slots in gossip_duplicate_confirmed_slots_receiver.try_iter() {
|
||||||
for (confirmed_slot, confirmed_hash) in new_confirmed_slots {
|
for (confirmed_slot, duplicate_confirmed_hash) in new_confirmed_slots {
|
||||||
if confirmed_slot <= root {
|
if confirmed_slot <= root {
|
||||||
continue;
|
continue;
|
||||||
} else if let Some(prev_hash) =
|
} else if let Some(prev_hash) = gossip_duplicate_confirmed_slots
|
||||||
gossip_duplicate_confirmed_slots.insert(confirmed_slot, confirmed_hash)
|
.insert(confirmed_slot, duplicate_confirmed_hash)
|
||||||
{
|
{
|
||||||
assert_eq!(prev_hash, confirmed_hash);
|
assert_eq!(prev_hash, duplicate_confirmed_hash);
|
||||||
// Already processed this signal
|
// Already processed this signal
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let duplicate_confirmed_state = DuplicateConfirmedState::new_from_state(
|
||||||
|
duplicate_confirmed_hash,
|
||||||
|
|| progress.is_dead(confirmed_slot).unwrap_or(false),
|
||||||
|
|| bank_forks.read().unwrap().bank_hash(confirmed_slot),
|
||||||
|
);
|
||||||
check_slot_agrees_with_cluster(
|
check_slot_agrees_with_cluster(
|
||||||
confirmed_slot,
|
confirmed_slot,
|
||||||
root,
|
root,
|
||||||
bank_forks.read().unwrap().bank_hash(confirmed_slot),
|
blockstore,
|
||||||
duplicate_slots_tracker,
|
duplicate_slots_tracker,
|
||||||
gossip_duplicate_confirmed_slots,
|
|
||||||
progress,
|
|
||||||
fork_choice,
|
fork_choice,
|
||||||
duplicate_slots_to_repair,
|
duplicate_slots_to_repair,
|
||||||
SlotStateUpdate::DuplicateConfirmed,
|
SlotStateUpdate::DuplicateConfirmed(duplicate_confirmed_state),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1104,6 +1110,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(
|
||||||
|
blockstore: &Blockstore,
|
||||||
duplicate_slots_receiver: &DuplicateSlotReceiver,
|
duplicate_slots_receiver: &DuplicateSlotReceiver,
|
||||||
duplicate_slots_tracker: &mut DuplicateSlotsTracker,
|
duplicate_slots_tracker: &mut DuplicateSlotsTracker,
|
||||||
gossip_duplicate_confirmed_slots: &GossipDuplicateConfirmedSlots,
|
gossip_duplicate_confirmed_slots: &GossipDuplicateConfirmedSlots,
|
||||||
|
@ -1126,16 +1133,21 @@ impl ReplayStage {
|
||||||
new_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
|
||||||
|
let duplicate_state = DuplicateState::new_from_state(
|
||||||
|
duplicate_slot,
|
||||||
|
gossip_duplicate_confirmed_slots,
|
||||||
|
fork_choice,
|
||||||
|
|| progress.is_dead(duplicate_slot).unwrap_or(false),
|
||||||
|
|| bank_hash,
|
||||||
|
);
|
||||||
check_slot_agrees_with_cluster(
|
check_slot_agrees_with_cluster(
|
||||||
duplicate_slot,
|
duplicate_slot,
|
||||||
root_slot,
|
root_slot,
|
||||||
bank_hash,
|
blockstore,
|
||||||
duplicate_slots_tracker,
|
duplicate_slots_tracker,
|
||||||
gossip_duplicate_confirmed_slots,
|
|
||||||
progress,
|
|
||||||
fork_choice,
|
fork_choice,
|
||||||
duplicate_slots_to_repair,
|
duplicate_slots_to_repair,
|
||||||
SlotStateUpdate::Duplicate,
|
SlotStateUpdate::Duplicate(duplicate_state),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1417,16 +1429,20 @@ impl ReplayStage {
|
||||||
err: format!("error: {:?}", err),
|
err: format!("error: {:?}", err),
|
||||||
timestamp: timestamp(),
|
timestamp: timestamp(),
|
||||||
});
|
});
|
||||||
|
let dead_state = DeadState::new_from_state(
|
||||||
|
slot,
|
||||||
|
duplicate_slots_tracker,
|
||||||
|
gossip_duplicate_confirmed_slots,
|
||||||
|
heaviest_subtree_fork_choice,
|
||||||
|
);
|
||||||
check_slot_agrees_with_cluster(
|
check_slot_agrees_with_cluster(
|
||||||
slot,
|
slot,
|
||||||
root,
|
root,
|
||||||
Some(bank.hash()),
|
blockstore,
|
||||||
duplicate_slots_tracker,
|
duplicate_slots_tracker,
|
||||||
gossip_duplicate_confirmed_slots,
|
|
||||||
progress,
|
|
||||||
heaviest_subtree_fork_choice,
|
heaviest_subtree_fork_choice,
|
||||||
duplicate_slots_to_repair,
|
duplicate_slots_to_repair,
|
||||||
SlotStateUpdate::Dead,
|
SlotStateUpdate::Dead(dead_state),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1926,16 +1942,21 @@ impl ReplayStage {
|
||||||
.get_fork_stats_mut(bank.slot())
|
.get_fork_stats_mut(bank.slot())
|
||||||
.expect("All frozen banks must exist in the Progress map")
|
.expect("All frozen banks must exist in the Progress map")
|
||||||
.bank_hash = Some(bank.hash());
|
.bank_hash = Some(bank.hash());
|
||||||
|
let bank_frozen_state = BankFrozenState::new_from_state(
|
||||||
|
bank.slot(),
|
||||||
|
bank.hash(),
|
||||||
|
duplicate_slots_tracker,
|
||||||
|
gossip_duplicate_confirmed_slots,
|
||||||
|
heaviest_subtree_fork_choice,
|
||||||
|
);
|
||||||
check_slot_agrees_with_cluster(
|
check_slot_agrees_with_cluster(
|
||||||
bank.slot(),
|
bank.slot(),
|
||||||
bank_forks.read().unwrap().root(),
|
bank_forks.read().unwrap().root(),
|
||||||
Some(bank.hash()),
|
blockstore,
|
||||||
duplicate_slots_tracker,
|
duplicate_slots_tracker,
|
||||||
gossip_duplicate_confirmed_slots,
|
|
||||||
progress,
|
|
||||||
heaviest_subtree_fork_choice,
|
heaviest_subtree_fork_choice,
|
||||||
duplicate_slots_to_repair,
|
duplicate_slots_to_repair,
|
||||||
SlotStateUpdate::Frozen,
|
SlotStateUpdate::BankFrozen(bank_frozen_state),
|
||||||
);
|
);
|
||||||
if let Some(sender) = bank_notification_sender {
|
if let Some(sender) = bank_notification_sender {
|
||||||
sender
|
sender
|
||||||
|
@ -2444,41 +2465,39 @@ impl ReplayStage {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_slots_confirmed(
|
fn mark_slots_confirmed(
|
||||||
confirmed_forks: &[Slot],
|
confirmed_forks: &[(Slot, Hash)],
|
||||||
|
blockstore: &Blockstore,
|
||||||
bank_forks: &RwLock<BankForks>,
|
bank_forks: &RwLock<BankForks>,
|
||||||
progress: &mut ProgressMap,
|
progress: &mut ProgressMap,
|
||||||
duplicate_slots_tracker: &mut DuplicateSlotsTracker,
|
duplicate_slots_tracker: &mut DuplicateSlotsTracker,
|
||||||
fork_choice: &mut HeaviestSubtreeForkChoice,
|
fork_choice: &mut HeaviestSubtreeForkChoice,
|
||||||
duplicate_slots_to_repair: &mut DuplicateSlotsToRepair,
|
duplicate_slots_to_repair: &mut DuplicateSlotsToRepair,
|
||||||
) {
|
) {
|
||||||
let (root_slot, bank_hashes) = {
|
let root_slot = bank_forks.read().unwrap().root();
|
||||||
let r_bank_forks = bank_forks.read().unwrap();
|
for (slot, frozen_hash) in confirmed_forks.iter() {
|
||||||
let bank_hashes: Vec<Option<Hash>> = confirmed_forks
|
|
||||||
.iter()
|
|
||||||
.map(|slot| r_bank_forks.bank_hash(*slot))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
(r_bank_forks.root(), bank_hashes)
|
|
||||||
};
|
|
||||||
for (slot, bank_hash) in confirmed_forks.iter().zip(bank_hashes.into_iter()) {
|
|
||||||
// This case should be guaranteed as false by confirm_forks()
|
// This case should be guaranteed as false by confirm_forks()
|
||||||
if let Some(false) = progress.is_supermajority_confirmed(*slot) {
|
if let Some(false) = progress.is_supermajority_confirmed(*slot) {
|
||||||
// Because supermajority confirmation will iterate through and update the
|
// Because supermajority confirmation will iterate through and update the
|
||||||
// subtree in fork choice, only incur this cost if the slot wasn't already
|
// subtree in fork choice, only incur this cost if the slot wasn't already
|
||||||
// confirmed
|
// confirmed
|
||||||
progress.set_supermajority_confirmed_slot(*slot);
|
progress.set_supermajority_confirmed_slot(*slot);
|
||||||
|
// If the slot was confirmed, then it must be frozen. Otherwise, we couldn't
|
||||||
|
// have replayed any of its descendants and figured out it was confirmed.
|
||||||
|
assert!(*frozen_hash != Hash::default());
|
||||||
|
|
||||||
|
let duplicate_confirmed_state = DuplicateConfirmedState::new_from_state(
|
||||||
|
*frozen_hash,
|
||||||
|
|| false,
|
||||||
|
|| Some(*frozen_hash),
|
||||||
|
);
|
||||||
check_slot_agrees_with_cluster(
|
check_slot_agrees_with_cluster(
|
||||||
*slot,
|
*slot,
|
||||||
root_slot,
|
root_slot,
|
||||||
bank_hash,
|
blockstore,
|
||||||
duplicate_slots_tracker,
|
duplicate_slots_tracker,
|
||||||
// Don't need to pass the gossip confirmed slots since `slot`
|
|
||||||
// is already marked as confirmed in progress
|
|
||||||
&BTreeMap::new(),
|
|
||||||
progress,
|
|
||||||
fork_choice,
|
fork_choice,
|
||||||
duplicate_slots_to_repair,
|
duplicate_slots_to_repair,
|
||||||
SlotStateUpdate::DuplicateConfirmed,
|
SlotStateUpdate::DuplicateConfirmed(duplicate_confirmed_state),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2490,7 +2509,7 @@ impl ReplayStage {
|
||||||
total_stake: Stake,
|
total_stake: Stake,
|
||||||
progress: &ProgressMap,
|
progress: &ProgressMap,
|
||||||
bank_forks: &RwLock<BankForks>,
|
bank_forks: &RwLock<BankForks>,
|
||||||
) -> Vec<Slot> {
|
) -> Vec<(Slot, Hash)> {
|
||||||
let mut confirmed_forks = vec![];
|
let mut confirmed_forks = vec![];
|
||||||
for (slot, prog) in progress.iter() {
|
for (slot, prog) in progress.iter() {
|
||||||
if !prog.fork_stats.is_supermajority_confirmed {
|
if !prog.fork_stats.is_supermajority_confirmed {
|
||||||
|
@ -2504,7 +2523,7 @@ impl ReplayStage {
|
||||||
if bank.is_frozen() && tower.is_slot_confirmed(*slot, voted_stakes, total_stake) {
|
if bank.is_frozen() && tower.is_slot_confirmed(*slot, voted_stakes, total_stake) {
|
||||||
info!("validator fork confirmed {} {}ms", *slot, duration);
|
info!("validator fork confirmed {} {}ms", *slot, duration);
|
||||||
datapoint_info!("validator-confirmation", ("duration_ms", duration, i64));
|
datapoint_info!("validator-confirmation", ("duration_ms", duration, i64));
|
||||||
confirmed_forks.push(*slot);
|
confirmed_forks.push((*slot, bank.hash()));
|
||||||
} else {
|
} else {
|
||||||
debug!(
|
debug!(
|
||||||
"validator fork not confirmed {} {}ms {:?}",
|
"validator fork not confirmed {} {}ms {:?}",
|
||||||
|
@ -2673,7 +2692,7 @@ impl ReplayStage {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
pub mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
consensus::Tower,
|
consensus::Tower,
|
||||||
|
@ -3247,14 +3266,14 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dead_fork_trailing_entry() {
|
fn test_dead_fork_trailing_entry() {
|
||||||
let keypair = Keypair::new();
|
let keypair = Keypair::new();
|
||||||
let res = check_dead_fork(|genesis_keypair, bank| {
|
let res = check_dead_fork(|funded_keypair, bank| {
|
||||||
let blockhash = bank.last_blockhash();
|
let blockhash = bank.last_blockhash();
|
||||||
let slot = bank.slot();
|
let slot = bank.slot();
|
||||||
let hashes_per_tick = bank.hashes_per_tick().unwrap_or(0);
|
let hashes_per_tick = bank.hashes_per_tick().unwrap_or(0);
|
||||||
let mut entries =
|
let mut entries =
|
||||||
entry::create_ticks(bank.ticks_per_slot(), hashes_per_tick, blockhash);
|
entry::create_ticks(bank.ticks_per_slot(), hashes_per_tick, blockhash);
|
||||||
let last_entry_hash = entries.last().unwrap().hash;
|
let last_entry_hash = entries.last().unwrap().hash;
|
||||||
let tx = system_transaction::transfer(genesis_keypair, &keypair.pubkey(), 2, blockhash);
|
let tx = system_transaction::transfer(funded_keypair, &keypair.pubkey(), 2, blockhash);
|
||||||
let trailing_entry = entry::next_entry(&last_entry_hash, 1, vec![tx]);
|
let trailing_entry = entry::next_entry(&last_entry_hash, 1, vec![tx]);
|
||||||
entries.push(trailing_entry);
|
entries.push(trailing_entry);
|
||||||
entries_to_test_shreds(entries, slot, slot.saturating_sub(1), true, 0)
|
entries_to_test_shreds(entries, slot, slot.saturating_sub(1), true, 0)
|
||||||
|
@ -3270,14 +3289,19 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dead_fork_entry_deserialize_failure() {
|
fn test_dead_fork_entry_deserialize_failure() {
|
||||||
// Insert entry that causes deserialization failure
|
// Insert entry that causes deserialization failure
|
||||||
let res = check_dead_fork(|_, _| {
|
let res = check_dead_fork(|_, bank| {
|
||||||
let gibberish = [0xa5u8; PACKET_DATA_SIZE];
|
let gibberish = [0xa5u8; PACKET_DATA_SIZE];
|
||||||
let mut data_header = DataShredHeader::default();
|
let mut data_header = DataShredHeader::default();
|
||||||
data_header.flags |= DATA_COMPLETE_SHRED;
|
data_header.flags |= DATA_COMPLETE_SHRED;
|
||||||
// Need to provide the right size for Shredder::deshred.
|
// Need to provide the right size for Shredder::deshred.
|
||||||
data_header.size = SIZE_OF_DATA_SHRED_PAYLOAD as u16;
|
data_header.size = SIZE_OF_DATA_SHRED_PAYLOAD as u16;
|
||||||
|
data_header.parent_offset = (bank.slot() - bank.parent_slot()) as u16;
|
||||||
|
let shred_common_header = ShredCommonHeader {
|
||||||
|
slot: bank.slot(),
|
||||||
|
..ShredCommonHeader::default()
|
||||||
|
};
|
||||||
let mut shred = Shred::new_empty_from_header(
|
let mut shred = Shred::new_empty_from_header(
|
||||||
ShredCommonHeader::default(),
|
shred_common_header,
|
||||||
data_header,
|
data_header,
|
||||||
CodingShredHeader::default(),
|
CodingShredHeader::default(),
|
||||||
);
|
);
|
||||||
|
@ -3306,37 +3330,43 @@ mod tests {
|
||||||
let ledger_path = get_tmp_ledger_path!();
|
let ledger_path = get_tmp_ledger_path!();
|
||||||
let (replay_vote_sender, _replay_vote_receiver) = unbounded();
|
let (replay_vote_sender, _replay_vote_receiver) = unbounded();
|
||||||
let res = {
|
let res = {
|
||||||
let blockstore = Arc::new(
|
let ReplayBlockstoreComponents {
|
||||||
Blockstore::open(&ledger_path)
|
blockstore,
|
||||||
.expect("Expected to be able to open database ledger"),
|
vote_simulator,
|
||||||
);
|
|
||||||
let GenesisConfigInfo {
|
|
||||||
mut genesis_config,
|
|
||||||
mint_keypair,
|
|
||||||
..
|
..
|
||||||
} = create_genesis_config(1000);
|
} = replay_blockstore_components(Some(tr(0)), 1, None);
|
||||||
genesis_config.poh_config.hashes_per_tick = Some(2);
|
let VoteSimulator {
|
||||||
let bank_forks = BankForks::new(Bank::new(&genesis_config));
|
mut progress,
|
||||||
let bank0 = bank_forks.working_bank();
|
bank_forks,
|
||||||
let mut progress = ProgressMap::default();
|
mut heaviest_subtree_fork_choice,
|
||||||
let last_blockhash = bank0.last_blockhash();
|
validator_keypairs,
|
||||||
let mut bank0_progress = progress
|
..
|
||||||
.entry(bank0.slot())
|
} = vote_simulator;
|
||||||
.or_insert_with(|| ForkProgress::new(last_blockhash, None, None, 0, 0));
|
|
||||||
let shreds = shred_to_insert(&mint_keypair, bank0.clone());
|
let bank0 = bank_forks.read().unwrap().get(0).cloned().unwrap();
|
||||||
|
assert!(bank0.is_frozen());
|
||||||
|
assert_eq!(bank0.tick_height(), bank0.max_tick_height());
|
||||||
|
let bank1 = Bank::new_from_parent(&bank0, &Pubkey::default(), 1);
|
||||||
|
bank_forks.write().unwrap().insert(bank1);
|
||||||
|
let bank1 = bank_forks.read().unwrap().get(1).cloned().unwrap();
|
||||||
|
let mut bank1_progress = progress
|
||||||
|
.entry(bank1.slot())
|
||||||
|
.or_insert_with(|| ForkProgress::new(bank1.last_blockhash(), None, None, 0, 0));
|
||||||
|
let shreds = shred_to_insert(
|
||||||
|
&validator_keypairs.values().next().unwrap().node_keypair,
|
||||||
|
bank1.clone(),
|
||||||
|
);
|
||||||
blockstore.insert_shreds(shreds, None, false).unwrap();
|
blockstore.insert_shreds(shreds, None, false).unwrap();
|
||||||
let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default()));
|
let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default()));
|
||||||
let bank_forks = Arc::new(RwLock::new(bank_forks));
|
|
||||||
let exit = Arc::new(AtomicBool::new(false));
|
let exit = Arc::new(AtomicBool::new(false));
|
||||||
let res = ReplayStage::replay_blockstore_into_bank(
|
let res = ReplayStage::replay_blockstore_into_bank(
|
||||||
&bank0,
|
&bank1,
|
||||||
&blockstore,
|
&blockstore,
|
||||||
&mut bank0_progress,
|
&mut bank1_progress,
|
||||||
None,
|
None,
|
||||||
&replay_vote_sender,
|
&replay_vote_sender,
|
||||||
&VerifyRecyclers::default(),
|
&VerifyRecyclers::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let rpc_subscriptions = Arc::new(RpcSubscriptions::new(
|
let rpc_subscriptions = Arc::new(RpcSubscriptions::new(
|
||||||
&exit,
|
&exit,
|
||||||
bank_forks.clone(),
|
bank_forks.clone(),
|
||||||
|
@ -3346,26 +3376,26 @@ mod tests {
|
||||||
if let Err(err) = &res {
|
if let Err(err) = &res {
|
||||||
ReplayStage::mark_dead_slot(
|
ReplayStage::mark_dead_slot(
|
||||||
&blockstore,
|
&blockstore,
|
||||||
&bank0,
|
&bank1,
|
||||||
0,
|
0,
|
||||||
err,
|
err,
|
||||||
&rpc_subscriptions,
|
&rpc_subscriptions,
|
||||||
&mut DuplicateSlotsTracker::default(),
|
&mut DuplicateSlotsTracker::default(),
|
||||||
&GossipDuplicateConfirmedSlots::default(),
|
&GossipDuplicateConfirmedSlots::default(),
|
||||||
&mut progress,
|
&mut progress,
|
||||||
&mut HeaviestSubtreeForkChoice::new((0, Hash::default())),
|
&mut heaviest_subtree_fork_choice,
|
||||||
&mut DuplicateSlotsToRepair::default(),
|
&mut DuplicateSlotsToRepair::default(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the erroring bank was marked as dead in the progress map
|
// Check that the erroring bank was marked as dead in the progress map
|
||||||
assert!(progress
|
assert!(progress
|
||||||
.get(&bank0.slot())
|
.get(&bank1.slot())
|
||||||
.map(|b| b.is_dead)
|
.map(|b| b.is_dead)
|
||||||
.unwrap_or(false));
|
.unwrap_or(false));
|
||||||
|
|
||||||
// Check that the erroring bank was marked as dead in blockstore
|
// Check that the erroring bank was marked as dead in blockstore
|
||||||
assert!(blockstore.is_dead(bank0.slot()));
|
assert!(blockstore.is_dead(bank1.slot()));
|
||||||
res.map(|_| ())
|
res.map(|_| ())
|
||||||
};
|
};
|
||||||
let _ignored = remove_dir_all(&ledger_path);
|
let _ignored = remove_dir_all(&ledger_path);
|
||||||
|
@ -3659,7 +3689,7 @@ mod tests {
|
||||||
&bank_forks,
|
&bank_forks,
|
||||||
);
|
);
|
||||||
// No new stats should have been computed
|
// No new stats should have been computed
|
||||||
assert_eq!(confirmed_forks, vec![0]);
|
assert_eq!(confirmed_forks, vec![(0, bank0.hash())]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ancestors = bank_forks.read().unwrap().ancestors();
|
let ancestors = bank_forks.read().unwrap().ancestors();
|
||||||
|
@ -4785,16 +4815,21 @@ mod tests {
|
||||||
let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default();
|
let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default();
|
||||||
let bank4_hash = bank_forks.read().unwrap().bank_hash(4).unwrap();
|
let bank4_hash = bank_forks.read().unwrap().bank_hash(4).unwrap();
|
||||||
assert_ne!(bank4_hash, Hash::default());
|
assert_ne!(bank4_hash, Hash::default());
|
||||||
|
let duplicate_state = DuplicateState::new_from_state(
|
||||||
|
4,
|
||||||
|
&gossip_duplicate_confirmed_slots,
|
||||||
|
&mut vote_simulator.heaviest_subtree_fork_choice,
|
||||||
|
|| progress.is_dead(4).unwrap_or(false),
|
||||||
|
|| Some(bank4_hash),
|
||||||
|
);
|
||||||
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),
|
&blockstore,
|
||||||
&mut duplicate_slots_tracker,
|
&mut duplicate_slots_tracker,
|
||||||
&gossip_duplicate_confirmed_slots,
|
|
||||||
&progress,
|
|
||||||
&mut vote_simulator.heaviest_subtree_fork_choice,
|
&mut vote_simulator.heaviest_subtree_fork_choice,
|
||||||
&mut DuplicateSlotsToRepair::default(),
|
&mut DuplicateSlotsToRepair::default(),
|
||||||
SlotStateUpdate::Duplicate,
|
SlotStateUpdate::Duplicate(duplicate_state),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (vote_fork, reset_fork) = run_compute_and_select_forks(
|
let (vote_fork, reset_fork) = run_compute_and_select_forks(
|
||||||
|
@ -4811,16 +4846,21 @@ mod tests {
|
||||||
blockstore.store_duplicate_slot(2, vec![], vec![]).unwrap();
|
blockstore.store_duplicate_slot(2, vec![], vec![]).unwrap();
|
||||||
let bank2_hash = bank_forks.read().unwrap().bank_hash(2).unwrap();
|
let bank2_hash = bank_forks.read().unwrap().bank_hash(2).unwrap();
|
||||||
assert_ne!(bank2_hash, Hash::default());
|
assert_ne!(bank2_hash, Hash::default());
|
||||||
|
let duplicate_state = DuplicateState::new_from_state(
|
||||||
|
2,
|
||||||
|
&gossip_duplicate_confirmed_slots,
|
||||||
|
&mut vote_simulator.heaviest_subtree_fork_choice,
|
||||||
|
|| progress.is_dead(2).unwrap_or(false),
|
||||||
|
|| Some(bank2_hash),
|
||||||
|
);
|
||||||
check_slot_agrees_with_cluster(
|
check_slot_agrees_with_cluster(
|
||||||
2,
|
2,
|
||||||
bank_forks.read().unwrap().root(),
|
bank_forks.read().unwrap().root(),
|
||||||
Some(bank2_hash),
|
&blockstore,
|
||||||
&mut duplicate_slots_tracker,
|
&mut duplicate_slots_tracker,
|
||||||
&gossip_duplicate_confirmed_slots,
|
|
||||||
&progress,
|
|
||||||
&mut vote_simulator.heaviest_subtree_fork_choice,
|
&mut vote_simulator.heaviest_subtree_fork_choice,
|
||||||
&mut DuplicateSlotsToRepair::default(),
|
&mut DuplicateSlotsToRepair::default(),
|
||||||
SlotStateUpdate::Duplicate,
|
SlotStateUpdate::Duplicate(duplicate_state),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (vote_fork, reset_fork) = run_compute_and_select_forks(
|
let (vote_fork, reset_fork) = run_compute_and_select_forks(
|
||||||
|
@ -4840,16 +4880,19 @@ mod tests {
|
||||||
// then slot 4 is now the heaviest bank again
|
// then slot 4 is now the heaviest bank again
|
||||||
let mut duplicate_slots_to_repair = HashSet::new();
|
let mut duplicate_slots_to_repair = HashSet::new();
|
||||||
gossip_duplicate_confirmed_slots.insert(4, bank4_hash);
|
gossip_duplicate_confirmed_slots.insert(4, bank4_hash);
|
||||||
|
let duplicate_confirmed_state = DuplicateConfirmedState::new_from_state(
|
||||||
|
bank4_hash,
|
||||||
|
|| progress.is_dead(4).unwrap_or(false),
|
||||||
|
|| Some(bank4_hash),
|
||||||
|
);
|
||||||
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),
|
&blockstore,
|
||||||
&mut duplicate_slots_tracker,
|
&mut duplicate_slots_tracker,
|
||||||
&gossip_duplicate_confirmed_slots,
|
|
||||||
&progress,
|
|
||||||
&mut vote_simulator.heaviest_subtree_fork_choice,
|
&mut vote_simulator.heaviest_subtree_fork_choice,
|
||||||
&mut duplicate_slots_to_repair,
|
&mut duplicate_slots_to_repair,
|
||||||
SlotStateUpdate::DuplicateConfirmed,
|
SlotStateUpdate::DuplicateConfirmed(duplicate_confirmed_state),
|
||||||
);
|
);
|
||||||
// The confirmed hash is detected in `progress`, which means
|
// The confirmed hash is detected in `progress`, which means
|
||||||
// it's confirmation on the replayed block. This means we have
|
// it's confirmation on the replayed block. This means we have
|
||||||
|
@ -4980,16 +5023,19 @@ mod tests {
|
||||||
|
|
||||||
// Mark fork choice branch as invalid so select forks below doesn't panic
|
// Mark fork choice branch as invalid so select forks below doesn't panic
|
||||||
// on a nonexistent `heaviest_bank_on_same_fork` after we dump the duplciate fork.
|
// on a nonexistent `heaviest_bank_on_same_fork` after we dump the duplciate fork.
|
||||||
|
let duplicate_confirmed_state = DuplicateConfirmedState::new_from_state(
|
||||||
|
duplicate_confirmed_bank2_hash,
|
||||||
|
|| progress.is_dead(2).unwrap_or(false),
|
||||||
|
|| Some(our_bank2_hash),
|
||||||
|
);
|
||||||
check_slot_agrees_with_cluster(
|
check_slot_agrees_with_cluster(
|
||||||
2,
|
2,
|
||||||
bank_forks.read().unwrap().root(),
|
bank_forks.read().unwrap().root(),
|
||||||
Some(our_bank2_hash),
|
blockstore,
|
||||||
&mut duplicate_slots_tracker,
|
&mut duplicate_slots_tracker,
|
||||||
&gossip_duplicate_confirmed_slots,
|
|
||||||
progress,
|
|
||||||
heaviest_subtree_fork_choice,
|
heaviest_subtree_fork_choice,
|
||||||
&mut duplicate_slots_to_repair,
|
&mut duplicate_slots_to_repair,
|
||||||
SlotStateUpdate::DuplicateConfirmed,
|
SlotStateUpdate::DuplicateConfirmed(duplicate_confirmed_state),
|
||||||
);
|
);
|
||||||
assert!(duplicate_slots_to_repair.contains(&(2, duplicate_confirmed_bank2_hash)));
|
assert!(duplicate_slots_to_repair.contains(&(2, duplicate_confirmed_bank2_hash)));
|
||||||
let mut ancestors = bank_forks.read().unwrap().ancestors();
|
let mut ancestors = bank_forks.read().unwrap().ancestors();
|
||||||
|
@ -5613,7 +5659,7 @@ mod tests {
|
||||||
|
|
||||||
type GenerateVotes = Box<dyn Fn(Vec<Pubkey>) -> HashMap<Pubkey, Vec<Slot>>>;
|
type GenerateVotes = Box<dyn Fn(Vec<Pubkey>) -> HashMap<Pubkey, Vec<Slot>>>;
|
||||||
|
|
||||||
fn setup_forks_from_tree(
|
pub fn setup_forks_from_tree(
|
||||||
tree: Tree<Slot>,
|
tree: Tree<Slot>,
|
||||||
num_keys: usize,
|
num_keys: usize,
|
||||||
generate_votes: Option<GenerateVotes>,
|
generate_votes: Option<GenerateVotes>,
|
||||||
|
|
|
@ -104,6 +104,9 @@ impl VoteSimulator {
|
||||||
.any(|lockout| lockout.slot == parent));
|
.any(|lockout| lockout.slot == parent));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
while new_bank.tick_height() < new_bank.max_tick_height() {
|
||||||
|
new_bank.register_tick(&Hash::new_unique());
|
||||||
|
}
|
||||||
new_bank.freeze();
|
new_bank.freeze();
|
||||||
self.progress
|
self.progress
|
||||||
.get_fork_stats_mut(new_bank.slot())
|
.get_fork_stats_mut(new_bank.slot())
|
||||||
|
@ -324,7 +327,7 @@ pub fn initialize_state(
|
||||||
) -> (BankForks, ProgressMap, HeaviestSubtreeForkChoice) {
|
) -> (BankForks, ProgressMap, HeaviestSubtreeForkChoice) {
|
||||||
let validator_keypairs: Vec<_> = validator_keypairs_map.values().collect();
|
let validator_keypairs: Vec<_> = validator_keypairs_map.values().collect();
|
||||||
let GenesisConfigInfo {
|
let GenesisConfigInfo {
|
||||||
genesis_config,
|
mut genesis_config,
|
||||||
mint_keypair,
|
mint_keypair,
|
||||||
voting_keypair: _,
|
voting_keypair: _,
|
||||||
} = create_genesis_config_with_vote_accounts(
|
} = create_genesis_config_with_vote_accounts(
|
||||||
|
@ -333,12 +336,16 @@ pub fn initialize_state(
|
||||||
vec![stake; validator_keypairs.len()],
|
vec![stake; validator_keypairs.len()],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
genesis_config.poh_config.hashes_per_tick = Some(2);
|
||||||
let bank0 = Bank::new(&genesis_config);
|
let bank0 = Bank::new(&genesis_config);
|
||||||
|
|
||||||
for pubkey in validator_keypairs_map.keys() {
|
for pubkey in validator_keypairs_map.keys() {
|
||||||
bank0.transfer(10_000, &mint_keypair, pubkey).unwrap();
|
bank0.transfer(10_000, &mint_keypair, pubkey).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while bank0.tick_height() < bank0.max_tick_height() {
|
||||||
|
bank0.register_tick(&Hash::new_unique());
|
||||||
|
}
|
||||||
bank0.freeze();
|
bank0.freeze();
|
||||||
let mut progress = ProgressMap::default();
|
let mut progress = ProgressMap::default();
|
||||||
progress.insert(
|
progress.insert(
|
||||||
|
|
Loading…
Reference in New Issue