Skip to content

Commit

Permalink
Allow for skipping state roots in block replay
Browse files Browse the repository at this point in the history
  • Loading branch information
paulhauner committed Aug 15, 2020
1 parent 6aeb896 commit 8113ce6
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 9 deletions.
7 changes: 6 additions & 1 deletion beacon_node/beacon_chain/src/attestation_verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -775,7 +775,12 @@ where
metrics::start_timer(&metrics::ATTESTATION_PROCESSING_STATE_READ_TIMES);

let mut state = chain
.get_state(&target_block.state_root, Some(target_block.slot))?
.store
.get_inconsistent_state_for_attestation_verification_only(
&target_block.state_root,
Some(target_block.slot),
)
.map_err(BeaconChainError::from)?
.ok_or_else(|| BeaconChainError::MissingBeaconState(target_block.state_root))?;

metrics::stop_timer(state_read_timer);
Expand Down
52 changes: 44 additions & 8 deletions beacon_node/store/src/hot_cold_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ use types::*;
/// 32-byte key for accessing the `split` of the freezer DB.
pub const SPLIT_DB_KEY: &str = "FREEZERDBSPLITFREEZERDBSPLITFREE";

pub enum BlockReplay {
Accurate,
InconsistentStateRoots,
}

/// On-disk database that stores finalized states efficiently.
///
/// Stores vector fields like the `block_roots` and `state_roots` separately, and only stores
Expand Down Expand Up @@ -230,16 +235,30 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
// chain. This way we avoid returning a state that doesn't match `state_root`.
self.load_cold_state(state_root)
} else {
self.load_hot_state(state_root)
self.load_hot_state(state_root, BlockReplay::Accurate)
}
} else {
match self.load_hot_state(state_root)? {
match self.load_hot_state(state_root, BlockReplay::Accurate)? {
Some(state) => Ok(Some(state)),
None => self.load_cold_state(state_root),
}
}
}

pub fn get_inconsistent_state_for_attestation_verification_only(
&self,
state_root: &Hash256,
slot: Option<Slot>,
) -> Result<Option<BeaconState<E>>, Error> {
metrics::inc_counter(&metrics::BEACON_STATE_GET_COUNT);

if slot.map_or(false, |slot| slot < self.get_split_slot()) {
Ok(None)
} else {
self.load_hot_state(state_root, BlockReplay::InconsistentStateRoots)
}
}

/// Delete a state, ensuring it is removed from the LRU cache, as well as from on-disk.
///
/// It is assumed that all states being deleted reside in the hot DB, even if their slot is less
Expand Down Expand Up @@ -283,8 +302,11 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
}) = self.load_hot_state_summary(state_root)?
{
// NOTE: minor inefficiency here because we load an unnecessary hot state summary
//
// `BlockReplay` should be irrelevant here since we never replay blocks for an epoch
// boundary state in the hot DB.
let state = self
.load_hot_state(&epoch_boundary_state_root)?
.load_hot_state(&epoch_boundary_state_root, BlockReplay::Accurate)?
.ok_or_else(|| {
HotColdDBError::MissingEpochBoundaryState(epoch_boundary_state_root)
})?;
Expand Down Expand Up @@ -415,7 +437,11 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
/// Load a post-finalization state from the hot database.
///
/// Will replay blocks from the nearest epoch boundary.
pub fn load_hot_state(&self, state_root: &Hash256) -> Result<Option<BeaconState<E>>, Error> {
pub fn load_hot_state(
&self,
state_root: &Hash256,
block_replay: BlockReplay,
) -> Result<Option<BeaconState<E>>, Error> {
metrics::inc_counter(&metrics::BEACON_STATE_HOT_GET_COUNT);

if let Some(HotStateSummary {
Expand All @@ -436,7 +462,7 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
} else {
let blocks =
self.load_blocks_to_replay(boundary_state.slot, slot, latest_block_root)?;
self.replay_blocks(boundary_state, blocks, slot)?
self.replay_blocks(boundary_state, blocks, slot, block_replay)?
};

Ok(Some(state))
Expand Down Expand Up @@ -567,7 +593,7 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
)?;

// 3. Replay the blocks on top of the low restore point.
self.replay_blocks(low_restore_point, blocks, slot)
self.replay_blocks(low_restore_point, blocks, slot, BlockReplay::Accurate)
}

/// Get a suitable block root for backtracking from `high_restore_point` to the state at `slot`.
Expand Down Expand Up @@ -626,6 +652,7 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
mut state: BeaconState<E>,
blocks: Vec<SignedBeaconBlock<E>>,
target_slot: Slot,
block_replay: BlockReplay,
) -> Result<BeaconState<E>, Error> {
let state_root_from_prev_block = |i: usize, state: &BeaconState<E>| {
if i > 0 {
Expand All @@ -650,18 +677,27 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
per_slot_processing(&mut state, state_root, &self.spec)
.map_err(HotColdDBError::BlockReplaySlotError)?;
}

let state_root = match block_replay {
BlockReplay::Accurate => None,
BlockReplay::InconsistentStateRoots => Some(Hash256::zero()),
};

per_block_processing(
&mut state,
&block,
None,
state_root,
BlockSignatureStrategy::NoVerification,
&self.spec,
)
.map_err(HotColdDBError::BlockReplayBlockError)?;
}

while state.slot < target_slot {
let state_root = state_root_from_prev_block(blocks.len(), &state);
let state_root = match block_replay {
BlockReplay::Accurate => state_root_from_prev_block(blocks.len(), &state),
BlockReplay::InconsistentStateRoots => Some(Hash256::zero()),
};
per_slot_processing(&mut state, state_root, &self.spec)
.map_err(HotColdDBError::BlockReplaySlotError)?;
}
Expand Down

0 comments on commit 8113ce6

Please sign in to comment.