From 3ecf964385f944eaeffa3bcd50cdbf1db377b903 Mon Sep 17 00:00:00 2001 From: Eitan Seri-Levi Date: Sun, 1 Feb 2026 21:58:42 -0800 Subject: [PATCH] Replace `INTERVALS_PER_SLOT` with explicit slot component times (#7944) https://github.com/ethereum/consensus-specs/pull/4476 Co-Authored-By: Barnabas Busa Co-Authored-By: Eitan Seri- Levi Co-Authored-By: Eitan Seri-Levi Co-Authored-By: Michael Sproul Co-Authored-By: Michael Sproul --- account_manager/src/validator/exit.rs | 10 +- .../beacon_chain/src/beacon_block_streamer.rs | 3 +- beacon_node/beacon_chain/src/beacon_chain.rs | 7 +- .../beacon_chain/src/bellatrix_readiness.rs | 2 +- .../beacon_chain/src/canonical_head.rs | 4 +- beacon_node/beacon_chain/src/chain_config.rs | 6 +- .../beacon_chain/src/custody_context.rs | 5 +- .../src/data_availability_checker.rs | 2 +- ...ght_client_finality_update_verification.rs | 4 +- ...t_client_optimistic_update_verification.rs | 6 +- beacon_node/beacon_chain/src/test_utils.rs | 33 +- .../beacon_chain/src/validator_monitor.rs | 22 +- beacon_node/beacon_chain/tests/bellatrix.rs | 2 +- beacon_node/beacon_chain/tests/capella.rs | 2 +- beacon_node/beacon_chain/tests/store_tests.rs | 7 +- beacon_node/client/src/builder.rs | 12 +- beacon_node/client/src/notifier.rs | 8 +- .../src/test_utils/mock_builder.rs | 3 +- beacon_node/http_api/src/publish_blocks.rs | 3 +- beacon_node/http_api/src/sync_committees.rs | 2 + beacon_node/http_api/src/validator/mod.rs | 1 + beacon_node/http_api/tests/tests.rs | 6 +- beacon_node/lighthouse_network/src/config.rs | 4 +- .../service/gossipsub_scoring_parameters.rs | 2 +- .../lighthouse_network/src/service/mod.rs | 9 +- .../gossip_methods.rs | 7 +- .../network_beacon_processor/sync_methods.rs | 2 +- beacon_node/network/src/service.rs | 10 +- beacon_node/src/config.rs | 5 +- book/src/api_vc_endpoints.md | 1 + .../chiado/config.yaml | 14 + .../gnosis/config.yaml | 14 + common/slot_clock/src/lib.rs | 34 -- consensus/fork_choice/src/fork_choice.rs | 7 +- .../src/per_block_processing.rs | 2 +- consensus/types/src/core/chain_spec.rs | 369 +++++++++++++++++- consensus/types/src/core/consts.rs | 2 +- .../environment/tests/testnet_dir/config.yaml | 14 + lighthouse/tests/beacon_node.rs | 7 +- scripts/local_testnet/network_params.yaml | 2 +- scripts/tests/doppelganger_protection.sh | 10 +- .../tests/genesis-sync-config-electra.yaml | 2 +- scripts/tests/genesis-sync-config-fulu.yaml | 2 +- scripts/tests/network_params.yaml | 2 +- testing/ef_tests/src/cases/fork_choice.rs | 2 +- testing/simulator/src/basic_sim.rs | 10 +- testing/simulator/src/fallback_sim.rs | 11 +- testing/simulator/src/local_network.rs | 18 +- .../beacon_node_fallback/src/lib.rs | 4 +- validator_client/src/lib.rs | 4 +- .../src/attestation_service.rs | 13 +- .../src/notifier_service.rs | 4 +- .../src/preparation_service.rs | 6 +- .../src/sync_committee_service.rs | 14 +- validator_manager/src/exit_validators.rs | 2 +- validator_manager/src/list_validators.rs | 4 +- 56 files changed, 579 insertions(+), 184 deletions(-) diff --git a/account_manager/src/validator/exit.rs b/account_manager/src/validator/exit.rs index 5ea77f284e..b0b5c8a59d 100644 --- a/account_manager/src/validator/exit.rs +++ b/account_manager/src/validator/exit.rs @@ -102,7 +102,7 @@ pub fn cli_run(matches: &ArgMatches, env: Environment) -> Result< let client = BeaconNodeHttpClient::new( SensitiveUrl::parse(&server_url) .map_err(|e| format!("Failed to parse beacon http server: {:?}", e))?, - Timeouts::set_all(Duration::from_secs(env.eth2_config.spec.seconds_per_slot)), + Timeouts::set_all(env.eth2_config.spec.get_slot_duration()), ); let eth2_network_config = env @@ -230,7 +230,7 @@ async fn publish_voluntary_exit( loop { // Sleep for a slot duration and then check if voluntary exit was processed // by checking the validator status. - sleep(Duration::from_secs(spec.seconds_per_slot)).await; + sleep(spec.get_slot_duration()).await; let validator_data = get_validator_data(client, &keypair.pk).await?; match validator_data.status { @@ -251,7 +251,9 @@ async fn publish_voluntary_exit( eprintln!("Please keep your validator running till exit epoch"); eprintln!( "Exit epoch in approximately {} secs", - (exit_epoch - current_epoch) * spec.seconds_per_slot * E::slots_per_epoch() + (exit_epoch - current_epoch) + * spec.get_slot_duration().as_secs() + * E::slots_per_epoch() ); break; } @@ -350,7 +352,7 @@ fn get_current_epoch(genesis_time: u64, spec: &ChainSpec) -> Option< let slot_clock = SystemTimeSlotClock::new( spec.genesis_slot, Duration::from_secs(genesis_time), - Duration::from_secs(spec.seconds_per_slot), + spec.get_slot_duration(), ); slot_clock.now().map(|s| s.epoch(E::slots_per_epoch())) } diff --git a/beacon_node/beacon_chain/src/beacon_block_streamer.rs b/beacon_node/beacon_chain/src/beacon_block_streamer.rs index a462376cc0..edbdd6d4d9 100644 --- a/beacon_node/beacon_chain/src/beacon_block_streamer.rs +++ b/beacon_node/beacon_chain/src/beacon_block_streamer.rs @@ -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| { diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index df0db42a66..c29aa10e73 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -4687,7 +4687,8 @@ impl BeaconChain { // 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 BeaconChain { .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 BeaconChain { ); 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`. diff --git a/beacon_node/beacon_chain/src/bellatrix_readiness.rs b/beacon_node/beacon_chain/src/bellatrix_readiness.rs index 412870354b..88ccc21b85 100644 --- a/beacon_node/beacon_chain/src/bellatrix_readiness.rs +++ b/beacon_node/beacon_chain/src/bellatrix_readiness.rs @@ -147,7 +147,7 @@ impl BeaconChain { 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 diff --git a/beacon_node/beacon_chain/src/canonical_head.rs b/beacon_node/beacon_chain/src/canonical_head.rs index 417d7f4e2f..db071db166 100644 --- a/beacon_node/beacon_chain/src/canonical_head.rs +++ b/beacon_node/beacon_chain/src/canonical_head.rs @@ -856,6 +856,7 @@ impl BeaconChain { .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( head_block_graffiti: String, slot_clock: &S, event_handler: Option<&ServerSentEventHandler>, + 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( // 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. diff --git a/beacon_node/beacon_chain/src/chain_config.rs b/beacon_node/beacon_chain/src/chain_config.rs index 1f5abc4891..711ffdc99c 100644 --- a/beacon_node/beacon_chain/src/chain_config.rs +++ b/beacon_node/beacon_chain/src/chain_config.rs @@ -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) } } diff --git a/beacon_node/beacon_chain/src/custody_context.rs b/beacon_node/beacon_chain/src/custody_context.rs index c512ce616a..72f62db1b4 100644 --- a/beacon_node/beacon_chain/src/custody_context.rs +++ b/beacon_node/beacon_chain/src/custody_context.rs @@ -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 diff --git a/beacon_node/beacon_chain/src/data_availability_checker.rs b/beacon_node/beacon_chain/src/data_availability_checker.rs index db37a79372..7f4848f006 100644 --- a/beacon_node/beacon_chain/src/data_availability_checker.rs +++ b/beacon_node/beacon_chain/src/data_availability_checker.rs @@ -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()); diff --git a/beacon_node/beacon_chain/src/light_client_finality_update_verification.rs b/beacon_node/beacon_chain/src/light_client_finality_update_verification.rs index 2dc4de7d04..81bc03a402 100644 --- a/beacon_node/beacon_chain/src/light_client_finality_update_verification.rs +++ b/beacon_node/beacon_chain/src/light_client_finality_update_verification.rs @@ -75,9 +75,9 @@ impl VerifiedLightClientFinalityUpdate { .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); } diff --git a/beacon_node/beacon_chain/src/light_client_optimistic_update_verification.rs b/beacon_node/beacon_chain/src/light_client_optimistic_update_verification.rs index 4079a374f8..826f170b80 100644 --- a/beacon_node/beacon_chain/src/light_client_optimistic_update_verification.rs +++ b/beacon_node/beacon_chain/src/light_client_optimistic_update_verification.rs @@ -70,9 +70,11 @@ impl VerifiedLightClientOptimisticUpdate { .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); } diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index a170d6a3d4..f4e4c37234 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -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( task_executor: TaskExecutor, ) -> MockExecutionLayer { 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); diff --git a/beacon_node/beacon_chain/src/validator_monitor.rs b/beacon_node/beacon_chain/src/validator_monitor.rs index 2a76d65d32..fdc7d27320 100644 --- a/beacon_node/beacon_chain/src/validator_monitor.rs +++ b/beacon_node/beacon_chain/src/validator_monitor.rs @@ -1263,6 +1263,7 @@ impl ValidatorMonitor { signed_aggregate_and_proof: &SignedAggregateAndProof, indexed_attestation: &IndexedAttestation, slot_clock: &S, + spec: &ChainSpec, ) { self.register_aggregated_attestation( "gossip", @@ -1270,6 +1271,7 @@ impl ValidatorMonitor { signed_aggregate_and_proof, indexed_attestation, slot_clock, + spec, ) } @@ -1280,6 +1282,7 @@ impl ValidatorMonitor { signed_aggregate_and_proof: &SignedAggregateAndProof, indexed_attestation: &IndexedAttestation, slot_clock: &S, + spec: &ChainSpec, ) { self.register_aggregated_attestation( "api", @@ -1287,6 +1290,7 @@ impl ValidatorMonitor { signed_aggregate_and_proof, indexed_attestation, slot_clock, + spec, ) } @@ -1297,13 +1301,14 @@ impl ValidatorMonitor { signed_aggregate_and_proof: &SignedAggregateAndProof, indexed_attestation: &IndexedAttestation, 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 ValidatorMonitor { 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 ValidatorMonitor { 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 ValidatorMonitor { 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 ValidatorMonitor { sync_contribution: &SignedContributionAndProof, participant_pubkeys: &[PublicKeyBytes], slot_clock: &S, + spec: &ChainSpec, ) { self.register_sync_committee_contribution( "gossip", @@ -1575,6 +1585,7 @@ impl ValidatorMonitor { sync_contribution, participant_pubkeys, slot_clock, + spec, ) } @@ -1585,6 +1596,7 @@ impl ValidatorMonitor { sync_contribution: &SignedContributionAndProof, participant_pubkeys: &[PublicKeyBytes], slot_clock: &S, + spec: &ChainSpec, ) { self.register_sync_committee_contribution( "api", @@ -1592,6 +1604,7 @@ impl ValidatorMonitor { sync_contribution, participant_pubkeys, slot_clock, + spec, ) } @@ -1603,6 +1616,7 @@ impl ValidatorMonitor { sync_contribution: &SignedContributionAndProof, 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 ValidatorMonitor { let delay = get_message_delay_ms( seen_timestamp, slot, - slot_clock.sync_committee_contribution_production_delay(), + spec.get_contribution_message_due(), slot_clock, ); diff --git a/beacon_node/beacon_chain/tests/bellatrix.rs b/beacon_node/beacon_chain/tests/bellatrix.rs index 5d466dd1d3..fc0f96ef88 100644 --- a/beacon_node/beacon_chain/tests/bellatrix.rs +++ b/beacon_node/beacon_chain/tests/bellatrix.rs @@ -174,7 +174,7 @@ async fn base_altair_bellatrix_with_terminal_block_after_fork() { .unwrap(); // Add a slot duration to get to the next slot - 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() diff --git a/beacon_node/beacon_chain/tests/capella.rs b/beacon_node/beacon_chain/tests/capella.rs index 2c2ba8e01a..e8ab795366 100644 --- a/beacon_node/beacon_chain/tests/capella.rs +++ b/beacon_node/beacon_chain/tests/capella.rs @@ -103,7 +103,7 @@ async fn base_altair_bellatrix_capella() { .unwrap(); // Add a slot duration to get to the next slot - 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| { diff --git a/beacon_node/beacon_chain/tests/store_tests.rs b/beacon_node/beacon_chain/tests/store_tests.rs index 14e9deb62a..ea5f735bde 100644 --- a/beacon_node/beacon_chain/tests/store_tests.rs +++ b/beacon_node/beacon_chain/tests/store_tests.rs @@ -3011,7 +3011,6 @@ async fn weak_subjectivity_sync_test( let temp2 = tempdir().unwrap(); let store = get_store(&temp2); let spec = test_spec::(); - let seconds_per_slot = spec.seconds_per_slot; let kzg = get_kzg(&spec); @@ -3025,7 +3024,7 @@ async fn weak_subjectivity_sync_test( let slot_clock = TestingSlotClock::new( Slot::new(0), Duration::from_secs(harness.chain.genesis_time), - Duration::from_secs(seconds_per_slot), + spec.get_slot_duration(), ); slot_clock.set_slot(harness.get_current_slot().as_u64()); @@ -3940,8 +3939,6 @@ async fn revert_minority_fork_on_resume() { let mut spec2 = MinimalEthSpec::default_spec(); spec2.altair_fork_epoch = Some(fork_epoch); - let seconds_per_slot = spec1.seconds_per_slot; - let all_validators = (0..validator_count).collect::>(); // Chain with no fork epoch configured. @@ -4061,7 +4058,7 @@ async fn revert_minority_fork_on_resume() { builder = builder .resume_from_db() .unwrap() - .testing_slot_clock(Duration::from_secs(seconds_per_slot)) + .testing_slot_clock(spec2.get_slot_duration()) .unwrap(); builder .get_slot_clock() diff --git a/beacon_node/client/src/builder.rs b/beacon_node/client/src/builder.rs index ba90cbd8be..1b395ac8da 100644 --- a/beacon_node/client/src/builder.rs +++ b/beacon_node/client/src/builder.rs @@ -315,7 +315,7 @@ where let deneb_time = genesis_time + (deneb_fork_epoch.as_u64() * E::slots_per_epoch() - * spec.seconds_per_slot); + * spec.get_slot_duration().as_secs()); // Shrink the blob availability window so users don't start // a sync right before blobs start to disappear from the P2P @@ -325,7 +325,7 @@ where .saturating_sub(BLOB_AVAILABILITY_REDUCTION_EPOCHS); let blob_availability_window = reduced_p2p_availability_epochs * E::slots_per_epoch() - * spec.seconds_per_slot; + * spec.get_slot_duration().as_secs(); if now > deneb_time + blob_availability_window { return Err( @@ -592,17 +592,17 @@ where .network_globals .clone() .ok_or("slot_notifier requires a libp2p network")?; - let seconds_per_slot = self + let slot_duration = self .chain_spec .as_ref() .ok_or("slot_notifier requires a chain spec")? - .seconds_per_slot; + .get_slot_duration(); spawn_notifier( context.executor, beacon_chain, network_globals, - seconds_per_slot, + slot_duration, ) .map_err(|e| format!("Unable to start slot notifier: {}", e))?; @@ -906,7 +906,7 @@ where let slot_clock = SystemTimeSlotClock::new( spec.genesis_slot, Duration::from_secs(genesis_time), - Duration::from_secs(spec.seconds_per_slot), + spec.get_slot_duration(), ); self.slot_clock = Some(slot_clock); diff --git a/beacon_node/client/src/notifier.rs b/beacon_node/client/src/notifier.rs index 52a3b92cb6..3f01622c35 100644 --- a/beacon_node/client/src/notifier.rs +++ b/beacon_node/client/src/notifier.rs @@ -44,10 +44,8 @@ pub fn spawn_notifier( executor: task_executor::TaskExecutor, beacon_chain: Arc>, network: Arc>, - seconds_per_slot: u64, + slot_duration: Duration, ) -> Result<(), String> { - let slot_duration = Duration::from_secs(seconds_per_slot); - let speedo = Mutex::new(Speedo::default()); // Keep track of sync state and reset the speedo on specific sync state changes. @@ -568,8 +566,8 @@ fn find_next_fork_to_prepare( // Find the first fork that is scheduled and close to happen if let Some(fork_epoch) = fork_epoch { let fork_slot = fork_epoch.start_slot(T::EthSpec::slots_per_epoch()); - let preparation_slots = - FORK_READINESS_PREPARATION_SECONDS / beacon_chain.spec.seconds_per_slot; + let preparation_slots = FORK_READINESS_PREPARATION_SECONDS + / beacon_chain.spec.get_slot_duration().as_secs(); let in_fork_preparation_period = current_slot + preparation_slots > fork_slot; if in_fork_preparation_period { return Some(*fork); diff --git a/beacon_node/execution_layer/src/test_utils/mock_builder.rs b/beacon_node/execution_layer/src/test_utils/mock_builder.rs index 0016db9e0c..464879288b 100644 --- a/beacon_node/execution_layer/src/test_utils/mock_builder.rs +++ b/beacon_node/execution_layer/src/test_utils/mock_builder.rs @@ -860,7 +860,8 @@ impl MockBuilder { .data .genesis_time }; - let timestamp = (slots_since_genesis * self.spec.seconds_per_slot) + genesis_time; + let timestamp = + (slots_since_genesis * self.spec.get_slot_duration().as_secs()) + genesis_time; let head_state: BeaconState = self .beacon_client diff --git a/beacon_node/http_api/src/publish_blocks.rs b/beacon_node/http_api/src/publish_blocks.rs index 1887dee640..7826ec55e1 100644 --- a/beacon_node/http_api/src/publish_blocks.rs +++ b/beacon_node/http_api/src/publish_blocks.rs @@ -21,7 +21,6 @@ use futures::TryFutureExt; use lighthouse_network::PubsubMessage; use network::NetworkMessage; use rand::prelude::SliceRandom; -use slot_clock::SlotClock; use std::marker::PhantomData; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; @@ -771,7 +770,7 @@ fn late_block_logging>( // // Check to see the thresholds are non-zero to avoid logging errors with small // slot times (e.g., during testing) - let too_late_threshold = chain.slot_clock.unagg_attestation_production_delay(); + let too_late_threshold = chain.spec.get_unaggregated_attestation_due(); let delayed_threshold = too_late_threshold / 2; if delay >= too_late_threshold { error!( diff --git a/beacon_node/http_api/src/sync_committees.rs b/beacon_node/http_api/src/sync_committees.rs index 6e2f4c9585..efba0056b9 100644 --- a/beacon_node/http_api/src/sync_committees.rs +++ b/beacon_node/http_api/src/sync_committees.rs @@ -235,6 +235,7 @@ pub fn process_sync_committee_signatures( seen_timestamp, verified.sync_message(), &chain.slot_clock, + &chain.spec, ); verified_for_pool = Some(verified); @@ -376,6 +377,7 @@ pub fn process_signed_contribution_and_proofs( verified_contribution.aggregate(), verified_contribution.participant_pubkeys(), &chain.slot_clock, + &chain.spec, ); verified_contributions.push((index, verified_contribution)); diff --git a/beacon_node/http_api/src/validator/mod.rs b/beacon_node/http_api/src/validator/mod.rs index 8baf7c5245..b1ab4c648a 100644 --- a/beacon_node/http_api/src/validator/mod.rs +++ b/beacon_node/http_api/src/validator/mod.rs @@ -862,6 +862,7 @@ pub fn post_validator_aggregate_and_proofs( verified_aggregate.aggregate(), verified_aggregate.indexed_attestation(), &chain.slot_clock, + &chain.spec, ); verified_aggregates.push((index, verified_aggregate)); diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index a763db6421..c60f572002 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -52,7 +52,7 @@ use types::{ type E = MainnetEthSpec; -const SECONDS_PER_SLOT: u64 = 12; +const SLOT_DURATION_MS: u64 = 12_000; const SLOTS_PER_EPOCH: u64 = 32; const VALIDATOR_COUNT: usize = SLOTS_PER_EPOCH as usize; const CHAIN_LENGTH: u64 = SLOTS_PER_EPOCH * 5 - 1; // Make `next_block` an epoch transition @@ -323,7 +323,7 @@ impl ApiTester { let client = BeaconNodeHttpClient::new( beacon_url, - Timeouts::set_all(Duration::from_secs(SECONDS_PER_SLOT)), + Timeouts::set_all(Duration::from_millis(SLOT_DURATION_MS)), ); Self { @@ -411,7 +411,7 @@ impl ApiTester { listening_socket.port() )) .unwrap(), - Timeouts::set_all(Duration::from_secs(SECONDS_PER_SLOT)), + Timeouts::set_all(Duration::from_millis(SLOT_DURATION_MS)), ); Self { diff --git a/beacon_node/lighthouse_network/src/config.rs b/beacon_node/lighthouse_network/src/config.rs index c14d207484..9940cb9f7f 100644 --- a/beacon_node/lighthouse_network/src/config.rs +++ b/beacon_node/lighthouse_network/src/config.rs @@ -443,7 +443,7 @@ pub fn gossipsub_config( network_load: u8, fork_context: Arc, gossipsub_config_params: GossipsubConfigParams, - seconds_per_slot: u64, + slot_duration: Duration, slots_per_epoch: u64, idontwant_message_size_threshold: usize, ) -> gossipsub::Config { @@ -487,7 +487,7 @@ pub fn gossipsub_config( // To accommodate the increase, we should increase the duplicate cache time to filter older seen messages. // 2 epochs is quite sane for pre-deneb network parameters as well. // Hence we keep the same parameters for pre-deneb networks as well to avoid switching at the fork. - let duplicate_cache_time = Duration::from_secs(slots_per_epoch * seconds_per_slot * 2); + let duplicate_cache_time = Duration::from_secs(slots_per_epoch * slot_duration.as_secs() * 2); gossipsub::ConfigBuilder::default() .max_transmit_size(gossipsub_config_params.gossipsub_max_transmit_size) diff --git a/beacon_node/lighthouse_network/src/service/gossipsub_scoring_parameters.rs b/beacon_node/lighthouse_network/src/service/gossipsub_scoring_parameters.rs index 2cfae67281..7d1fa2d4dc 100644 --- a/beacon_node/lighthouse_network/src/service/gossipsub_scoring_parameters.rs +++ b/beacon_node/lighthouse_network/src/service/gossipsub_scoring_parameters.rs @@ -54,7 +54,7 @@ pub struct PeerScoreSettings { impl PeerScoreSettings { pub fn new(chain_spec: &ChainSpec, mesh_n: usize) -> PeerScoreSettings { - let slot = Duration::from_secs(chain_spec.seconds_per_slot); + let slot = chain_spec.get_slot_duration(); let beacon_attestation_subnet_weight = 1.0 / chain_spec.attestation_subnet_count as f64; let max_positive_score = (MAX_IN_MESH_SCORE + MAX_FIRST_MESSAGE_DELIVERIES_SCORE) * (BEACON_BLOCK_WEIGHT diff --git a/beacon_node/lighthouse_network/src/service/mod.rs b/beacon_node/lighthouse_network/src/service/mod.rs index 8b52096b38..74b1fb4b98 100644 --- a/beacon_node/lighthouse_network/src/service/mod.rs +++ b/beacon_node/lighthouse_network/src/service/mod.rs @@ -232,7 +232,7 @@ impl Network { config.network_load, ctx.fork_context.clone(), gossipsub_config_params, - ctx.chain_spec.seconds_per_slot, + ctx.chain_spec.get_slot_duration(), E::slots_per_epoch(), config.idontwant_message_size_threshold, ); @@ -240,13 +240,12 @@ impl Network { let score_settings = PeerScoreSettings::new(&ctx.chain_spec, gs_config.mesh_n()); let gossip_cache = { - let slot_duration = std::time::Duration::from_secs(ctx.chain_spec.seconds_per_slot); - let half_epoch = std::time::Duration::from_secs( - ctx.chain_spec.seconds_per_slot * E::slots_per_epoch() / 2, + let half_epoch = std::time::Duration::from_millis( + (ctx.chain_spec.get_slot_duration().as_millis() as u64) * E::slots_per_epoch() / 2, ); GossipCache::builder() - .beacon_block_timeout(slot_duration) + .beacon_block_timeout(ctx.chain_spec.get_slot_duration()) .aggregates_timeout(half_epoch) .attestation_timeout(half_epoch) .voluntary_exit_timeout(half_epoch * 2) diff --git a/beacon_node/network/src/network_beacon_processor/gossip_methods.rs b/beacon_node/network/src/network_beacon_processor/gossip_methods.rs index fec557ec04..a45441a37a 100644 --- a/beacon_node/network/src/network_beacon_processor/gossip_methods.rs +++ b/beacon_node/network/src/network_beacon_processor/gossip_methods.rs @@ -540,6 +540,7 @@ impl NetworkBeaconProcessor { aggregate, indexed_attestation, &self.chain.slot_clock, + &self.chain.spec, ); metrics::inc_counter( @@ -809,7 +810,7 @@ impl NetworkBeaconProcessor { Ok(gossip_verified_blob) => { metrics::inc_counter(&metrics::BEACON_PROCESSOR_GOSSIP_BLOB_VERIFIED_TOTAL); - if delay >= self.chain.slot_clock.unagg_attestation_production_delay() { + if delay >= self.chain.spec.get_unaggregated_attestation_due() { metrics::inc_counter(&metrics::BEACON_BLOB_GOSSIP_ARRIVED_LATE_TOTAL); debug!( block_root = ?gossip_verified_blob.block_root(), @@ -1237,7 +1238,7 @@ impl NetworkBeaconProcessor { let verified_block = match verification_result { Ok(verified_block) => { - if block_delay >= self.chain.slot_clock.unagg_attestation_production_delay() { + if block_delay >= self.chain.spec.get_unaggregated_attestation_due() { metrics::inc_counter(&metrics::BEACON_BLOCK_DELAY_GOSSIP_ARRIVED_LATE_TOTAL); debug!( block_root = ?verified_block.block_root, @@ -1914,6 +1915,7 @@ impl NetworkBeaconProcessor { seen_timestamp, sync_signature.sync_message(), &self.chain.slot_clock, + &self.chain.spec, ); metrics::inc_counter(&metrics::BEACON_PROCESSOR_SYNC_MESSAGE_VERIFIED_TOTAL); @@ -1976,6 +1978,7 @@ impl NetworkBeaconProcessor { sync_contribution.aggregate(), sync_contribution.participant_pubkeys(), &self.chain.slot_clock, + &self.chain.spec, ); metrics::inc_counter(&metrics::BEACON_PROCESSOR_SYNC_CONTRIBUTION_VERIFIED_TOTAL); diff --git a/beacon_node/network/src/network_beacon_processor/sync_methods.rs b/beacon_node/network/src/network_beacon_processor/sync_methods.rs index bf1485a339..a6b3ea9e4b 100644 --- a/beacon_node/network/src/network_beacon_processor/sync_methods.rs +++ b/beacon_node/network/src/network_beacon_processor/sync_methods.rs @@ -297,7 +297,7 @@ impl NetworkBeaconProcessor { && current_slot == slot { // Note: this metric is useful to gauge how long it takes to receive blobs requested - // over rpc. Since we always send the request for block components at `slot_clock.single_lookup_delay()` + // over rpc. Since we always send the request for block components at `get_unaggregated_attestation_due() / 2` // we can use that as a baseline to measure against. let delay = get_slot_delay_ms(seen_timestamp, slot, &self.chain.slot_clock); diff --git a/beacon_node/network/src/service.rs b/beacon_node/network/src/service.rs index 0869b442ae..af56b80822 100644 --- a/beacon_node/network/src/service.rs +++ b/beacon_node/network/src/service.rs @@ -862,9 +862,11 @@ impl NetworkService { self.next_digest_update = Box::pin(next_digest_delay(&self.beacon_chain).into()); // Set the next_unsubscribe delay. - let epoch_duration = - self.beacon_chain.spec.seconds_per_slot * T::EthSpec::slots_per_epoch(); - let unsubscribe_delay = Duration::from_secs(UNSUBSCRIBE_DELAY_EPOCHS * epoch_duration); + let unsubscribe_delay = Duration::from_secs( + UNSUBSCRIBE_DELAY_EPOCHS + * self.beacon_chain.spec.get_slot_duration().as_secs() + * T::EthSpec::slots_per_epoch(), + ); // Update the `next_topic_subscriptions` timer if the next change in the fork digest is known. self.next_topic_subscriptions = @@ -915,7 +917,7 @@ fn next_topic_subscriptions_delay( ) -> Option { if let Some((_, duration_to_epoch)) = beacon_chain.duration_to_next_digest() { let duration_to_subscription = duration_to_epoch.saturating_sub(Duration::from_secs( - beacon_chain.spec.seconds_per_slot * SUBSCRIBE_DELAY_SLOTS, + beacon_chain.spec.get_slot_duration().as_secs() * SUBSCRIBE_DELAY_SLOTS, )); if !duration_to_subscription.is_zero() { return Some(tokio::time::sleep(duration_to_subscription)); diff --git a/beacon_node/src/config.rs b/beacon_node/src/config.rs index 26dd3b6642..2e5a045502 100644 --- a/beacon_node/src/config.rs +++ b/beacon_node/src/config.rs @@ -766,10 +766,7 @@ pub fn get_config( client_config.chain.prepare_payload_lookahead = clap_utils::parse_optional(cli_args, "prepare-payload-lookahead")? .map(Duration::from_millis) - .unwrap_or_else(|| { - Duration::from_secs(spec.seconds_per_slot) - / DEFAULT_PREPARE_PAYLOAD_LOOKAHEAD_FACTOR - }); + .unwrap_or_else(|| spec.get_slot_duration() / DEFAULT_PREPARE_PAYLOAD_LOOKAHEAD_FACTOR); client_config.chain.always_prepare_payload = cli_args.get_flag("always-prepare-payload"); diff --git a/book/src/api_vc_endpoints.md b/book/src/api_vc_endpoints.md index d128b13b2f..cc9dd362f8 100644 --- a/book/src/api_vc_endpoints.md +++ b/book/src/api_vc_endpoints.md @@ -249,6 +249,7 @@ Example Response Body "FULU_FORK_VERSION": "0x70000910", "FULU_FORK_EPOCH": "18446744073709551615", "SECONDS_PER_SLOT": "12", + "SLOT_DURATION_MS": "12000", "SECONDS_PER_ETH1_BLOCK": "12", "MIN_VALIDATOR_WITHDRAWABILITY_DELAY": "256", "SHARD_COMMITTEE_PERIOD": "256", diff --git a/common/eth2_network_config/built_in_network_configs/chiado/config.yaml b/common/eth2_network_config/built_in_network_configs/chiado/config.yaml index eafe1ad38c..f0c04d891a 100644 --- a/common/eth2_network_config/built_in_network_configs/chiado/config.yaml +++ b/common/eth2_network_config/built_in_network_configs/chiado/config.yaml @@ -58,6 +58,8 @@ GLOAS_FORK_EPOCH: 18446744073709551615 # --------------------------------------------------------------- # 5 seconds SECONDS_PER_SLOT: 5 +# 5 seconds +SLOT_DURATION_MS: 5000 # 6 (estimate from xDai mainnet) SECONDS_PER_ETH1_BLOCK: 6 # 2**8 (= 256) epochs ~5.7 hours @@ -66,6 +68,18 @@ MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256 SHARD_COMMITTEE_PERIOD: 256 # 2**10 (= 1024) ~1.4 hour ETH1_FOLLOW_DISTANCE: 1024 +# 1667 basis points, ~17% of SLOT_DURATION_MS +PROPOSER_REORG_CUTOFF_BPS: 1667 +# 3333 basis points, ~33% of SLOT_DURATION_MS +ATTESTATION_DUE_BPS: 3333 +# 6667 basis points, ~67% of SLOT_DURATION_MS +AGGREGATE_DUE_BPS: 6667 + +# Altair +# 3333 basis points, ~33% of SLOT_DURATION_MS +SYNC_MESSAGE_DUE_BPS: 3333 +# 6667 basis points, ~67% of SLOT_DURATION_MS +CONTRIBUTION_DUE_BPS: 6667 # Validator cycle # --------------------------------------------------------------- diff --git a/common/eth2_network_config/built_in_network_configs/gnosis/config.yaml b/common/eth2_network_config/built_in_network_configs/gnosis/config.yaml index 2beeb45b25..34313aa393 100644 --- a/common/eth2_network_config/built_in_network_configs/gnosis/config.yaml +++ b/common/eth2_network_config/built_in_network_configs/gnosis/config.yaml @@ -55,6 +55,8 @@ GLOAS_FORK_EPOCH: 18446744073709551615 # --------------------------------------------------------------- # 5 seconds SECONDS_PER_SLOT: 5 +# 5 seconds +SLOT_DURATION_MS: 5000 # 6 (estimate from Gnosis Chain) SECONDS_PER_ETH1_BLOCK: 6 # 2**8 (= 256) epochs ~8 hours @@ -63,6 +65,18 @@ MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256 SHARD_COMMITTEE_PERIOD: 256 # 2**10 (= 1024) ~1.4 hour ETH1_FOLLOW_DISTANCE: 1024 +# 1667 basis points, ~17% of SLOT_DURATION_MS +PROPOSER_REORG_CUTOFF_BPS: 1667 +# 3333 basis points, ~33% of SLOT_DURATION_MS +ATTESTATION_DUE_BPS: 3333 +# 6667 basis points, ~67% of SLOT_DURATION_MS +AGGREGATE_DUE_BPS: 6667 + +# Altair +# 3333 basis points, ~33% of SLOT_DURATION_MS +SYNC_MESSAGE_DUE_BPS: 3333 +# 6667 basis points, ~67% of SLOT_DURATION_MS +CONTRIBUTION_DUE_BPS: 6667 # Validator cycle # --------------------------------------------------------------- diff --git a/common/slot_clock/src/lib.rs b/common/slot_clock/src/lib.rs index e51bc3f647..abfab547b9 100644 --- a/common/slot_clock/src/lib.rs +++ b/common/slot_clock/src/lib.rs @@ -9,7 +9,6 @@ pub use crate::manual_slot_clock::ManualSlotClock; pub use crate::system_time_slot_clock::SystemTimeSlotClock; pub use metrics::scrape_for_metrics; pub use types::Slot; -use types::consts::bellatrix::INTERVALS_PER_SLOT; /// A clock that reports the current slot. /// @@ -77,30 +76,6 @@ pub trait SlotClock: Send + Sync + Sized + Clone { .or_else(|| Some(self.genesis_slot())) } - /// Returns the delay between the start of the slot and when unaggregated attestations should be - /// produced. - fn unagg_attestation_production_delay(&self) -> Duration { - self.slot_duration() / INTERVALS_PER_SLOT as u32 - } - - /// Returns the delay between the start of the slot and when sync committee messages should be - /// produced. - fn sync_committee_message_production_delay(&self) -> Duration { - self.slot_duration() / INTERVALS_PER_SLOT as u32 - } - - /// Returns the delay between the start of the slot and when aggregated attestations should be - /// produced. - fn agg_attestation_production_delay(&self) -> Duration { - self.slot_duration() * 2 / INTERVALS_PER_SLOT as u32 - } - - /// Returns the delay between the start of the slot and when partially aggregated `SyncCommitteeContribution` should be - /// produced. - fn sync_committee_contribution_production_delay(&self) -> Duration { - self.slot_duration() * 2 / INTERVALS_PER_SLOT as u32 - } - /// Returns the `Duration` since the start of the current `Slot` at seconds precision. Useful in determining whether to apply proposer boosts. fn seconds_from_current_slot_start(&self) -> Option { self.now_duration() @@ -134,13 +109,4 @@ pub trait SlotClock: Send + Sync + Sized + Clone { slot_clock.set_current_time(freeze_at); slot_clock } - - /// Returns the delay between the start of the slot and when a request for block components - /// missed over gossip in the current slot should be made via RPC. - /// - /// Currently set equal to 1/2 of the `unagg_attestation_production_delay`, but this may be - /// changed in the future. - fn single_lookup_delay(&self) -> Duration { - self.unagg_attestation_production_delay() / 2 - } } diff --git a/consensus/fork_choice/src/fork_choice.rs b/consensus/fork_choice/src/fork_choice.rs index 9a8cae0c36..9744b9fa08 100644 --- a/consensus/fork_choice/src/fork_choice.rs +++ b/consensus/fork_choice/src/fork_choice.rs @@ -21,7 +21,6 @@ use types::{ AbstractExecPayload, AttestationShufflingId, AttesterSlashingRef, BeaconBlockRef, BeaconState, BeaconStateError, ChainSpec, Checkpoint, Epoch, EthSpec, ExecPayload, ExecutionBlockHash, Hash256, IndexedAttestationRef, RelativeEpoch, SignedBeaconBlock, Slot, - consts::bellatrix::INTERVALS_PER_SLOT, }; #[derive(Debug)] @@ -77,6 +76,7 @@ pub enum Error { }, UnrealizedVoteProcessing(state_processing::EpochProcessingError), ValidatorStatuses(BeaconStateError), + ChainSpecError(String), } impl From for Error { @@ -727,9 +727,10 @@ where })); } + let attestation_threshold = spec.get_unaggregated_attestation_due(); + // Add proposer score boost if the block is timely. - let is_before_attesting_interval = - block_delay < Duration::from_secs(spec.seconds_per_slot / INTERVALS_PER_SLOT); + let is_before_attesting_interval = block_delay < attestation_threshold; let is_first_block = self.fc_store.proposer_boost_root().is_zero(); if current_slot == block.slot() && is_before_attesting_interval && is_first_block { diff --git a/consensus/state_processing/src/per_block_processing.rs b/consensus/state_processing/src/per_block_processing.rs index cd1c1b9849..f8d86cd06d 100644 --- a/consensus/state_processing/src/per_block_processing.rs +++ b/consensus/state_processing/src/per_block_processing.rs @@ -510,7 +510,7 @@ pub fn compute_timestamp_at_slot( ) -> Result { let slots_since_genesis = block_slot.as_u64().safe_sub(spec.genesis_slot.as_u64())?; slots_since_genesis - .safe_mul(spec.seconds_per_slot) + .safe_mul(spec.get_slot_duration().as_secs()) .and_then(|since_genesis| state.genesis_time().safe_add(since_genesis)) } diff --git a/consensus/types/src/core/chain_spec.rs b/consensus/types/src/core/chain_spec.rs index a90a39a69d..6d25e3baf4 100644 --- a/consensus/types/src/core/chain_spec.rs +++ b/consensus/types/src/core/chain_spec.rs @@ -12,6 +12,7 @@ use ssz_types::RuntimeVariableList; use tree_hash::TreeHash; use crate::{ + consts::bellatrix::BASIS_POINTS, core::{ APPLICATION_DOMAIN_BUILDER, Address, ApplicationDomain, EnrForkId, Epoch, EthSpec, EthSpecId, ExecutionBlockHash, Hash256, MainnetEthSpec, Slot, Uint256, @@ -95,8 +96,10 @@ pub struct ChainSpec { * Time parameters */ pub genesis_delay: u64, + // TODO deprecate seconds_per_slot pub seconds_per_slot: u64, - pub slot_duration_ms: u64, + // Private so that this value can't get changed except via the `set_slot_duration_ms` function. + slot_duration_ms: u64, pub min_attestation_inclusion_delay: u64, pub min_seed_lookahead: Epoch, pub max_seed_lookahead: Epoch, @@ -109,6 +112,14 @@ pub struct ChainSpec { pub sync_message_due_bps: u64, pub contribution_due_bps: u64, + /* + * Derived time values (computed at startup via `compute_derived_values()`) + */ + pub unaggregated_attestation_due: Duration, + pub aggregate_attestation_due: Duration, + pub sync_message_due: Duration, + pub contribution_and_proof_due: Duration, + /* * Reward and penalty quotients */ @@ -851,6 +862,110 @@ impl ChainSpec { ) } + /// Get the duration into a slot in which an unaggregated attestation is due. + /// Returns the pre-computed value from `compute_derived_values()`. + pub fn get_unaggregated_attestation_due(&self) -> Duration { + self.unaggregated_attestation_due + } + + /// Get the duration into a slot in which an aggregated attestation is due. + /// Returns the pre-computed value from `compute_derived_values()`. + pub fn get_aggregate_attestation_due(&self) -> Duration { + self.aggregate_attestation_due + } + + /// Get the duration into a slot in which a `SignedContributionAndProof` is due. + /// Returns the pre-computed value from `compute_derived_values()`. + pub fn get_contribution_message_due(&self) -> Duration { + self.contribution_and_proof_due + } + + /// Get the duration into a slot in which a sync committee message is due. + /// Returns the pre-computed value from `compute_derived_values()`. + pub fn get_sync_message_due(&self) -> Duration { + self.sync_message_due + } + + /// Calculate the duration into a slot for a given slot component + fn compute_slot_component_duration( + &self, + component_basis_points: u64, + ) -> Result { + Ok(Duration::from_millis( + component_basis_points + .safe_mul(self.slot_duration_ms)? + .safe_div(BASIS_POINTS)?, + )) + } + + /// Get the duration of a slot + pub fn get_slot_duration(&self) -> Duration { + Duration::from_millis(self.slot_duration_ms) + } + + /// Set the duration of a slot (in ms). + pub fn set_slot_duration_ms(mut self, slot_duration_ms: u64) -> Self { + self.slot_duration_ms = slot_duration_ms; + self.compute_derived_values::() + } + + /// Compute values that are derived from other config values. + /// + /// Must be called after loading or modifying a ChainSpec's fields. + /// + /// Panics if any computation fails (indicates invalid config). + pub fn compute_derived_values(mut self) -> Self { + assert!( + self.attestation_due_bps <= BASIS_POINTS, + "invalid chain spec: attestation_due_bps ({}) exceeds slot duration", + self.attestation_due_bps + ); + assert!( + self.aggregate_due_bps <= BASIS_POINTS, + "invalid chain spec: aggregate_due_bps ({}) exceeds slot duration", + self.aggregate_due_bps + ); + assert!( + self.sync_message_due_bps <= BASIS_POINTS, + "invalid chain spec: sync_message_due_bps ({}) exceeds slot duration", + self.sync_message_due_bps + ); + assert!( + self.contribution_due_bps <= BASIS_POINTS, + "invalid chain spec: contribution_due_bps ({}) exceeds slot duration", + self.contribution_due_bps + ); + + self.unaggregated_attestation_due = self + .compute_slot_component_duration(self.attestation_due_bps) + .expect("invalid chain spec: cannot compute unaggregated_attestation_due"); + self.aggregate_attestation_due = self + .compute_slot_component_duration(self.aggregate_due_bps) + .expect("invalid chain spec: cannot compute aggregate_attestation_due"); + self.sync_message_due = self + .compute_slot_component_duration(self.sync_message_due_bps) + .expect("invalid chain spec: cannot compute sync_message_due"); + self.contribution_and_proof_due = self + .compute_slot_component_duration(self.contribution_due_bps) + .expect("invalid chain spec: cannot compute contribution_and_proof_due"); + + self.attestation_subnet_prefix_bits = compute_attestation_subnet_prefix_bits( + self.attestation_subnet_count, + self.attestation_subnet_extra_bits, + ); + + self.max_blocks_by_root_request = + max_blocks_by_root_request_common(self.max_request_blocks); + self.max_blocks_by_root_request_deneb = + max_blocks_by_root_request_common(self.max_request_blocks_deneb); + self.max_blobs_by_root_request = + max_blobs_by_root_request_common(self.max_request_blob_sidecars); + self.max_data_columns_by_root_request = + max_data_columns_by_root_request_common::(self.max_request_blocks_deneb); + + self + } + /// Returns the slot at which the proposer shuffling was decided. /// /// The block root at this slot can be used to key the proposer shuffling for the given epoch. @@ -956,6 +1071,14 @@ impl ChainSpec { sync_message_due_bps: 3333, contribution_due_bps: 6667, + /* + * Derived time values (set by `compute_derived_values()`) + */ + unaggregated_attestation_due: Duration::from_millis(3999), + aggregate_attestation_due: Duration::from_millis(8000), + sync_message_due: Duration::from_millis(3999), + contribution_and_proof_due: Duration::from_millis(8000), + /* * Reward and penalty quotients */ @@ -1201,6 +1324,7 @@ impl ChainSpec { shard_committee_period: 64, genesis_delay: 300, seconds_per_slot: 6, + slot_duration_ms: 6000, inactivity_penalty_quotient: u64::checked_pow(2, 25).expect("pow does not overflow"), min_slashing_penalty_quotient: 64, proportional_slashing_multiplier: 2, @@ -1245,6 +1369,16 @@ impl ChainSpec { // Gloas gloas_fork_version: [0x07, 0x00, 0x00, 0x01], gloas_fork_epoch: None, + + /* + * Derived time values (set by `compute_derived_values()`) + * Precomputed for 6000ms slot: 3333 bps = 1999ms, 6667 bps = 4000ms + */ + unaggregated_attestation_due: Duration::from_millis(1999), + aggregate_attestation_due: Duration::from_millis(4000), + sync_message_due: Duration::from_millis(1999), + contribution_and_proof_due: Duration::from_millis(4000), + // Other network_id: 2, // lighthouse testnet network id deposit_chain_id: 5, @@ -1328,8 +1462,15 @@ impl ChainSpec { proposer_reorg_cutoff_bps: 1667, attestation_due_bps: 3333, aggregate_due_bps: 6667, - sync_message_due_bps: 3333, - contribution_due_bps: 6667, + + /* + * Derived time values (set by `compute_derived_values()`) + * Precomputed for 5000ms slot: 3333 bps = 1666ms, 6667 bps = 3333ms + */ + unaggregated_attestation_due: Duration::from_millis(1666), + aggregate_attestation_due: Duration::from_millis(3333), + sync_message_due: Duration::from_millis(1666), + contribution_and_proof_due: Duration::from_millis(3333), /* * Reward and penalty quotients @@ -1398,6 +1539,8 @@ impl ChainSpec { domain_contribution_and_proof: 9, altair_fork_version: [0x01, 0x00, 0x00, 0x64], altair_fork_epoch: Some(Epoch::new(512)), + sync_message_due_bps: 3333, + contribution_due_bps: 6667, /* * Bellatrix hard fork params @@ -1754,6 +1897,9 @@ pub struct Config { #[serde(with = "serde_utils::quoted_u64")] seconds_per_slot: u64, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + slot_duration_ms: Option>, #[serde(with = "serde_utils::quoted_u64")] seconds_per_eth1_block: u64, #[serde(with = "serde_utils::quoted_u64")] @@ -1889,6 +2035,22 @@ pub struct Config { #[serde(default = "default_min_epochs_for_data_column_sidecars_requests")] #[serde(with = "serde_utils::quoted_u64")] min_epochs_for_data_column_sidecars_requests: u64, + + #[serde(default = "default_proposer_reorg_cutoff_bps")] + #[serde(with = "serde_utils::quoted_u64")] + proposer_reorg_cutoff_bps: u64, + #[serde(default = "default_attestation_due_bps")] + #[serde(with = "serde_utils::quoted_u64")] + attestation_due_bps: u64, + #[serde(default = "default_aggregate_due_bps")] + #[serde(with = "serde_utils::quoted_u64")] + aggregate_due_bps: u64, + #[serde(default = "default_sync_message_due_bps")] + #[serde(with = "serde_utils::quoted_u64")] + sync_message_due_bps: u64, + #[serde(default = "default_contribution_due_bps")] + #[serde(with = "serde_utils::quoted_u64")] + contribution_due_bps: u64, } fn default_bellatrix_fork_version() -> [u8; 4] { @@ -2094,6 +2256,26 @@ const fn default_min_epochs_for_data_column_sidecars_requests() -> u64 { 4096 } +const fn default_proposer_reorg_cutoff_bps() -> u64 { + 1667 +} + +const fn default_attestation_due_bps() -> u64 { + 3333 +} + +const fn default_aggregate_due_bps() -> u64 { + 6667 +} + +const fn default_sync_message_due_bps() -> u64 { + 3333 +} + +const fn default_contribution_due_bps() -> u64 { + 6667 +} + fn max_blocks_by_root_request_common(max_request_blocks: u64) -> usize { let max_request_blocks = max_request_blocks as usize; RuntimeVariableList::::new( @@ -2257,6 +2439,9 @@ impl Config { .map(|epoch| MaybeQuoted { value: epoch }), seconds_per_slot: spec.seconds_per_slot, + slot_duration_ms: Some(MaybeQuoted { + value: spec.slot_duration_ms, + }), seconds_per_eth1_block: spec.seconds_per_eth1_block, min_validator_withdrawability_delay: spec.min_validator_withdrawability_delay, shard_committee_period: spec.shard_committee_period, @@ -2313,6 +2498,12 @@ impl Config { balance_per_additional_custody_group: spec.balance_per_additional_custody_group, min_epochs_for_data_column_sidecars_requests: spec .min_epochs_for_data_column_sidecars_requests, + + proposer_reorg_cutoff_bps: spec.proposer_reorg_cutoff_bps, + attestation_due_bps: spec.attestation_due_bps, + aggregate_due_bps: spec.aggregate_due_bps, + sync_message_due_bps: spec.sync_message_due_bps, + contribution_due_bps: spec.contribution_due_bps, } } @@ -2350,6 +2541,7 @@ impl Config { gloas_fork_version, gloas_fork_epoch, seconds_per_slot, + slot_duration_ms, seconds_per_eth1_block, min_validator_withdrawability_delay, shard_committee_period, @@ -2398,13 +2590,18 @@ impl Config { validator_custody_requirement, balance_per_additional_custody_group, min_epochs_for_data_column_sidecars_requests, + proposer_reorg_cutoff_bps, + attestation_due_bps, + aggregate_due_bps, + sync_message_due_bps, + contribution_due_bps, } = self; if preset_base != E::spec_name().to_string().as_str() { return None; } - Some(ChainSpec { + let spec = ChainSpec { config_name: config_name.clone(), min_genesis_active_validator_count, min_genesis_time, @@ -2425,6 +2622,9 @@ impl Config { gloas_fork_version, gloas_fork_epoch: gloas_fork_epoch.map(|q| q.value), seconds_per_slot, + slot_duration_ms: slot_duration_ms + .map(|q| q.value) + .unwrap_or_else(|| seconds_per_slot.saturating_mul(1000)), seconds_per_eth1_block, min_validator_withdrawability_delay, shard_committee_period, @@ -2453,11 +2653,6 @@ impl Config { resp_timeout, message_domain_invalid_snappy, message_domain_valid_snappy, - // Compute attestation_subnet_prefix_bits dynamically - attestation_subnet_prefix_bits: compute_attestation_subnet_prefix_bits( - attestation_subnet_count, - attestation_subnet_extra_bits, - ), max_request_blocks, attestation_propagation_slot_range, maximum_gossip_clock_disparity, @@ -2474,16 +2669,6 @@ impl Config { max_request_blob_sidecars_electra, blob_sidecar_subnet_count_electra, - // We need to re-derive any values that might have changed in the config. - max_blocks_by_root_request: max_blocks_by_root_request_common(max_request_blocks), - max_blocks_by_root_request_deneb: max_blocks_by_root_request_common( - max_request_blocks_deneb, - ), - max_blobs_by_root_request: max_blobs_by_root_request_common(max_request_blob_sidecars), - max_data_columns_by_root_request: max_data_columns_by_root_request_common::( - max_request_blocks_deneb, - ), - number_of_custody_groups, data_column_sidecar_subnet_count, samples_per_slot, @@ -2493,8 +2678,15 @@ impl Config { balance_per_additional_custody_group, min_epochs_for_data_column_sidecars_requests, + proposer_reorg_cutoff_bps, + attestation_due_bps, + aggregate_due_bps, + sync_message_due_bps, + contribution_due_bps, + ..chain_spec.clone() - }) + }; + Some(spec.compute_derived_values::()) } } @@ -2698,6 +2890,7 @@ mod yaml_tests { GENESIS_FORK_VERSION: 0x10355025 GENESIS_DELAY: 60 SECONDS_PER_SLOT: 12 + SLOT_DURATION_MS: 12000 SECONDS_PER_ETH1_BLOCK: 12 MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256 SHARD_COMMITTEE_PERIOD: 256 @@ -2850,6 +3043,7 @@ mod yaml_tests { GENESIS_FORK_VERSION: 0x10355025 GENESIS_DELAY: 60 SECONDS_PER_SLOT: 12 + SLOT_DURATION_MS: 12000 SECONDS_PER_ETH1_BLOCK: 12 MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256 SHARD_COMMITTEE_PERIOD: 256 @@ -2965,6 +3159,7 @@ mod yaml_tests { SHARDING_FORK_VERSION: 0x03000000 SHARDING_FORK_EPOCH: 18446744073709551615 SECONDS_PER_SLOT: 12 + SLOT_DURATION_MS: 12000 SECONDS_PER_ETH1_BLOCK: 14 MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256 SHARD_COMMITTEE_PERIOD: 256 @@ -3135,4 +3330,138 @@ mod yaml_tests { ); } } + + #[test] + fn test_slot_component_duration_calculations() { + let spec = ChainSpec::mainnet().compute_derived_values::(); + + // Test unaggregated attestation (3333 bps = 33.33% of 12s = 4s) + let unagg_due = spec.get_unaggregated_attestation_due(); + assert_eq!(unagg_due, Duration::from_millis(3999)); // 12000 * 3333 / 10000 + + // Test aggregate attestation (6667 bps = 66.67% of 12s = 8s) + let agg_due = spec.get_aggregate_attestation_due(); + assert_eq!(agg_due, Duration::from_millis(8000)); // 12000 * 6667 / 10000 + + // Test sync message (3333 bps = 33.33% of 12s = 4s) + let sync_msg_due = spec.get_sync_message_due(); + assert_eq!(sync_msg_due, Duration::from_millis(3999)); // 12000 * 3333 / 10000 + + // Test contribution message (6667 bps = 66.67% of 12s = 8s) + let contribution_due = spec.get_contribution_message_due(); + assert_eq!(contribution_due, Duration::from_millis(8000)); // 12000 * 6667 / 10000 + + // Test slot duration + let slot_duration = spec.get_slot_duration(); + assert_eq!(slot_duration, Duration::from_millis(12000)); + assert_eq!(slot_duration, Duration::from_secs(spec.seconds_per_slot)); + + // Test edge cases with custom spec + let mut custom_spec = spec.clone(); + + // Edge case: 0 bps should give 0 duration + custom_spec.attestation_due_bps = 0; + let custom_spec = custom_spec.compute_derived_values::(); + let zero_due = custom_spec.get_unaggregated_attestation_due(); + assert_eq!(zero_due, Duration::from_millis(0)); + + // Edge case: 10000 bps (100%) should give full slot duration + let mut custom_spec = custom_spec; + custom_spec.attestation_due_bps = 10_000; + let custom_spec = custom_spec.compute_derived_values::(); + let full_due = custom_spec.get_unaggregated_attestation_due(); + assert_eq!(full_due, Duration::from_millis(12000)); + + // Edge case: 5000 bps (50%) should give half slot duration + let mut custom_spec = custom_spec; + custom_spec.attestation_due_bps = 5_000; + let custom_spec = custom_spec.compute_derived_values::(); + let half_due = custom_spec.get_unaggregated_attestation_due(); + assert_eq!(half_due, Duration::from_millis(6000)); + + // Test with different slot duration (Gnosis: 5s slots) + let mut custom_spec = custom_spec; + custom_spec.slot_duration_ms = 5000; + custom_spec.attestation_due_bps = 3333; + let custom_spec = custom_spec.compute_derived_values::(); + let gnosis_due = custom_spec.get_unaggregated_attestation_due(); + assert_eq!(gnosis_due, Duration::from_millis(1666)); // 5000 * 3333 / 10000 + + // Test with very small slot duration + let mut custom_spec = custom_spec; + custom_spec.slot_duration_ms = 1000; // 1 second + custom_spec.attestation_due_bps = 3333; + let custom_spec = custom_spec.compute_derived_values::(); + let small_due = custom_spec.get_unaggregated_attestation_due(); + assert_eq!(small_due, Duration::from_millis(333)); // 1000 * 3333 / 10000 + + // Test rounding behavior with non-divisible values + let mut custom_spec = custom_spec; + custom_spec.slot_duration_ms = 12000; + custom_spec.attestation_due_bps = 1; // 0.01% + let custom_spec = custom_spec.compute_derived_values::(); + let tiny_due = custom_spec.get_unaggregated_attestation_due(); + assert_eq!(tiny_due, Duration::from_millis(1)); // 12000 * 1 / 10000 = 1.2 -> 1 + } + + #[test] + fn test_default_duration_values_without_compute_derived_values() { + // Verify that mainnet, minimal, and gnosis have correct pre-computed defaults + // without needing to call compute_derived_values() + let mainnet = ChainSpec::mainnet(); + assert_eq!( + mainnet.get_unaggregated_attestation_due(), + Duration::from_millis(3999) + ); + assert_eq!( + mainnet.get_aggregate_attestation_due(), + Duration::from_millis(8000) + ); + assert_eq!(mainnet.get_sync_message_due(), Duration::from_millis(3999)); + assert_eq!( + mainnet.get_contribution_message_due(), + Duration::from_millis(8000) + ); + + // Minimal spec: 6000ms slots, 3333 bps = 1999ms, 6667 bps = 4000ms + let minimal = ChainSpec::minimal(); + assert_eq!( + minimal.get_unaggregated_attestation_due(), + Duration::from_millis(1999) + ); + assert_eq!( + minimal.get_aggregate_attestation_due(), + Duration::from_millis(4000) + ); + assert_eq!(minimal.get_sync_message_due(), Duration::from_millis(1999)); + assert_eq!( + minimal.get_contribution_message_due(), + Duration::from_millis(4000) + ); + + // Gnosis spec: 5000ms slots, 3333 bps = 1666ms, 6667 bps = 3333ms + let gnosis = ChainSpec::gnosis(); + assert_eq!( + gnosis.get_unaggregated_attestation_due(), + Duration::from_millis(1666) + ); + assert_eq!( + gnosis.get_aggregate_attestation_due(), + Duration::from_millis(3333) + ); + assert_eq!(gnosis.get_sync_message_due(), Duration::from_millis(1666)); + assert_eq!( + gnosis.get_contribution_message_due(), + Duration::from_millis(3333) + ); + } + + #[test] + #[should_panic(expected = "exceeds slot duration")] + fn test_compute_derived_values_panics_on_invalid_bps_values() { + let mut spec = ChainSpec::mainnet(); + // 15000 bps = 150% of slot duration, which is invalid + spec.attestation_due_bps = 15000; + spec.compute_derived_values::(); + } } diff --git a/consensus/types/src/core/consts.rs b/consensus/types/src/core/consts.rs index 2c67657ee6..0d4c0591cb 100644 --- a/consensus/types/src/core/consts.rs +++ b/consensus/types/src/core/consts.rs @@ -20,7 +20,7 @@ pub mod altair { pub const NUM_FLAG_INDICES: usize = 3; } pub mod bellatrix { - pub const INTERVALS_PER_SLOT: u64 = 3; + pub const BASIS_POINTS: u64 = 10_000; } pub mod deneb { pub use kzg::VERSIONED_HASH_VERSION_KZG; diff --git a/lighthouse/environment/tests/testnet_dir/config.yaml b/lighthouse/environment/tests/testnet_dir/config.yaml index 7cfeb746f2..f155fac826 100644 --- a/lighthouse/environment/tests/testnet_dir/config.yaml +++ b/lighthouse/environment/tests/testnet_dir/config.yaml @@ -47,6 +47,8 @@ TRANSITION_TOTAL_DIFFICULTY: 4294967296 # --------------------------------------------------------------- # 12 seconds SECONDS_PER_SLOT: 12 +# 12 seconds +SLOT_DURATION_MS: 12000 # 14 (estimate from Eth1 mainnet) SECONDS_PER_ETH1_BLOCK: 14 # 2**8 (= 256) epochs ~27 hours @@ -55,6 +57,18 @@ MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256 SHARD_COMMITTEE_PERIOD: 256 # 2**11 (= 2,048) Eth1 blocks ~8 hours ETH1_FOLLOW_DISTANCE: 2048 +# 1667 basis points, ~17% of SLOT_DURATION_MS +PROPOSER_REORG_CUTOFF_BPS: 1667 +# 3333 basis points, ~33% of SLOT_DURATION_MS +ATTESTATION_DUE_BPS: 3333 +# 6667 basis points, ~67% of SLOT_DURATION_MS +AGGREGATE_DUE_BPS: 6667 + +# Altair +# 3333 basis points, ~33% of SLOT_DURATION_MS +SYNC_MESSAGE_DUE_BPS: 3333 +# 6667 basis points, ~67% of SLOT_DURATION_MS +CONTRIBUTION_DUE_BPS: 6667 # Validator cycle diff --git a/lighthouse/tests/beacon_node.rs b/lighthouse/tests/beacon_node.rs index 69817e5c9d..a2fad31f65 100644 --- a/lighthouse/tests/beacon_node.rs +++ b/lighthouse/tests/beacon_node.rs @@ -2332,7 +2332,7 @@ fn enable_proposer_re_orgs_default() { DEFAULT_RE_ORG_MAX_EPOCHS_SINCE_FINALIZATION, ); assert_eq!( - config.chain.re_org_cutoff(12), + config.chain.re_org_cutoff(Duration::from_secs(12)), Duration::from_secs(12) / DEFAULT_RE_ORG_CUTOFF_DENOMINATOR ); }); @@ -2384,7 +2384,10 @@ fn proposer_re_org_cutoff() { .flag("proposer-reorg-cutoff", Some("500")) .run_with_zero_port() .with_config(|config| { - assert_eq!(config.chain.re_org_cutoff(12), Duration::from_millis(500)) + assert_eq!( + config.chain.re_org_cutoff(Duration::from_secs(12)), + Duration::from_millis(500) + ) }); } diff --git a/scripts/local_testnet/network_params.yaml b/scripts/local_testnet/network_params.yaml index a048674e63..0c36e5c49c 100644 --- a/scripts/local_testnet/network_params.yaml +++ b/scripts/local_testnet/network_params.yaml @@ -18,7 +18,7 @@ participants: count: 2 network_params: fulu_fork_epoch: 0 - seconds_per_slot: 6 + slot_duration_ms: 3000 snooper_enabled: false global_log_level: debug additional_services: diff --git a/scripts/tests/doppelganger_protection.sh b/scripts/tests/doppelganger_protection.sh index 9009d49d58..e5ffee494f 100755 --- a/scripts/tests/doppelganger_protection.sh +++ b/scripts/tests/doppelganger_protection.sh @@ -9,7 +9,7 @@ NETWORK_PARAMS_FILE=$SCRIPT_DIR/network_params.yaml BEHAVIOR=$1 ENCLAVE_NAME=local-testnet-$BEHAVIOR -SECONDS_PER_SLOT=$(yq eval ".network_params.seconds_per_slot" $NETWORK_PARAMS_FILE) +SLOT_DURATION_MS=$(yq eval ".network_params.slot_duration_ms" $NETWORK_PARAMS_FILE) KEYS_PER_NODE=$(yq eval ".network_params.num_validator_keys_per_node" $NETWORK_PARAMS_FILE) LH_IMAGE_NAME=$(yq eval ".participants[0].cl_image" $NETWORK_PARAMS_FILE) @@ -56,7 +56,7 @@ GENESIS_DELAY=`curl -s $BN1_HTTP_ADDRESS/eth/v1/config/spec | jq '.data.GENESIS_ CURRENT_TIME=`date +%s` # Note: doppelganger protection can only be started post epoch 0 echo "Waiting until next epoch before starting the next validator client..." -DELAY=$(( $SECONDS_PER_SLOT * 32 + $GENESIS_DELAY + $MIN_GENESIS_TIME - $CURRENT_TIME)) +DELAY=$((($SLOT_DURATION_MS / 1000) * 32 + $GENESIS_DELAY + $MIN_GENESIS_TIME - $CURRENT_TIME)) sleep $DELAY # Use BN2 for the next validator client @@ -98,7 +98,7 @@ EOF # Check if doppelganger VC has stopped and exited. Exit code 1 means the check timed out and VC is still running. check_exit_cmd="until [ \$(get_service_status $service_name) != 'RUNNING' ]; do sleep 1; done" - doppelganger_exit=$(run_command_without_exit "timeout $(( $SECONDS_PER_SLOT * 32 * 2 )) bash -c \"$check_exit_cmd\"") + doppelganger_exit=$(run_command_without_exit "timeout $((($SLOT_DURATION_MS / 1000) * 32 * 2 )) bash -c \"$check_exit_cmd\"") if [[ $doppelganger_exit -eq 1 ]]; then echo "Test failed: expected doppelganger but VC is still running. Check the logs for details." @@ -148,7 +148,7 @@ EOF # # See: https://lighthouse-book.sigmaprime.io/api_validator_inclusion.html echo "Waiting three epochs..." - sleep $(( $SECONDS_PER_SLOT * 32 * 3 )) + sleep $((($SLOT_DURATION_MS / 1000) * 32 * 3 )) # Get VC4 validator keys keys_path=$SCRIPT_DIR/$ENCLAVE_NAME/node_4/validators @@ -176,7 +176,7 @@ EOF # # See: https://lighthouse-book.sigmaprime.io/api_validator_inclusion.html echo "Waiting two more epochs..." - sleep $(( $SECONDS_PER_SLOT * 32 * 2 )) + sleep $(( $SLOT_DURATION_MS / 1000 * 32 * 2 )) for val in 0x*; do [[ -e $val ]] || continue is_attester=$(run_command_without_exit "curl -s $BN1_HTTP_ADDRESS/lighthouse/validator_inclusion/5/$val | jq | grep -q '\"is_previous_epoch_target_attester\": true'") diff --git a/scripts/tests/genesis-sync-config-electra.yaml b/scripts/tests/genesis-sync-config-electra.yaml index 1d1ed4d315..0e41a5d165 100644 --- a/scripts/tests/genesis-sync-config-electra.yaml +++ b/scripts/tests/genesis-sync-config-electra.yaml @@ -11,7 +11,7 @@ participants: cl_image: lighthouse:local validator_count: 0 network_params: - seconds_per_slot: 6 + slot_duration_ms: 6000 electra_fork_epoch: 0 fulu_fork_epoch: 100000 # a really big number so this test stays in electra preset: "minimal" diff --git a/scripts/tests/genesis-sync-config-fulu.yaml b/scripts/tests/genesis-sync-config-fulu.yaml index 6d2c2647a9..5ca76a7736 100644 --- a/scripts/tests/genesis-sync-config-fulu.yaml +++ b/scripts/tests/genesis-sync-config-fulu.yaml @@ -20,7 +20,7 @@ participants: supernode: false validator_count: 0 network_params: - seconds_per_slot: 6 + slot_duration_ms: 6000 fulu_fork_epoch: 0 preset: "minimal" additional_services: diff --git a/scripts/tests/network_params.yaml b/scripts/tests/network_params.yaml index 35916ac1e4..30654deaae 100644 --- a/scripts/tests/network_params.yaml +++ b/scripts/tests/network_params.yaml @@ -10,7 +10,7 @@ participants: count: 4 network_params: fulu_fork_epoch: 0 - seconds_per_slot: 3 + slot_duration_ms: 3000 num_validator_keys_per_node: 20 global_log_level: debug snooper_enabled: false diff --git a/testing/ef_tests/src/cases/fork_choice.rs b/testing/ef_tests/src/cases/fork_choice.rs index a3c2fab468..45bed7c6cd 100644 --- a/testing/ef_tests/src/cases/fork_choice.rs +++ b/testing/ef_tests/src/cases/fork_choice.rs @@ -488,7 +488,7 @@ impl Tester { let since_genesis = tick .checked_sub(genesis_time) .ok_or_else(|| Error::FailedToParseTest("tick is prior to genesis".into()))?; - let slots_since_genesis = since_genesis / self.spec.seconds_per_slot; + let slots_since_genesis = since_genesis / self.spec.get_slot_duration().as_secs(); Ok(self.spec.genesis_slot + slots_since_genesis) } diff --git a/testing/simulator/src/basic_sim.rs b/testing/simulator/src/basic_sim.rs index 13bfcb5fc3..a9d0a0756b 100644 --- a/testing/simulator/src/basic_sim.rs +++ b/testing/simulator/src/basic_sim.rs @@ -14,7 +14,6 @@ use rayon::prelude::*; use std::cmp::max; use std::path::PathBuf; use std::sync::Arc; -use std::time::Duration; use environment::tracing_common; use tracing_subscriber::prelude::*; @@ -175,8 +174,11 @@ pub fn run_basic_sim(matches: &ArgMatches) -> Result<(), String> { let latest_fork_version = spec.electra_fork_version; let latest_fork_start_epoch = ELECTRA_FORK_EPOCH; - spec.seconds_per_slot /= speed_up_factor; - spec.seconds_per_slot = max(1, spec.seconds_per_slot); + let mut slot_duration_ms = spec.get_slot_duration().as_millis() as u64; + slot_duration_ms /= speed_up_factor; + slot_duration_ms = max(1_000, slot_duration_ms); + spec = spec.set_slot_duration_ms::(slot_duration_ms); + spec.genesis_delay = genesis_delay; spec.min_genesis_time = 0; spec.min_genesis_active_validator_count = total_validator_count as u64; @@ -188,7 +190,7 @@ pub fn run_basic_sim(matches: &ArgMatches) -> Result<(), String> { let spec = Arc::new(spec); env.eth2_config.spec = spec.clone(); - let slot_duration = Duration::from_secs(spec.seconds_per_slot); + let slot_duration = spec.get_slot_duration(); let slots_per_epoch = MinimalEthSpec::slots_per_epoch(); let initial_validator_count = spec.min_genesis_active_validator_count as usize; diff --git a/testing/simulator/src/fallback_sim.rs b/testing/simulator/src/fallback_sim.rs index 3d9a60abc7..06f4478c5e 100644 --- a/testing/simulator/src/fallback_sim.rs +++ b/testing/simulator/src/fallback_sim.rs @@ -15,12 +15,12 @@ use rayon::prelude::*; use std::cmp::max; use std::path::PathBuf; use std::sync::Arc; -use std::time::Duration; use tokio::time::sleep; use tracing::Level; use tracing_subscriber::prelude::*; use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt}; use types::{Epoch, EthSpec, MinimalEthSpec}; + const END_EPOCH: u64 = 16; const GENESIS_DELAY: u64 = 38; const ALTAIR_FORK_EPOCH: u64 = 0; @@ -179,8 +179,11 @@ pub fn run_fallback_sim(matches: &ArgMatches) -> Result<(), String> { let genesis_delay = GENESIS_DELAY; - spec.seconds_per_slot /= speed_up_factor; - spec.seconds_per_slot = max(1, spec.seconds_per_slot); + let mut slot_duration_ms = spec.get_slot_duration().as_millis() as u64; + slot_duration_ms /= speed_up_factor; + slot_duration_ms = max(1_000, slot_duration_ms); + spec = spec.set_slot_duration_ms::(slot_duration_ms); + spec.genesis_delay = genesis_delay; spec.min_genesis_time = 0; spec.min_genesis_active_validator_count = total_validator_count as u64; @@ -193,7 +196,7 @@ pub fn run_fallback_sim(matches: &ArgMatches) -> Result<(), String> { let spec = Arc::new(spec); env.eth2_config.spec = spec.clone(); - let slot_duration = Duration::from_secs(spec.seconds_per_slot); + let slot_duration = spec.get_slot_duration(); let slots_per_epoch = MinimalEthSpec::slots_per_epoch(); let disconnection_epoch = 1; diff --git a/testing/simulator/src/local_network.rs b/testing/simulator/src/local_network.rs index 58d7e1372f..2beb9c0efc 100644 --- a/testing/simulator/src/local_network.rs +++ b/testing/simulator/src/local_network.rs @@ -73,23 +73,33 @@ fn default_mock_execution_config( if let Some(capella_fork_epoch) = spec.capella_fork_epoch { mock_execution_config.shanghai_time = Some( genesis_time - + spec.seconds_per_slot * E::slots_per_epoch() * capella_fork_epoch.as_u64(), + + (spec.get_slot_duration().as_secs()) + * E::slots_per_epoch() + * capella_fork_epoch.as_u64(), ) } if let Some(deneb_fork_epoch) = spec.deneb_fork_epoch { mock_execution_config.cancun_time = Some( - genesis_time + spec.seconds_per_slot * E::slots_per_epoch() * deneb_fork_epoch.as_u64(), + genesis_time + + (spec.get_slot_duration().as_secs()) + * E::slots_per_epoch() + * deneb_fork_epoch.as_u64(), ) } if let Some(electra_fork_epoch) = spec.electra_fork_epoch { mock_execution_config.prague_time = Some( genesis_time - + spec.seconds_per_slot * E::slots_per_epoch() * electra_fork_epoch.as_u64(), + + (spec.get_slot_duration().as_secs()) + * E::slots_per_epoch() + * electra_fork_epoch.as_u64(), ) } if let Some(fulu_fork_epoch) = spec.fulu_fork_epoch { mock_execution_config.osaka_time = Some( - genesis_time + spec.seconds_per_slot * E::slots_per_epoch() * fulu_fork_epoch.as_u64(), + genesis_time + + (spec.get_slot_duration().as_secs()) + * E::slots_per_epoch() + * fulu_fork_epoch.as_u64(), ) } diff --git a/validator_client/beacon_node_fallback/src/lib.rs b/validator_client/beacon_node_fallback/src/lib.rs index 2d75df2fa3..3c20e57200 100644 --- a/validator_client/beacon_node_fallback/src/lib.rs +++ b/validator_client/beacon_node_fallback/src/lib.rs @@ -476,9 +476,9 @@ impl BeaconNodeFallback { } let timeouts: Timeouts = if new_list.len() == 1 || use_long_timeouts { - Timeouts::set_all(Duration::from_secs(self.spec.seconds_per_slot)) + Timeouts::set_all(self.spec.get_slot_duration()) } else { - Timeouts::use_optimized_timeouts(Duration::from_secs(self.spec.seconds_per_slot)) + Timeouts::use_optimized_timeouts(self.spec.get_slot_duration()) }; let new_candidates: Vec = new_list diff --git a/validator_client/src/lib.rs b/validator_client/src/lib.rs index b3cd3425f3..2b863715d2 100644 --- a/validator_client/src/lib.rs +++ b/validator_client/src/lib.rs @@ -268,7 +268,7 @@ impl ProductionValidatorClient { let beacon_node_setup = |x: (usize, &SensitiveUrl)| { let i = x.0; let url = x.1; - let slot_duration = Duration::from_secs(context.eth2_config.spec.seconds_per_slot); + let slot_duration = context.eth2_config.spec.get_slot_duration(); let mut beacon_node_http_client_builder = ClientBuilder::new(); @@ -389,7 +389,7 @@ impl ProductionValidatorClient { let slot_clock = SystemTimeSlotClock::new( context.eth2_config.spec.genesis_slot, Duration::from_secs(genesis_time), - Duration::from_secs(context.eth2_config.spec.seconds_per_slot), + context.eth2_config.spec.get_slot_duration(), ); beacon_nodes.set_slot_clock(slot_clock.clone()); diff --git a/validator_client/validator_services/src/attestation_service.rs b/validator_client/validator_services/src/attestation_service.rs index 58b1acfcdf..326ec6d01e 100644 --- a/validator_client/validator_services/src/attestation_service.rs +++ b/validator_client/validator_services/src/attestation_service.rs @@ -144,7 +144,7 @@ impl AttestationService AttestationService AttestationService Result<(), String> { + fn spawn_attestation_tasks(&self) -> Result<(), String> { let slot = self.slot_clock.now().ok_or("Failed to read slot clock")?; let duration_to_next_slot = self .slot_clock @@ -247,7 +249,8 @@ impl AttestationService> = self diff --git a/validator_client/validator_services/src/notifier_service.rs b/validator_client/validator_services/src/notifier_service.rs index 7d73059f02..a8f73490c7 100644 --- a/validator_client/validator_services/src/notifier_service.rs +++ b/validator_client/validator_services/src/notifier_service.rs @@ -2,7 +2,7 @@ use crate::duties_service::DutiesService; use slot_clock::SlotClock; use std::sync::Arc; use task_executor::TaskExecutor; -use tokio::time::{Duration, sleep}; +use tokio::time::sleep; use tracing::{debug, error, info}; use types::{ChainSpec, EthSpec}; use validator_metrics::set_gauge; @@ -14,7 +14,7 @@ pub fn spawn_notifier( executor: TaskExecutor, spec: &ChainSpec, ) -> Result<(), String> { - let slot_duration = Duration::from_secs(spec.seconds_per_slot); + let slot_duration = spec.get_slot_duration(); let interval_fut = async move { loop { diff --git a/validator_client/validator_services/src/preparation_service.rs b/validator_client/validator_services/src/preparation_service.rs index 063b11512f..913b6ab4e9 100644 --- a/validator_client/validator_services/src/preparation_service.rs +++ b/validator_client/validator_services/src/preparation_service.rs @@ -8,7 +8,7 @@ use std::ops::Deref; use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; use task_executor::TaskExecutor; -use tokio::time::{Duration, sleep}; +use tokio::time::sleep; use tracing::{debug, error, info, warn}; use types::{ Address, ChainSpec, EthSpec, ProposerPreparationData, SignedValidatorRegistrationData, @@ -174,7 +174,7 @@ impl PreparationService Result<(), String> { - let slot_duration = Duration::from_secs(spec.seconds_per_slot); + let slot_duration = spec.get_slot_duration(); info!("Proposer preparation service started"); let executor = self.executor.clone(); @@ -214,7 +214,7 @@ impl PreparationService SyncCommitteeService SyncCommitteeService SyncCommitteeService Result<(), String> { + async fn spawn_contribution_tasks(&self) -> Result<(), String> { + let spec = &self.duties_service.spec; let slot = self.slot_clock.now().ok_or("Failed to read slot clock")?; let duration_to_next_slot = self .slot_clock @@ -151,7 +154,8 @@ impl SyncCommitteeService(genesis_time: u64, spec: &ChainSpec) -> Opt let slot_clock = SystemTimeSlotClock::new( spec.genesis_slot, Duration::from_secs(genesis_time), - Duration::from_secs(spec.seconds_per_slot), + spec.get_slot_duration(), ); slot_clock.now().map(|s| s.epoch(E::slots_per_epoch())) } diff --git a/validator_manager/src/list_validators.rs b/validator_manager/src/list_validators.rs index f7a09f8d8e..7cc31d1b7a 100644 --- a/validator_manager/src/list_validators.rs +++ b/validator_manager/src/list_validators.rs @@ -185,7 +185,9 @@ async fn run(config: ListConfig) -> Result {