From 1a77486f8e8a5ec5d4c36ed61e7624db85f666a5 Mon Sep 17 00:00:00 2001 From: carllin Date: Mon, 20 May 2019 23:09:00 -0700 Subject: [PATCH] Make RootedSlotsIterator for traversing slots on the root fork (#4361) --- core/src/blocktree.rs | 7 ++ core/src/blocktree/rooted_slot_iterator.rs | 126 +++++++++++++++++++++ core/src/blocktree_processor.rs | 4 +- 3 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 core/src/blocktree/rooted_slot_iterator.rs diff --git a/core/src/blocktree.rs b/core/src/blocktree.rs index 3b66daf5c7..9a4ba89648 100644 --- a/core/src/blocktree.rs +++ b/core/src/blocktree.rs @@ -32,9 +32,11 @@ use std::sync::mpsc::{sync_channel, Receiver, SyncSender, TrySendError}; use std::sync::{Arc, RwLock}; pub use self::meta::*; +pub use self::rooted_slot_iterator::*; mod db; mod meta; +mod rooted_slot_iterator; macro_rules! db_imports { { $mod:ident, $db:ident, $db_path:expr } => { @@ -74,6 +76,7 @@ pub enum BlocktreeError { RocksDb(rocksdb::Error), #[cfg(feature = "kvstore")] KvsDb(kvstore::Error), + SlotNotRooted, } // ledger window @@ -182,6 +185,10 @@ impl Blocktree { self.orphans_cf.get(slot) } + pub fn rooted_slot_iterator<'a>(&'a self, slot: u64) -> Result> { + RootedSlotIterator::new(slot, self) + } + pub fn slot_meta_iterator(&self, slot: u64) -> Result> { let meta_iter = self.db.iter::(Some(slot))?; Ok(meta_iter.map(|(slot, slot_meta_bytes)| { diff --git a/core/src/blocktree/rooted_slot_iterator.rs b/core/src/blocktree/rooted_slot_iterator.rs new file mode 100644 index 0000000000..32f8365482 --- /dev/null +++ b/core/src/blocktree/rooted_slot_iterator.rs @@ -0,0 +1,126 @@ +use super::*; + +pub struct RootedSlotIterator<'a> { + next_slots: Vec, + blocktree: &'a super::Blocktree, +} + +impl<'a> RootedSlotIterator<'a> { + pub fn new(start_slot: u64, blocktree: &'a super::Blocktree) -> Result { + if blocktree.is_root(start_slot) { + Ok(Self { + next_slots: vec![start_slot], + blocktree, + }) + } else { + Err(Error::BlocktreeError(BlocktreeError::SlotNotRooted)) + } + } +} +impl<'a> Iterator for RootedSlotIterator<'a> { + type Item = (u64, super::SlotMeta); + + fn next(&mut self) -> Option { + // Clone b/c passing the closure to the map below requires exclusive access to + // `self`, which is borrowed here if we don't clone. + let rooted_slot = self + .next_slots + .iter() + .find(|x| self.blocktree.is_root(**x)) + .cloned(); + + rooted_slot.map(|rooted_slot| { + let slot_meta = self + .blocktree + .meta(rooted_slot) + .expect("Database failure, couldnt fetch SlotMeta") + .expect("SlotMeta in iterator didn't exist"); + + self.next_slots = slot_meta.next_slots.clone(); + (rooted_slot, slot_meta) + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::blocktree_processor::tests::fill_blocktree_slot_with_ticks; + + #[test] + fn test_rooted_slot_iterator() { + let blocktree_path = get_tmp_ledger_path("test_rooted_slot_iterator"); + let blocktree = Blocktree::open(&blocktree_path).unwrap(); + blocktree.set_root(0, 0).unwrap(); + let ticks_per_slot = 5; + /* + Build a blocktree in the ledger with the following fork structure: + + slot 0 + | + slot 1 <-- set_root(true) + / \ + 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_blocktree_slot_with_ticks( + &blocktree, + 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_blocktree_slot_with_ticks(&blocktree, ticks_per_slot, 4, fork_point, fork_hash); + + // Set a root + blocktree.set_root(3, 0).unwrap(); + + // Trying to get an iterator on a different fork will error + assert!(RootedSlotIterator::new(4, &blocktree).is_err()); + + // Trying to get an iterator on any slot on the root fork should succeed + let result: Vec<_> = RootedSlotIterator::new(3, &blocktree) + .unwrap() + .into_iter() + .map(|(slot, _)| slot) + .collect(); + let expected = vec![3]; + assert_eq!(result, expected); + + let result: Vec<_> = RootedSlotIterator::new(0, &blocktree) + .unwrap() + .into_iter() + .map(|(slot, _)| slot) + .collect(); + let expected = vec![0, 1, 2, 3]; + assert_eq!(result, expected); + + drop(blocktree); + Blocktree::destroy(&blocktree_path).expect("Expected successful database destruction"); + } +} diff --git a/core/src/blocktree_processor.rs b/core/src/blocktree_processor.rs index 1736553ddb..1ef8af0a98 100644 --- a/core/src/blocktree_processor.rs +++ b/core/src/blocktree_processor.rs @@ -281,7 +281,7 @@ pub fn process_blocktree( } #[cfg(test)] -mod tests { +pub mod tests { use super::*; use crate::blocktree::create_new_tmp_ledger; use crate::blocktree::tests::entries_to_blobs; @@ -295,7 +295,7 @@ mod tests { use solana_sdk::system_transaction; use solana_sdk::transaction::TransactionError; - fn fill_blocktree_slot_with_ticks( + pub fn fill_blocktree_slot_with_ticks( blocktree: &Blocktree, ticks_per_slot: u64, slot: u64,