From 77b8de193cd08560daa38b6afa4aff98628f2624 Mon Sep 17 00:00:00 2001 From: carllin Date: Tue, 23 Jun 2020 12:05:00 -0700 Subject: [PATCH] Add utility functions for testing (#10749) * Add ancestor iterator * Add blockstore generation from trees Co-authored-by: Carl --- Cargo.lock | 1 + ledger/Cargo.toml | 1 + ledger/src/ancestor_iterator.rs | 116 ++++++++++++++++++++++++++++++++ ledger/src/blockstore.rs | 18 +++++ 4 files changed, 136 insertions(+) create mode 100644 ledger/src/ancestor_iterator.rs diff --git a/Cargo.lock b/Cargo.lock index 6d6c5da087..62bb297442 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4199,6 +4199,7 @@ dependencies = [ "solana-vote-program", "tempfile", "thiserror", + "trees", ] [[package]] diff --git a/ledger/Cargo.toml b/ledger/Cargo.toml index ecc83e175e..a1ac7ec079 100644 --- a/ledger/Cargo.toml +++ b/ledger/Cargo.toml @@ -43,6 +43,7 @@ solana-stake-program = { path = "../programs/stake", version = "1.3.0" } solana-vote-program = { path = "../programs/vote", version = "1.3.0" } tempfile = "3.1.0" thiserror = "1.0" +trees = "0.2.1" [dependencies.rocksdb] # Avoid the vendored bzip2 within rocksdb-sys that can cause linker conflicts diff --git a/ledger/src/ancestor_iterator.rs b/ledger/src/ancestor_iterator.rs new file mode 100644 index 0000000000..ee61f9ddd7 --- /dev/null +++ b/ledger/src/ancestor_iterator.rs @@ -0,0 +1,116 @@ +use crate::blockstore::*; +use solana_sdk::clock::Slot; + +pub struct AncestorIterator<'a> { + current: Option, + blockstore: &'a Blockstore, +} + +impl<'a> AncestorIterator<'a> { + pub fn new(start_slot: Slot, blockstore: &'a Blockstore) -> Self { + let current = blockstore.meta(start_slot).unwrap().and_then(|slot_meta| { + if slot_meta.is_parent_set() && start_slot != 0 { + Some(slot_meta.parent_slot) + } else { + None + } + }); + Self { + current, + blockstore, + } + } +} +impl<'a> Iterator for AncestorIterator<'a> { + type Item = Slot; + + fn next(&mut self) -> Option { + let current = self.current.clone(); + current.map(|slot| { + if slot != 0 { + self.current = self.blockstore.meta(slot).unwrap().and_then(|slot_meta| { + if slot_meta.is_parent_set() { + Some(slot_meta.parent_slot) + } else { + None + } + }); + } else { + self.current = None; + } + slot + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::blockstore_processor::fill_blockstore_slot_with_ticks; + use solana_sdk::hash::Hash; + + #[test] + fn test_ancestor_iterator() { + let blockstore_path = get_tmp_ledger_path!(); + let blockstore = Blockstore::open(&blockstore_path).unwrap(); + blockstore.set_roots(&[0]).unwrap(); + let ticks_per_slot = 5; + + /* + Build a blockstore in the ledger with the following fork structure: + + slot 0 + | + slot 1 + / \ + slot 2 | + / | + slot 3 | + | + slot 4 + + */ + + // Fork 1, ending at slot 3 + let last_entry_hash = Hash::default(); + let fork_point = 1; + let mut fork_hash = Hash::default(); + for slot in 0..=3 { + let parent = { + if slot == 0 { + 0 + } else { + slot - 1 + } + }; + let last_entry_hash = fill_blockstore_slot_with_ticks( + &blockstore, + ticks_per_slot, + slot, + parent, + last_entry_hash, + ); + + if slot == fork_point { + fork_hash = last_entry_hash; + } + } + + // Fork 2, ending at slot 4 + let _ = + fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 4, fork_point, fork_hash); + + // Test correctness + assert!(AncestorIterator::new(0, &blockstore) + .collect::>() + .is_empty()); + assert_eq!( + AncestorIterator::new(4, &blockstore).collect::>(), + vec![1, 0] + ); + assert_eq!( + AncestorIterator::new(3, &blockstore).collect::>(), + vec![2, 1, 0] + ); + } +} diff --git a/ledger/src/blockstore.rs b/ledger/src/blockstore.rs index d703a4efb5..e380cbbc2a 100644 --- a/ledger/src/blockstore.rs +++ b/ledger/src/blockstore.rs @@ -55,6 +55,7 @@ use std::{ }, time::Duration, }; +use trees::{Tree, TreeWalk}; pub mod blockstore_purge; @@ -303,6 +304,23 @@ impl Blockstore { Ok((blockstore, signal_receiver, completed_slots_receiver)) } + pub fn add_tree(&self, forks: Tree, is_orphan: bool) { + let mut walk = TreeWalk::from(forks); + while let Some(visit) = walk.get() { + let slot = visit.node().data; + if self.meta(slot).unwrap().is_some() { + walk.forward(); + continue; + } + let parent = walk.get_parent().map(|n| n.data); + if parent.is_some() || !is_orphan { + let (shreds, _) = make_slot_entries(slot, parent.unwrap_or(slot), 1); + self.insert_shreds(shreds, None, false).unwrap(); + } + walk.forward(); + } + } + pub fn set_no_compaction(&mut self, no_compaction: bool) { self.no_compaction = no_compaction; }