diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index e8fa19ac6..13aad8112 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -2,6 +2,7 @@ use crate::checkpoint::CheckPoint; use crate::errors::{BeaconChainError as Error, BlockProductionError}; use crate::metrics::Metrics; use crate::persisted_beacon_chain::{PersistedBeaconChain, BEACON_CHAIN_DB_KEY}; +use crate::iter::BlockRootsIterator; use fork_choice::{ForkChoice, ForkChoiceError}; use log::{debug, trace}; use operation_pool::DepositInsertStatus; @@ -226,6 +227,17 @@ impl BeaconChain { Ok(headers?) } + /// Iterate in reverse through all block roots starting from the current state, through to + /// genesis. + /// + /// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. + /// + /// Contains duplicate roots when skip slots are encountered. + pub fn iter_block_roots(&self) -> BlockRootsIterator { + BlockRootsIterator::from_state(self.store.clone(), self.state.read().clone()) + } + + /* /// Returns `count `beacon block roots, starting from `start_slot` with an /// interval of `skip` slots between each root. /// @@ -305,6 +317,7 @@ impl BeaconChain { Err(BeaconStateError::SlotOutOfBounds.into()) } } + */ /// Returns the block at the given root, if any. /// diff --git a/beacon_node/beacon_chain/src/iter.rs b/beacon_node/beacon_chain/src/iter.rs new file mode 100644 index 000000000..da81a6582 --- /dev/null +++ b/beacon_node/beacon_chain/src/iter.rs @@ -0,0 +1,106 @@ +use std::sync::Arc; +use store::Store; +use types::{BeaconState, BeaconStateError, EthSpec, Hash256, Slot}; + +/// Iterates backwards through block roots. +/// +/// Uses the `latest_block_roots` field of `BeaconState` to as the source of block roots and will +/// perform a lookup on the `Store` for a prior `BeaconState` if `latest_block_roots` has been +/// exhausted. +/// +/// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. +pub struct BlockRootsIterator { + store: Arc, + beacon_state: BeaconState, + slot: Slot, +} + +impl BlockRootsIterator { + /// Create a new iterator over all block roots in the given `beacon_state` and prior states. + pub fn from_state(store: Arc, beacon_state: BeaconState) -> Self { + Self { + slot: beacon_state.slot, + beacon_state, + store, + } + } +} + +impl Iterator for BlockRootsIterator { + type Item = Hash256; + + fn next(&mut self) -> Option { + if self.slot == 0 { + return None; + } + + let slot = self.slot - 1; + + match self.beacon_state.get_block_root(slot) { + Ok(root) => Some(*root), + Err(BeaconStateError::SlotOutOfBounds) => { + // Read a `BeaconState` from the store that has access to prior historical root. + self.beacon_state = { + // Read the earliest historic state in the current slot. + let earliest_historic_slot = + self.beacon_state.slot - Slot::from(T::slots_per_historical_root()); + + // Load the earlier state from disk. + let new_state_root = self + .beacon_state + .get_state_root(earliest_historic_slot) + .ok()?; + + let state_option = self.store.get(&new_state_root).ok()?; + state_option? + }; + + self.beacon_state.get_block_root(slot).ok().cloned() + } + _ => return None, + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use store::MemoryStore; + use types::{test_utils::TestingBeaconStateBuilder, FoundationEthSpec, Keypair}; + + fn get_state() -> BeaconState { + let builder = + TestingBeaconStateBuilder::from_single_keypair(0, &Keypair::random(), &T::spec()); + let (state, _keypairs) = builder.build(); + state + } + + #[test] + fn root_iter() { + let store = Arc::new(MemoryStore::open()); + + let mut state_a: BeaconState = get_state(); + let mut state_b: BeaconState = get_state(); + + let mut hashes = (0..).into_iter().map(|i| Hash256::from(i)); + + for root in &mut state_a.latest_block_roots[..] { + *root = hashes.next().unwrap() + } + for root in &mut state_b.latest_block_roots[..] { + *root = hashes.next().unwrap() + } + + let state_a_root = hashes.next().unwrap(); + state_b.latest_state_roots[0] = state_a_root; + store.put(&state_a_root, &state_a).unwrap(); + + let iter = BlockRootsIterator::from_state(store.clone(), state_b.clone()); + let mut collected: Vec = iter.collect(); + collected.reverse(); + + for (i, item) in collected.iter().enumerate() { + assert_eq!(*item, Hash256::from(i as u64)); + } + } +} diff --git a/beacon_node/beacon_chain/src/lib.rs b/beacon_node/beacon_chain/src/lib.rs index c80dc4715..bde541fce 100644 --- a/beacon_node/beacon_chain/src/lib.rs +++ b/beacon_node/beacon_chain/src/lib.rs @@ -1,6 +1,7 @@ mod beacon_chain; mod checkpoint; mod errors; +pub mod iter; mod metrics; mod persisted_beacon_chain;