Replace INTERVALS_PER_SLOT with explicit slot component times (#7944)

https://github.com/ethereum/consensus-specs/pull/4476


  


Co-Authored-By: Barnabas Busa <barnabas.busa@ethereum.org>

Co-Authored-By: Eitan Seri- Levi <eserilev@gmail.com>

Co-Authored-By: Eitan Seri-Levi <eserilev@ucsc.edu>

Co-Authored-By: Michael Sproul <michaelsproul@users.noreply.github.com>

Co-Authored-By: Michael Sproul <michael@sigmaprime.io>
This commit is contained in:
Eitan Seri-Levi
2026-02-01 21:58:42 -08:00
committed by GitHub
parent cd8049a696
commit 3ecf964385
56 changed files with 579 additions and 184 deletions

View File

@@ -748,7 +748,8 @@ mod tests {
.execution_block_generator()
.move_to_terminal_block()
.expect("should move to terminal block");
let timestamp = harness.get_timestamp_at_slot() + harness.spec.seconds_per_slot;
let timestamp =
harness.get_timestamp_at_slot() + harness.spec.get_slot_duration().as_secs();
harness
.execution_block_generator()
.modify_last_block(|block| {

View File

@@ -4687,7 +4687,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// 1. It seems we have time to propagate and still receive the proposer boost.
// 2. The current head block was seen late.
// 3. The `get_proposer_head` conditions from fork choice pass.
let proposing_on_time = slot_delay < self.config.re_org_cutoff(self.spec.seconds_per_slot);
let proposing_on_time =
slot_delay < self.config.re_org_cutoff(self.spec.get_slot_duration());
if !proposing_on_time {
debug!(reason = "not proposing on time", "Not attempting re-org");
return None;
@@ -4977,7 +4978,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.and_then(|slot_start| {
let now = self.slot_clock.now_duration()?;
let slot_delay = now.saturating_sub(slot_start);
Some(slot_delay <= self.config.re_org_cutoff(self.spec.seconds_per_slot))
Some(slot_delay <= self.config.re_org_cutoff(self.spec.get_slot_duration()))
})
.unwrap_or(false)
} else {
@@ -5094,7 +5095,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
);
block_delays
.observed
.is_some_and(|delay| delay >= self.slot_clock.unagg_attestation_production_delay())
.is_some_and(|delay| delay >= self.spec.get_unaggregated_attestation_due())
}
/// Produce a block for some `slot` upon the given `state`.

View File

@@ -147,7 +147,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
if let Some(bellatrix_epoch) = self.spec.bellatrix_fork_epoch {
let bellatrix_slot = bellatrix_epoch.start_slot(T::EthSpec::slots_per_epoch());
let bellatrix_readiness_preparation_slots =
BELLATRIX_READINESS_PREPARATION_SECONDS / self.spec.seconds_per_slot;
BELLATRIX_READINESS_PREPARATION_SECONDS / self.spec.get_slot_duration().as_secs();
if self.execution_layer.is_some() {
// The user has already configured an execution layer, start checking for readiness

View File

@@ -856,6 +856,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.as_utf8_lossy(),
&self.slot_clock,
self.event_handler.as_ref(),
&self.spec,
);
if is_epoch_transition || reorg_distance.is_some() {
@@ -1292,6 +1293,7 @@ fn observe_head_block_delays<E: EthSpec, S: SlotClock>(
head_block_graffiti: String,
slot_clock: &S,
event_handler: Option<&ServerSentEventHandler<E>>,
spec: &ChainSpec,
) {
let Some(block_time_set_as_head) = slot_clock.now_duration() else {
// Practically unreachable: the slot clock's time should not be before the UNIX epoch.
@@ -1421,7 +1423,7 @@ fn observe_head_block_delays<E: EthSpec, S: SlotClock>(
// Determine whether the block has been set as head too late for proper attestation
// production.
let late_head = attestable_delay >= slot_clock.unagg_attestation_production_delay();
let late_head = attestable_delay >= spec.get_unaggregated_attestation_due();
// If the block was enshrined as head too late for attestations to be created for it,
// log a debug warning and increment a metric.

View File

@@ -168,11 +168,9 @@ impl Default for ChainConfig {
impl ChainConfig {
/// The latest delay from the start of the slot at which to attempt a 1-slot re-org.
pub fn re_org_cutoff(&self, seconds_per_slot: u64) -> Duration {
pub fn re_org_cutoff(&self, slot_duration: Duration) -> Duration {
self.re_org_cutoff_millis
.map(Duration::from_millis)
.unwrap_or_else(|| {
Duration::from_secs(seconds_per_slot) / DEFAULT_RE_ORG_CUTOFF_DENOMINATOR
})
.unwrap_or_else(|| slot_duration / DEFAULT_RE_ORG_CUTOFF_DENOMINATOR)
}
}

View File

@@ -113,8 +113,9 @@ impl ValidatorRegistrations {
// Apply the change from the next epoch after adding some delay buffer to ensure
// the node has enough time to subscribe to subnets etc, and to avoid having
// inconsistent column counts within an epoch.
let effective_delay_slots =
CUSTODY_CHANGE_DA_EFFECTIVE_DELAY_SECONDS / spec.seconds_per_slot;
let effective_delay_slots = CUSTODY_CHANGE_DA_EFFECTIVE_DELAY_SECONDS
.checked_div(spec.get_slot_duration().as_secs())
.unwrap_or(1);
let effective_epoch =
(current_slot + effective_delay_slots).epoch(E::slots_per_epoch()) + 1;
self.epoch_validator_custody_requirements

View File

@@ -1250,7 +1250,7 @@ mod test {
let slot_clock = TestingSlotClock::new(
Slot::new(0),
Duration::from_secs(0),
Duration::from_secs(spec.seconds_per_slot),
spec.get_slot_duration(),
);
let kzg = get_kzg(&spec);
let store = Arc::new(HotColdDB::open_ephemeral(<_>::default(), spec.clone()).unwrap());

View File

@@ -75,9 +75,9 @@ impl<T: BeaconChainTypes> VerifiedLightClientFinalityUpdate<T> {
.slot_clock
.start_of(rcv_finality_update.signature_slot())
.ok_or(Error::SigSlotStartIsNone)?;
let one_third_slot_duration = Duration::new(chain.spec.seconds_per_slot / 3, 0);
let sync_message_due = chain.spec.get_sync_message_due();
if seen_timestamp + chain.spec.maximum_gossip_clock_disparity()
< start_time + one_third_slot_duration
< start_time + sync_message_due
{
return Err(Error::TooEarly);
}

View File

@@ -70,9 +70,11 @@ impl<T: BeaconChainTypes> VerifiedLightClientOptimisticUpdate<T> {
.slot_clock
.start_of(rcv_optimistic_update.signature_slot())
.ok_or(Error::SigSlotStartIsNone)?;
let one_third_slot_duration = Duration::new(chain.spec.seconds_per_slot / 3, 0);
let sync_message_due = chain.spec.get_sync_message_due();
if seen_timestamp + chain.spec.maximum_gossip_clock_disparity()
< start_time + one_third_slot_duration
< start_time + sync_message_due
{
return Err(Error::TooEarly);
}

View File

@@ -531,21 +531,26 @@ where
.expect("cannot recalculate fork times without spec");
mock.server.execution_block_generator().shanghai_time =
spec.capella_fork_epoch.map(|epoch| {
genesis_time + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64()
genesis_time
+ spec.get_slot_duration().as_secs() * E::slots_per_epoch() * epoch.as_u64()
});
mock.server.execution_block_generator().cancun_time = spec.deneb_fork_epoch.map(|epoch| {
genesis_time + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64()
genesis_time
+ spec.get_slot_duration().as_secs() * E::slots_per_epoch() * epoch.as_u64()
});
mock.server.execution_block_generator().prague_time =
spec.electra_fork_epoch.map(|epoch| {
genesis_time + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64()
genesis_time
+ spec.get_slot_duration().as_secs() * E::slots_per_epoch() * epoch.as_u64()
});
mock.server.execution_block_generator().osaka_time = spec.fulu_fork_epoch.map(|epoch| {
genesis_time + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64()
genesis_time
+ spec.get_slot_duration().as_secs() * E::slots_per_epoch() * epoch.as_u64()
});
mock.server.execution_block_generator().amsterdam_time =
spec.gloas_fork_epoch.map(|epoch| {
genesis_time + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64()
genesis_time
+ spec.get_slot_duration().as_secs() * E::slots_per_epoch() * epoch.as_u64()
});
self
@@ -590,7 +595,6 @@ where
let (shutdown_tx, shutdown_receiver) = futures::channel::mpsc::channel(1);
let spec = self.spec.expect("cannot build without spec");
let seconds_per_slot = spec.seconds_per_slot;
let validator_keypairs = self
.validator_keypairs
.expect("cannot build without validator keypairs");
@@ -635,7 +639,7 @@ where
builder.slot_clock(testing_slot_clock)
} else if builder.get_slot_clock().is_none() {
builder
.testing_slot_clock(Duration::from_secs(seconds_per_slot))
.testing_slot_clock(spec.get_slot_duration())
.expect("should configure testing slot clock")
} else {
builder
@@ -662,19 +666,24 @@ pub fn mock_execution_layer_from_parts<E: EthSpec>(
task_executor: TaskExecutor,
) -> MockExecutionLayer<E> {
let shanghai_time = spec.capella_fork_epoch.map(|epoch| {
HARNESS_GENESIS_TIME + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64()
HARNESS_GENESIS_TIME
+ (spec.get_slot_duration().as_secs()) * E::slots_per_epoch() * epoch.as_u64()
});
let cancun_time = spec.deneb_fork_epoch.map(|epoch| {
HARNESS_GENESIS_TIME + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64()
HARNESS_GENESIS_TIME
+ (spec.get_slot_duration().as_secs()) * E::slots_per_epoch() * epoch.as_u64()
});
let prague_time = spec.electra_fork_epoch.map(|epoch| {
HARNESS_GENESIS_TIME + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64()
HARNESS_GENESIS_TIME
+ (spec.get_slot_duration().as_secs()) * E::slots_per_epoch() * epoch.as_u64()
});
let osaka_time = spec.fulu_fork_epoch.map(|epoch| {
HARNESS_GENESIS_TIME + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64()
HARNESS_GENESIS_TIME
+ (spec.get_slot_duration().as_secs()) * E::slots_per_epoch() * epoch.as_u64()
});
let amsterdam_time = spec.gloas_fork_epoch.map(|epoch| {
HARNESS_GENESIS_TIME + spec.seconds_per_slot * E::slots_per_epoch() * epoch.as_u64()
HARNESS_GENESIS_TIME
+ (spec.get_slot_duration().as_secs()) * E::slots_per_epoch() * epoch.as_u64()
});
let kzg = get_kzg(&spec);

View File

@@ -1263,6 +1263,7 @@ impl<E: EthSpec> ValidatorMonitor<E> {
signed_aggregate_and_proof: &SignedAggregateAndProof<E>,
indexed_attestation: &IndexedAttestation<E>,
slot_clock: &S,
spec: &ChainSpec,
) {
self.register_aggregated_attestation(
"gossip",
@@ -1270,6 +1271,7 @@ impl<E: EthSpec> ValidatorMonitor<E> {
signed_aggregate_and_proof,
indexed_attestation,
slot_clock,
spec,
)
}
@@ -1280,6 +1282,7 @@ impl<E: EthSpec> ValidatorMonitor<E> {
signed_aggregate_and_proof: &SignedAggregateAndProof<E>,
indexed_attestation: &IndexedAttestation<E>,
slot_clock: &S,
spec: &ChainSpec,
) {
self.register_aggregated_attestation(
"api",
@@ -1287,6 +1290,7 @@ impl<E: EthSpec> ValidatorMonitor<E> {
signed_aggregate_and_proof,
indexed_attestation,
slot_clock,
spec,
)
}
@@ -1297,13 +1301,14 @@ impl<E: EthSpec> ValidatorMonitor<E> {
signed_aggregate_and_proof: &SignedAggregateAndProof<E>,
indexed_attestation: &IndexedAttestation<E>,
slot_clock: &S,
spec: &ChainSpec,
) {
let data = indexed_attestation.data();
let epoch = data.slot.epoch(E::slots_per_epoch());
let delay = get_message_delay_ms(
seen_timestamp,
data.slot,
slot_clock.agg_attestation_production_delay(),
spec.get_aggregate_attestation_due(),
slot_clock,
);
@@ -1488,12 +1493,14 @@ impl<E: EthSpec> ValidatorMonitor<E> {
seen_timestamp: Duration,
sync_committee_message: &SyncCommitteeMessage,
slot_clock: &S,
spec: &ChainSpec,
) {
self.register_sync_committee_message(
"gossip",
seen_timestamp,
sync_committee_message,
slot_clock,
spec,
)
}
@@ -1503,12 +1510,14 @@ impl<E: EthSpec> ValidatorMonitor<E> {
seen_timestamp: Duration,
sync_committee_message: &SyncCommitteeMessage,
slot_clock: &S,
spec: &ChainSpec,
) {
self.register_sync_committee_message(
"api",
seen_timestamp,
sync_committee_message,
slot_clock,
spec,
)
}
@@ -1519,15 +1528,15 @@ impl<E: EthSpec> ValidatorMonitor<E> {
seen_timestamp: Duration,
sync_committee_message: &SyncCommitteeMessage,
slot_clock: &S,
spec: &ChainSpec,
) {
if let Some(validator) = self.get_validator(sync_committee_message.validator_index) {
let id = &validator.id;
let epoch = sync_committee_message.slot.epoch(E::slots_per_epoch());
let delay = get_message_delay_ms(
seen_timestamp,
sync_committee_message.slot,
slot_clock.sync_committee_message_production_delay(),
spec.get_sync_message_due(),
slot_clock,
);
@@ -1568,6 +1577,7 @@ impl<E: EthSpec> ValidatorMonitor<E> {
sync_contribution: &SignedContributionAndProof<E>,
participant_pubkeys: &[PublicKeyBytes],
slot_clock: &S,
spec: &ChainSpec,
) {
self.register_sync_committee_contribution(
"gossip",
@@ -1575,6 +1585,7 @@ impl<E: EthSpec> ValidatorMonitor<E> {
sync_contribution,
participant_pubkeys,
slot_clock,
spec,
)
}
@@ -1585,6 +1596,7 @@ impl<E: EthSpec> ValidatorMonitor<E> {
sync_contribution: &SignedContributionAndProof<E>,
participant_pubkeys: &[PublicKeyBytes],
slot_clock: &S,
spec: &ChainSpec,
) {
self.register_sync_committee_contribution(
"api",
@@ -1592,6 +1604,7 @@ impl<E: EthSpec> ValidatorMonitor<E> {
sync_contribution,
participant_pubkeys,
slot_clock,
spec,
)
}
@@ -1603,6 +1616,7 @@ impl<E: EthSpec> ValidatorMonitor<E> {
sync_contribution: &SignedContributionAndProof<E>,
participant_pubkeys: &[PublicKeyBytes],
slot_clock: &S,
spec: &ChainSpec,
) {
let slot = sync_contribution.message.contribution.slot;
let epoch = slot.epoch(E::slots_per_epoch());
@@ -1610,7 +1624,7 @@ impl<E: EthSpec> ValidatorMonitor<E> {
let delay = get_message_delay_ms(
seen_timestamp,
slot,
slot_clock.sync_committee_contribution_production_delay(),
spec.get_contribution_message_due(),
slot_clock,
);