diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index eb8df6f2a..d5fd113a8 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -142,7 +142,38 @@ where }) } - /// Returns beacon block roots for `count` slots, starting from `start_slot`. + /// Returns the beacon block body for each beacon block root in `roots`. + /// + /// Fails if any root in `roots` does not have a corresponding block. + pub fn get_block_bodies(&self, roots: &[Hash256]) -> Result, Error> { + let bodies: Result, _> = roots + .iter() + .map(|root| match self.get_block(root)? { + Some(block) => Ok(block.body), + None => Err(Error::DBInconsistent("Missing block".into())), + }) + .collect(); + + Ok(bodies?) + } + + /// Returns the beacon block header for each beacon block root in `roots`. + /// + /// Fails if any root in `roots` does not have a corresponding block. + pub fn get_block_headers(&self, roots: &[Hash256]) -> Result, Error> { + let headers: Result, _> = roots + .iter() + .map(|root| match self.get_block(root)? { + Some(block) => Ok(block.block_header()), + None => Err(Error::DBInconsistent("Missing block".into())), + }) + .collect(); + + Ok(headers?) + } + + /// Returns `count `beacon block roots, starting from `start_slot` with an + /// interval of `skip` slots between each root. /// /// ## Errors: /// @@ -152,40 +183,45 @@ where /// - Other: BeaconState` is inconsistent. pub fn get_block_roots( &self, - start_slot: Slot, - count: Slot, - ) -> Result, BeaconStateError> { + earliest_slot: Slot, + count: usize, + skip: usize, + ) -> Result, Error> { let spec = &self.spec; + let step_by = Slot::from(skip + 1); let mut roots: Vec = vec![]; + + // The state for reading block roots. Will be updated with an older state if slots go too + // far back in history. let mut state = self.state.read().clone(); - let mut slot = start_slot + count - 1; + + // The final slot in this series, will be reduced by `skip` each loop iteration. + let mut slot = earliest_slot + Slot::from(count * (skip + 1)) - 1; + + // If the highest slot requested is that of the current state insert the root of the + // head block, unless the head block's slot is not matching. + if slot == state.slot && self.head().beacon_block.slot == slot { + roots.push(self.head().beacon_block_root); + + slot -= step_by; + } else if slot >= state.slot { + return Err(BeaconStateError::SlotOutOfBounds.into()); + } loop { - // If the highest slot requested is that of the current state insert the root of the - // head block, unless the head block's slot is not matching. - if slot == state.slot && self.head().beacon_block.slot == slot { - roots.push(self.head().beacon_block_root); - - slot -= 1; - continue; - } else if slot >= state.slot { - return Err(BeaconStateError::SlotOutOfBounds); - } - // If the slot is within the range of the current state's block roots, append the root // to the output vec. // - // If we get `SlotOutOfBounds` error, load the oldest known state to the present state - // from the DB. + // If we get `SlotOutOfBounds` error, load the oldest available historic + // state from the DB. match state.get_block_root(slot, spec) { Ok(root) => { - roots.push(*root); - - if slot == start_slot { + if slot < earliest_slot { break; } else { - slot -= 1; + roots.push(*root); + slot -= step_by; } } Err(BeaconStateError::SlotOutOfBounds) => { @@ -201,18 +237,19 @@ where _ => break, } } - Err(e) => return Err(e), + Err(e) => return Err(e.into()), }; } - if (slot == start_slot) && (roots.len() == count.as_usize()) { + // Return the results if they pass a sanity check. + if (slot <= earliest_slot) && (roots.len() == count) { // Reverse the ordering of the roots. We extracted them in reverse order to make it // simpler to lookup historic states. // // This is a potential optimisation target. Ok(roots.iter().rev().cloned().collect()) } else { - Err(BeaconStateError::SlotOutOfBounds) + Err(BeaconStateError::SlotOutOfBounds.into()) } } diff --git a/beacon_node/network/src/beacon_chain.rs b/beacon_node/network/src/beacon_chain.rs index bb4e8e71e..cc54e8ae0 100644 --- a/beacon_node/network/src/beacon_chain.rs +++ b/beacon_node/network/src/beacon_chain.rs @@ -8,7 +8,7 @@ use beacon_chain::{ CheckPoint, }; use eth2_libp2p::HelloMessage; -use types::{BeaconBlock, BeaconStateError, Epoch, Hash256, Slot}; +use types::{BeaconBlock, BeaconBlockBody, BeaconBlockHeader, Epoch, Hash256, Slot}; pub use beacon_chain::{BeaconChainError, BlockProcessingOutcome}; @@ -40,8 +40,19 @@ pub trait BeaconChain: Send + Sync { fn get_block_roots( &self, start_slot: Slot, - count: Slot, - ) -> Result, BeaconStateError>; + count: usize, + skip: usize, + ) -> Result, BeaconChainError>; + + fn get_block_headers( + &self, + start_slot: Slot, + count: usize, + skip: usize, + ) -> Result, BeaconChainError>; + + fn get_block_bodies(&self, roots: &[Hash256]) + -> Result, BeaconChainError>; fn is_new_block_root(&self, beacon_block_root: &Hash256) -> Result; } @@ -111,9 +122,27 @@ where fn get_block_roots( &self, start_slot: Slot, - count: Slot, - ) -> Result, BeaconStateError> { - self.get_block_roots(start_slot, count) + count: usize, + skip: usize, + ) -> Result, BeaconChainError> { + self.get_block_roots(start_slot, count, skip) + } + + fn get_block_headers( + &self, + start_slot: Slot, + count: usize, + skip: usize, + ) -> Result, BeaconChainError> { + let roots = self.get_block_roots(start_slot, count, skip)?; + self.get_block_headers(&roots) + } + + fn get_block_bodies( + &self, + roots: &[Hash256], + ) -> Result, BeaconChainError> { + self.get_block_bodies(roots) } fn is_new_block_root(&self, beacon_block_root: &Hash256) -> Result {