mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-30 12:47:05 +00:00
Centralise Gloas boundary skip in CachedPTCs::try_from_state
CachedPTCs::try_from_state now returns Result<Option<Self>, _> and internalises the boundary rule (pre-Gloas state, Gloas shuffling epoch => Ok(None)). Callers (block import priming, state advance timer, with_cached_shuffling miss path) just skip insertion on None instead of duplicating the guard. The unit test exercises the three boundary cases against a pre-Gloas state.
This commit is contained in:
@@ -4909,27 +4909,18 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let shuffling_id = AttestationShufflingId::new(block_root, state, relative_epoch)?;
|
||||
let shuffling_epoch = relative_epoch.into_epoch(state.current_epoch());
|
||||
|
||||
let shuffling_is_cached = self.shuffling_cache.read().contains(&shuffling_id);
|
||||
|
||||
// Skip priming the cache for `shuffling_epoch` if it is Gloas but the state is not:
|
||||
// we do not have the PTCs on hand in this case.
|
||||
if self
|
||||
.spec
|
||||
.fork_name_at_epoch(shuffling_epoch)
|
||||
.gloas_enabled()
|
||||
&& !state.fork_name_unchecked().gloas_enabled()
|
||||
{
|
||||
if self.shuffling_cache.read().contains(&shuffling_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if !shuffling_is_cached {
|
||||
state.build_committee_cache(relative_epoch, &self.spec)?;
|
||||
let committee_cache = state.committee_cache(relative_epoch)?;
|
||||
let ptcs = CachedPTCs::from_state(state, shuffling_epoch, &self.spec)?;
|
||||
let cached_shuffling = CachedShuffling::new(committee_cache.clone(), ptcs);
|
||||
self.shuffling_cache
|
||||
.write()
|
||||
.insert_committee_cache(shuffling_id, cached_shuffling);
|
||||
state.build_committee_cache(relative_epoch, &self.spec)?;
|
||||
let committee_cache = state.committee_cache(relative_epoch)?.clone();
|
||||
|
||||
if let Some(ptcs) = CachedPTCs::try_from_state(state, shuffling_epoch, &self.spec)? {
|
||||
self.shuffling_cache.write().insert_committee_cache(
|
||||
shuffling_id,
|
||||
CachedShuffling::new(committee_cache, ptcs),
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -7,8 +7,8 @@ use parking_lot::RwLock;
|
||||
use state_processing::state_advance::partial_state_advance;
|
||||
use tracing::debug;
|
||||
use types::{
|
||||
AttestationShufflingId, BeaconState, ChainSpec, Epoch, EthSpec, Hash256, PTC, RelativeEpoch,
|
||||
Slot, state::CommitteeCache,
|
||||
AttestationShufflingId, BeaconState, BeaconStateError, ChainSpec, Epoch, EthSpec, Hash256, PTC,
|
||||
RelativeEpoch, Slot, state::CommitteeCache,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -47,19 +47,24 @@ pub enum CachedPTCs<E: EthSpec> {
|
||||
}
|
||||
|
||||
impl<E: EthSpec> CachedPTCs<E> {
|
||||
pub fn from_state(
|
||||
/// Returns `None` at the Gloas fork boundary (pre-Gloas state, Gloas shuffling epoch); the
|
||||
/// on-demand miss path in `with_cached_shuffling` handles those.
|
||||
pub fn try_from_state(
|
||||
state: &BeaconState<E>,
|
||||
epoch: Epoch,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Self, BeaconChainError> {
|
||||
) -> Result<Option<Self>, BeaconChainError> {
|
||||
if shuffling_requires_ptcs(epoch, spec) {
|
||||
if !state.fork_name_unchecked().gloas_enabled() {
|
||||
return Ok(None);
|
||||
}
|
||||
let ptcs = epoch
|
||||
.slot_iter(E::slots_per_epoch())
|
||||
.map(|slot| state.get_ptc(slot, spec))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
Ok(Self::PostGloas(ptcs, epoch))
|
||||
Ok(Some(Self::PostGloas(ptcs, epoch)))
|
||||
} else {
|
||||
Ok(Self::PreGloas)
|
||||
Ok(Some(Self::PreGloas))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -436,7 +441,11 @@ where
|
||||
.committee_cache(relative_epoch)
|
||||
.map_err(BeaconChainError::from)?
|
||||
.clone();
|
||||
let ptcs = CachedPTCs::from_state(&state, shuffling_epoch, spec)?;
|
||||
// The state has been advanced through the upgrade if needed, so `try_from_state`
|
||||
// cannot return None here.
|
||||
let ptcs = CachedPTCs::try_from_state(&state, shuffling_epoch, spec)?.ok_or(
|
||||
BeaconChainError::BeaconStateError(BeaconStateError::IncorrectStateVariant),
|
||||
)?;
|
||||
let shuffling_decision_block = shuffling_id.shuffling_decision_block;
|
||||
let cached_shuffling = CachedShuffling::new(committee_cache, ptcs);
|
||||
|
||||
@@ -816,4 +825,41 @@ mod test {
|
||||
"should limit cache size"
|
||||
);
|
||||
}
|
||||
|
||||
/// Pre-Gloas state across the Gloas fork: epoch G-1 returns `Some(PreGloas)`, epoch G and
|
||||
/// G+1 return `None` (the boundary skip).
|
||||
#[test]
|
||||
fn try_from_state_skips_at_gloas_boundary() {
|
||||
create_test_tracing_subscriber();
|
||||
|
||||
let mut spec = ForkName::Fulu.make_genesis_spec(E::default_spec());
|
||||
let gloas_fork_epoch = Epoch::new(2);
|
||||
spec.gloas_fork_epoch = Some(gloas_fork_epoch);
|
||||
|
||||
let harness = BeaconChainHarness::builder(MinimalEthSpec)
|
||||
.spec(Arc::new(spec.clone()))
|
||||
.deterministic_keypairs(8)
|
||||
.fresh_ephemeral_store()
|
||||
.build();
|
||||
let state = harness.get_current_state();
|
||||
assert!(!state.fork_name_unchecked().gloas_enabled());
|
||||
|
||||
for (epoch, expect_pre_gloas) in [
|
||||
(gloas_fork_epoch - 1, true),
|
||||
(gloas_fork_epoch, false),
|
||||
(gloas_fork_epoch + 1, false),
|
||||
] {
|
||||
let result = CachedPTCs::<E>::try_from_state(&state, epoch, &spec)
|
||||
.expect("must not error at the boundary");
|
||||
if expect_pre_gloas {
|
||||
assert!(
|
||||
matches!(result, Some(CachedPTCs::PreGloas)),
|
||||
"epoch {}: expected Some(PreGloas)",
|
||||
epoch
|
||||
);
|
||||
} else {
|
||||
assert!(result.is_none(), "epoch {}: expected None", epoch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
//! 1. We are required to store an additional `BeaconState` for the head block. This consumes
|
||||
//! memory.
|
||||
//! 2. There's a possibility that the head block is never built upon, causing wasted CPU cycles.
|
||||
use crate::shuffling_cache::CachedPTCs;
|
||||
use crate::validator_monitor::HISTORIC_EPOCHS as VALIDATOR_MONITOR_HISTORIC_EPOCHS;
|
||||
use crate::{
|
||||
BeaconChain, BeaconChainError, BeaconChainTypes, chain_config::FORK_CHOICE_LOOKAHEAD_FACTOR,
|
||||
shuffling_cache::CachedShuffling,
|
||||
BeaconChain, BeaconChainError, BeaconChainTypes,
|
||||
chain_config::FORK_CHOICE_LOOKAHEAD_FACTOR,
|
||||
shuffling_cache::{CachedPTCs, CachedShuffling},
|
||||
};
|
||||
use slot_clock::SlotClock;
|
||||
use state_processing::per_slot_processing;
|
||||
@@ -396,26 +396,16 @@ fn advance_head<T: BeaconChainTypes>(beacon_chain: &Arc<BeaconChain<T>>) -> Resu
|
||||
.map_err(BeaconChainError::from)?;
|
||||
let committee_cache = state
|
||||
.committee_cache(RelativeEpoch::Next)
|
||||
.map_err(BeaconChainError::from)?;
|
||||
.map_err(BeaconChainError::from)?
|
||||
.clone();
|
||||
let shuffling_epoch = RelativeEpoch::Next.into_epoch(state.current_epoch());
|
||||
|
||||
if beacon_chain
|
||||
.spec
|
||||
.fork_name_at_epoch(shuffling_epoch)
|
||||
.gloas_enabled()
|
||||
&& !state.fork_name_unchecked().gloas_enabled()
|
||||
if let Some(ptcs) = CachedPTCs::try_from_state(&state, shuffling_epoch, &beacon_chain.spec)?
|
||||
{
|
||||
debug!(
|
||||
%shuffling_epoch,
|
||||
"Skipping priming of attester cache for Gloas boundary epoch"
|
||||
beacon_chain.shuffling_cache.write().insert_committee_cache(
|
||||
shuffling_id.clone(),
|
||||
CachedShuffling::new(committee_cache, ptcs),
|
||||
);
|
||||
} else {
|
||||
let ptcs = CachedPTCs::from_state(&state, shuffling_epoch, &beacon_chain.spec)?;
|
||||
let cached_shuffling = CachedShuffling::new(committee_cache.clone(), ptcs);
|
||||
beacon_chain
|
||||
.shuffling_cache
|
||||
.write()
|
||||
.insert_committee_cache(shuffling_id.clone(), cached_shuffling);
|
||||
|
||||
debug!(
|
||||
?head_block_root,
|
||||
@@ -424,6 +414,11 @@ fn advance_head<T: BeaconChainTypes>(beacon_chain: &Arc<BeaconChain<T>>) -> Resu
|
||||
current_epoch = %current_slot.epoch(T::EthSpec::slots_per_epoch()),
|
||||
"Primed proposer and attester caches"
|
||||
);
|
||||
} else {
|
||||
debug!(
|
||||
%shuffling_epoch,
|
||||
"Skipping priming of attester cache for Gloas boundary epoch"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user