diff --git a/beacon_node/beacon_chain/src/attestation_verification.rs b/beacon_node/beacon_chain/src/attestation_verification.rs index aa24d08b2d6..9c8ec92e596 100644 --- a/beacon_node/beacon_chain/src/attestation_verification.rs +++ b/beacon_node/beacon_chain/src/attestation_verification.rs @@ -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); diff --git a/beacon_node/store/src/hot_cold_store.rs b/beacon_node/store/src/hot_cold_store.rs index 7bad9ef1567..bf81b67ddf0 100644 --- a/beacon_node/store/src/hot_cold_store.rs +++ b/beacon_node/store/src/hot_cold_store.rs @@ -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 @@ -230,16 +235,30 @@ impl, Cold: ItemStore> HotColdDB // 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, + ) -> Result>, 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 @@ -283,8 +302,11 @@ impl, Cold: ItemStore> HotColdDB }) = 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) })?; @@ -415,7 +437,11 @@ impl, Cold: ItemStore> HotColdDB /// 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>, Error> { + pub fn load_hot_state( + &self, + state_root: &Hash256, + block_replay: BlockReplay, + ) -> Result>, Error> { metrics::inc_counter(&metrics::BEACON_STATE_HOT_GET_COUNT); if let Some(HotStateSummary { @@ -436,7 +462,7 @@ impl, Cold: ItemStore> HotColdDB } 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)) @@ -567,7 +593,7 @@ impl, Cold: ItemStore> HotColdDB )?; // 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`. @@ -626,6 +652,7 @@ impl, Cold: ItemStore> HotColdDB mut state: BeaconState, blocks: Vec>, target_slot: Slot, + block_replay: BlockReplay, ) -> Result, Error> { let state_root_from_prev_block = |i: usize, state: &BeaconState| { if i > 0 { @@ -650,10 +677,16 @@ impl, Cold: ItemStore> HotColdDB 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, ) @@ -661,7 +694,10 @@ impl, Cold: ItemStore> HotColdDB } 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)?; }