diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index a06b31f72..2ea26d1b7 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -798,6 +798,8 @@ fn load_bank_forks( }; if let Some(halt_slot) = process_options.halt_at_slot { + // Check if we have the slot data necessary to replay from starting_slot to halt_slot. + // - This will not catch the case when loading from genesis without a full slot 0. if !blockstore.slots_connected(starting_slot, halt_slot) { eprintln!( "Unable to load bank forks at slot {} due to disconnected blocks.", diff --git a/ledger/src/blockstore.rs b/ledger/src/blockstore.rs index d9305133a..01e3bfb2a 100644 --- a/ledger/src/blockstore.rs +++ b/ledger/src/blockstore.rs @@ -547,9 +547,17 @@ impl Blockstore { self.prepare_rooted_slot_iterator(slot, IteratorDirection::Reverse) } - /// Determines if starting_slot and ending_slot are connected + /// Determines if `starting_slot` and `ending_slot` are connected by full slots + /// `starting_slot` is excluded from the `is_full()` check pub fn slots_connected(&self, starting_slot: Slot, ending_slot: Slot) -> bool { - let mut next_slots: VecDeque<_> = vec![starting_slot].into(); + if starting_slot == ending_slot { + return true; + } + + let mut next_slots: VecDeque<_> = match self.meta(starting_slot) { + Ok(Some(starting_slot_meta)) => starting_slot_meta.next_slots.into(), + _ => return false, + }; while let Some(slot) = next_slots.pop_front() { if let Ok(Some(slot_meta)) = self.meta(slot) { if slot_meta.is_full() { @@ -4354,6 +4362,16 @@ pub mod tests { entries } + fn make_and_insert_slot(blockstore: &Blockstore, slot: Slot, parent_slot: Slot) { + let (shreds, _) = make_slot_entries(slot, parent_slot, 100); + blockstore.insert_shreds(shreds, None, true).unwrap(); + + let meta = blockstore.meta(slot).unwrap().unwrap(); + assert_eq!(slot, meta.slot); + assert!(meta.is_full()); + assert!(meta.next_slots.is_empty()); + } + #[test] fn test_create_new_ledger() { solana_logger::setup(); @@ -5489,13 +5507,7 @@ pub mod tests { let num_slots = 3; for slot in 1..=num_slots { - let (shreds, _) = make_slot_entries(slot, slot.saturating_sub(1), 100); - blockstore.insert_shreds(shreds, None, true).unwrap(); - - let meta = blockstore.meta(slot).unwrap().unwrap(); - assert_eq!(slot, meta.slot); - assert!(meta.is_full()); - assert!(meta.next_slots.is_empty()); + make_and_insert_slot(&blockstore, slot, slot.saturating_sub(1)); } assert!(blockstore.slots_connected(1, 3)); @@ -5507,16 +5519,6 @@ pub mod tests { let ledger_path = get_tmp_ledger_path_auto_delete!(); let blockstore = Blockstore::open(ledger_path.path()).unwrap(); - fn make_and_insert_slot(blockstore: &Blockstore, slot: Slot, parent_slot: Slot) { - let (shreds, _) = make_slot_entries(slot, parent_slot, 100); - blockstore.insert_shreds(shreds, None, true).unwrap(); - - let meta = blockstore.meta(slot).unwrap().unwrap(); - assert_eq!(slot, meta.slot); - assert!(meta.is_full()); - assert!(meta.next_slots.is_empty()); - } - make_and_insert_slot(&blockstore, 1, 0); make_and_insert_slot(&blockstore, 2, 1); make_and_insert_slot(&blockstore, 4, 2); @@ -5525,6 +5527,26 @@ pub mod tests { assert!(blockstore.slots_connected(1, 4)); } + #[test] + fn test_slots_connected_same_slot() { + let ledger_path = get_tmp_ledger_path_auto_delete!(); + let blockstore = Blockstore::open(ledger_path.path()).unwrap(); + + assert!(blockstore.slots_connected(54, 54)); + } + + #[test] + fn test_slots_connected_starting_slot_not_full() { + let ledger_path = get_tmp_ledger_path_auto_delete!(); + let blockstore = Blockstore::open(ledger_path.path()).unwrap(); + + make_and_insert_slot(&blockstore, 5, 4); + make_and_insert_slot(&blockstore, 6, 5); + + assert!(!blockstore.meta(4).unwrap().unwrap().is_full()); + assert!(blockstore.slots_connected(4, 6)); + } + #[test] fn test_get_slots_since() { let ledger_path = get_tmp_ledger_path_auto_delete!();