4c7bb4984c
## Issue Addressed NA ## Primary Change When investigating memory usage, I noticed that retrieving a block from an early slot (e.g., slot 900) would cause a sharp increase in the memory footprint (from 400mb to 800mb+) which seemed to be ever-lasting. After some investigation, I found that the reverse iteration from the head back to that slot was the likely culprit. To counter this, I've switched the `BeaconChain::block_root_at_slot` to use the forwards iterator, instead of the reverse one. I also noticed that the networking stack is using `BeaconChain::root_at_slot` to check if a peer is relevant (`check_peer_relevance`). Perhaps the steep, seemingly-random-but-consistent increases in memory usage are caused by the use of this function. Using the forwards iterator with the HTTP API alleviated the sharp increases in memory usage. It also made the response much faster (before it felt like to took 1-2s, now it feels instant). ## Additional Changes In the process I also noticed that we have two functions for getting block roots: - `BeaconChain::block_root_at_slot`: returns `None` for a skip slot. - `BeaconChain::root_at_slot`: returns the previous root for a skip slot. I unified these two functions into `block_root_at_slot` and added the `WhenSlotSkipped` enum. Now, the caller must be explicit about the skip-slot behaviour when requesting a root. Additionally, I replaced `vec![]` with `Vec::with_capacity` in `store::chunked_vector::range_query`. I stumbled across this whilst debugging and made this modification to see what effect it would have (not much). It seems like a decent change to keep around, but I'm not concerned either way. Also, `BeaconChain::get_ancestor_block_root` is unused, so I got rid of it 🗑️. ## Additional Info I haven't also done the same for state roots here. Whilst it's possible and a good idea, it's more work since the fwds iterators are presently block-roots-specific. Whilst there's a few places a reverse iteration of state roots could be triggered (e.g., attestation production, HTTP API), they're no where near as common as the `check_peer_relevance` call. As such, I think we should get this PR merged first, then come back for the state root iters. I made an issue here https://github.com/sigp/lighthouse/issues/2377.
109 lines
3.9 KiB
Rust
109 lines
3.9 KiB
Rust
use beacon_chain::{BeaconChain, BeaconChainTypes, WhenSlotSkipped};
|
|
use eth2::types::BlockId as CoreBlockId;
|
|
use std::str::FromStr;
|
|
use types::{Hash256, SignedBeaconBlock, Slot};
|
|
|
|
/// Wraps `eth2::types::BlockId` and provides a simple way to obtain a block or root for a given
|
|
/// `BlockId`.
|
|
#[derive(Debug)]
|
|
pub struct BlockId(pub CoreBlockId);
|
|
|
|
impl BlockId {
|
|
pub fn from_slot(slot: Slot) -> Self {
|
|
Self(CoreBlockId::Slot(slot))
|
|
}
|
|
|
|
pub fn from_root(root: Hash256) -> Self {
|
|
Self(CoreBlockId::Root(root))
|
|
}
|
|
|
|
/// Return the block root identified by `self`.
|
|
pub fn root<T: BeaconChainTypes>(
|
|
&self,
|
|
chain: &BeaconChain<T>,
|
|
) -> Result<Hash256, warp::Rejection> {
|
|
match &self.0 {
|
|
CoreBlockId::Head => chain
|
|
.head_info()
|
|
.map(|head| head.block_root)
|
|
.map_err(warp_utils::reject::beacon_chain_error),
|
|
CoreBlockId::Genesis => Ok(chain.genesis_block_root),
|
|
CoreBlockId::Finalized => chain
|
|
.head_info()
|
|
.map(|head| head.finalized_checkpoint.root)
|
|
.map_err(warp_utils::reject::beacon_chain_error),
|
|
CoreBlockId::Justified => chain
|
|
.head_info()
|
|
.map(|head| head.current_justified_checkpoint.root)
|
|
.map_err(warp_utils::reject::beacon_chain_error),
|
|
CoreBlockId::Slot(slot) => chain
|
|
.block_root_at_slot(*slot, WhenSlotSkipped::None)
|
|
.map_err(warp_utils::reject::beacon_chain_error)
|
|
.and_then(|root_opt| {
|
|
root_opt.ok_or_else(|| {
|
|
warp_utils::reject::custom_not_found(format!(
|
|
"beacon block at slot {}",
|
|
slot
|
|
))
|
|
})
|
|
}),
|
|
CoreBlockId::Root(root) => Ok(*root),
|
|
}
|
|
}
|
|
|
|
/// Return the `SignedBeaconBlock` identified by `self`.
|
|
pub fn block<T: BeaconChainTypes>(
|
|
&self,
|
|
chain: &BeaconChain<T>,
|
|
) -> Result<SignedBeaconBlock<T::EthSpec>, warp::Rejection> {
|
|
match &self.0 {
|
|
CoreBlockId::Head => chain
|
|
.head_beacon_block()
|
|
.map_err(warp_utils::reject::beacon_chain_error),
|
|
CoreBlockId::Slot(slot) => {
|
|
let root = self.root(chain)?;
|
|
chain
|
|
.get_block(&root)
|
|
.map_err(warp_utils::reject::beacon_chain_error)
|
|
.and_then(|block_opt| match block_opt {
|
|
Some(block) => {
|
|
if block.slot() != *slot {
|
|
return Err(warp_utils::reject::custom_not_found(format!(
|
|
"slot {} was skipped",
|
|
slot
|
|
)));
|
|
}
|
|
Ok(block)
|
|
}
|
|
None => Err(warp_utils::reject::custom_not_found(format!(
|
|
"beacon block with root {}",
|
|
root
|
|
))),
|
|
})
|
|
}
|
|
_ => {
|
|
let root = self.root(chain)?;
|
|
chain
|
|
.get_block(&root)
|
|
.map_err(warp_utils::reject::beacon_chain_error)
|
|
.and_then(|root_opt| {
|
|
root_opt.ok_or_else(|| {
|
|
warp_utils::reject::custom_not_found(format!(
|
|
"beacon block with root {}",
|
|
root
|
|
))
|
|
})
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FromStr for BlockId {
|
|
type Err = String;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
CoreBlockId::from_str(s).map(Self)
|
|
}
|
|
}
|