diff --git a/beacon_node/beacon_chain/src/attester_cache.rs b/beacon_node/beacon_chain/src/attester_cache.rs index 01662efc135..5ce0f591ccb 100644 --- a/beacon_node/beacon_chain/src/attester_cache.rs +++ b/beacon_node/beacon_chain/src/attester_cache.rs @@ -278,10 +278,14 @@ impl AttesterCache { Ok(()) } - /// Read the state identified by `state_root` from the database, advance it to the required + /// Read the state identified by `state_root` from the database*, advance it to the required /// slot, use it to prime the cache and return the values for the provided `slot` and /// `committee_index`. /// + /// *: The database read is avoided if `state_opt.is_some()`. + /// + /// If `state_opt.is_some()`, the `state_root` *must* match that state. + /// /// ## Notes /// /// This function takes a write-lock on the internal cache. Prefer attempting a `Self::get` call @@ -290,6 +294,7 @@ impl AttesterCache { pub fn load_and_cache_state( &self, state_root: Hash256, + state_opt: Option>, key: AttesterCacheKey, slot: Slot, committee_index: CommitteeIndex, @@ -316,9 +321,13 @@ impl AttesterCache { return Ok(value); } - let mut state: BeaconState = chain - .get_state(&state_root, None)? - .ok_or(Error::MissingBeaconState(state_root))?; + let mut state = if let Some(state) = state_opt { + state + } else { + chain + .get_state(&state_root, None)? + .ok_or(Error::MissingBeaconState(state_root))? + }; if state.slot() > slot { // This indicates an internal inconsistency. diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index da21e264295..c25ba2bd94c 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -1248,6 +1248,7 @@ impl BeaconChain { let beacon_state_root; let target; let current_epoch_attesting_info: Option<(Checkpoint, usize)>; + let head_state_clone: Option>>; let attester_cache_key; let head_timer = metrics::start_timer(&metrics::ATTESTATION_PRODUCTION_HEAD_SCRAPE_SECONDS); if let Some(head) = self.canonical_head.try_read_for(HEAD_LOCK_TIMEOUT) { @@ -1305,21 +1306,41 @@ impl BeaconChain { root: target_root, }; - current_epoch_attesting_info = if head_state.current_epoch() == request_epoch { - // When the head state is in the same epoch as the request, all the information - // required to attest is available on the head state. - Some(( - head_state.current_justified_checkpoint(), - head_state - .get_beacon_committee(request_slot, request_index)? - .committee - .len(), - )) - } else { - // If the head state is in a *different* epoch to the request, more work is required - // to determine the justified checkpoint and committee length. - None - }; + match request_epoch.cmp(&head_state.current_epoch()) { + // The request is in the same epoch as the head state. + Ordering::Equal => { + // When the head state is in the same epoch as the request, all the information + // required to attest is available on the head state. + current_epoch_attesting_info = Some(( + head_state.current_justified_checkpoint(), + head_state + .get_beacon_committee(request_slot, request_index)? + .committee + .len(), + )); + // There is no need to clone the head state, all required information has + // already been obtained. + head_state_clone = None; + } + // The request is in a *later* epoch than the head state. + Ordering::Greater => { + // The justified checkpoint in the head state is not useful in this scenario. + current_epoch_attesting_info = None; + // The head state *is* useful in this this scenario because we can advance it + // into the required epoch. + head_state_clone = Some(Box::new( + head_state.clone_with(CloneConfig::committee_caches_only()), + )); + } + // The request is in a *earlier* epoch than the head state. + Ordering::Less => { + // The justified checkpoint in the head state is not useful in this scenario. + current_epoch_attesting_info = None; + // The head state is not useful in this scenario, we must load an older one from + // disk. + head_state_clone = None; + } + } // Determine the key for `self.attester_cache`, in case it is required later in this // routine. @@ -1369,6 +1390,7 @@ impl BeaconChain { metrics::start_timer(&metrics::ATTESTATION_PRODUCTION_CACHE_PRIME_SECONDS); self.attester_cache.load_and_cache_state( beacon_state_root, + head_state_clone.map(|boxed| *boxed), attester_cache_key, request_slot, request_index,