diff --git a/crates/common/chain/lean/Cargo.toml b/crates/common/chain/lean/Cargo.toml index c105321b2..05a9e5942 100644 --- a/crates/common/chain/lean/Cargo.toml +++ b/crates/common/chain/lean/Cargo.toml @@ -13,11 +13,13 @@ version.workspace = true default = ["devnet2"] devnet2 = [ "ream-consensus-lean/devnet2", + "ream-consensus-misc/devnet2", "ream-fork-choice-lean/devnet2", "ream-network-state-lean/devnet2", ] devnet3 = [ "ream-consensus-lean/devnet3", + "ream-consensus-misc/devnet3", "ream-fork-choice-lean/devnet3", "ream-network-state-lean/devnet3", ] diff --git a/crates/common/chain/lean/src/service.rs b/crates/common/chain/lean/src/service.rs index 0fbfab1e8..6f55ec2ae 100644 --- a/crates/common/chain/lean/src/service.rs +++ b/crates/common/chain/lean/src/service.rs @@ -15,6 +15,7 @@ use ream_consensus_lean::{ block::{BlockWithSignatures, SignedBlockWithAttestation}, checkpoint::Checkpoint, }; +use ream_consensus_misc::constants::lean::INTERVALS_PER_SLOT; use ream_fork_choice_lean::store::LeanStoreWriter; use ream_metrics::{CURRENT_SLOT, set_int_gauge_vec}; use ream_network_spec::networks::lean_network_spec; @@ -127,14 +128,18 @@ impl LeanChainService { tokio::select! { _ = interval.tick() => { - if tick_count.is_multiple_of(4) { + if tick_count.is_multiple_of(INTERVALS_PER_SLOT) { self.sync_status = self.update_sync_status().await?; } if self.sync_status == SyncStatus::Synced { #[cfg(feature = "devnet2")] self.store.write().await.tick_interval(tick_count % 4 == 1).await.expect("Failed to tick interval"); #[cfg(feature = "devnet3")] - self.store.write().await.tick_interval(tick_count % 4 == 1, false).await.expect("Failed to tick interval"); + { + // TODO: update is_aggregator logic from devnet config + let is_aggregator = true; + self.store.write().await.tick_interval(tick_count.is_multiple_of(INTERVALS_PER_SLOT), is_aggregator).await.expect("Failed to tick interval"); + } self.step_head_sync(tick_count).await?; } @@ -394,9 +399,10 @@ impl LeanChainService { } async fn step_head_sync(&mut self, tick_count: u64) -> anyhow::Result<()> { - match tick_count % 4 { + let interval = tick_count % INTERVALS_PER_SLOT; + match interval { 0 => { - // First tick (t=0/4): Log current head state, including its + // First tick: Log current head state, including its // justification/finalization status. let (head, state_provider) = { let fork_choice = self.store.read().await; @@ -435,8 +441,15 @@ impl LeanChainService { finalized_root = head_state.latest_finalized.root, ); } + 1 => { + // Second tick: Prune old data. + if let Err(err) = self.prune_old_state(tick_count).await { + warn!("Pruning cycle failed (non-fatal): {err:?}"); + } + } + #[cfg(feature = "devnet2")] 2 => { - // Third tick (t=2/4): Compute the safe target. + // Third tick (devnet2): Compute the safe target. info!( slot = get_current_slot(), tick = tick_count, @@ -449,8 +462,9 @@ impl LeanChainService { .await .expect("Failed to update safe target"); } + #[cfg(feature = "devnet2")] 3 => { - // Fourth tick (t=3/4): Accept new attestations. + // Fourth tick (devnet2): Accept new attestations. info!( slot = get_current_slot(), tick = tick_count, @@ -463,13 +477,39 @@ impl LeanChainService { .await .expect("Failed to accept new attestations"); } - 1 => { - // Other ticks (t=0, t=1/4): Prune old data. - if let Err(err) = self.prune_old_state(tick_count).await { - warn!("Pruning cycle failed (non-fatal): {err:?}"); - } + #[cfg(feature = "devnet3")] + 3 => { + // Fourth tick (devnet3): Compute the safe target. + info!( + slot = get_current_slot(), + tick = tick_count, + "Computing safe target" + ); + self.store + .write() + .await + .update_safe_target() + .await + .expect("Failed to update safe target"); + } + #[cfg(feature = "devnet3")] + 4 => { + // Fifth tick (devnet3): Accept new attestations. + info!( + slot = get_current_slot(), + tick = tick_count, + "Accepting new attestations" + ); + self.store + .write() + .await + .accept_new_attestations() + .await + .expect("Failed to accept new attestations"); + } + _ => { + // Other ticks: Do nothing. } - _ => unreachable!("Tick count modulo 4 should be in 0..=3"), } Ok(()) } @@ -660,7 +700,7 @@ impl LeanChainService { #[cfg(feature = "devnet2")] self.store.write().await.on_tick(now, false).await?; #[cfg(feature = "devnet3")] - self.store.write().await.on_tick(now, false, false).await?; + self.store.write().await.on_tick(now, false, true).await?; } Ok(sync_status) @@ -980,7 +1020,7 @@ impl LeanChainService { self.store .write() .await - .on_gossip_attestation(signed_attestation, false) + .on_gossip_attestation(signed_attestation, true) .await?; Ok(()) diff --git a/crates/common/consensus/misc/Cargo.toml b/crates/common/consensus/misc/Cargo.toml index 708d4f57d..fb9c928cd 100644 --- a/crates/common/consensus/misc/Cargo.toml +++ b/crates/common/consensus/misc/Cargo.toml @@ -9,6 +9,10 @@ repository.workspace = true rust-version.workspace = true version.workspace = true +[features] +devnet2 = [] +devnet3 = [] + [dependencies] alloy-primitives.workspace = true alloy-rlp.workspace = true diff --git a/crates/common/consensus/misc/src/constants/lean.rs b/crates/common/consensus/misc/src/constants/lean.rs index 44576ef5b..8d747cc0c 100644 --- a/crates/common/consensus/misc/src/constants/lean.rs +++ b/crates/common/consensus/misc/src/constants/lean.rs @@ -1,7 +1,9 @@ -/// 3SF-mini divides a slot into 4 intervals. -/// Reference: https://github.com/ethereum/research/blob/d225a6775a9b184b5c1fd6c830cc58a375d9535f/3sf-mini/p2p.py#L77-L98 pub const ATTESTATION_COMMITTEE_COUNT: u64 = 1; + +#[cfg(feature = "devnet2")] pub const INTERVALS_PER_SLOT: u64 = 4; +#[cfg(feature = "devnet3")] +pub const INTERVALS_PER_SLOT: u64 = 5; pub const MAX_HISTORICAL_BLOCK_HASHES: u64 = 262144; -pub const SLOT_DURATION: u64 = 12; +pub const SLOT_DURATION: u64 = 4; pub const VALIDATOR_REGISTRY_LIMIT: u64 = 4096; diff --git a/crates/common/fork_choice/lean/Cargo.toml b/crates/common/fork_choice/lean/Cargo.toml index 3b0c436bc..b1c0f3a2b 100644 --- a/crates/common/fork_choice/lean/Cargo.toml +++ b/crates/common/fork_choice/lean/Cargo.toml @@ -13,11 +13,13 @@ version.workspace = true default = ["devnet2"] devnet2 = [ "ream-consensus-lean/devnet2", + "ream-consensus-misc/devnet2", "ream-network-state-lean/devnet2", "ream-storage/devnet2", ] devnet3 = [ "ream-consensus-lean/devnet3", + "ream-consensus-misc/devnet3", "ream-network-state-lean/devnet3", "ream-storage/devnet3", ] diff --git a/crates/common/fork_choice/lean/src/store.rs b/crates/common/fork_choice/lean/src/store.rs index cfa43577d..ab4cbdf6c 100644 --- a/crates/common/fork_choice/lean/src/store.rs +++ b/crates/common/fork_choice/lean/src/store.rs @@ -429,7 +429,8 @@ impl Store { is_aggregator: bool, ) -> anyhow::Result<()> { let time_delta_ms = (time - lean_network_spec().genesis_time) * 1000; - let tick_interval_time = time_delta_ms / lean_network_spec().seconds_per_slot * 1000 / 5; + let tick_interval_time = + time_delta_ms * INTERVALS_PER_SLOT / (lean_network_spec().seconds_per_slot * 1000); let time_provider = self.store.lock().await.time_provider(); while time_provider.get()? < tick_interval_time { diff --git a/crates/common/validator/lean/src/service.rs b/crates/common/validator/lean/src/service.rs index 054467251..9076f216a 100644 --- a/crates/common/validator/lean/src/service.rs +++ b/crates/common/validator/lean/src/service.rs @@ -11,6 +11,7 @@ use ream_consensus_lean::{ }; #[cfg(feature = "devnet3")] use ream_consensus_misc::constants::lean::ATTESTATION_COMMITTEE_COUNT; +use ream_consensus_misc::constants::lean::INTERVALS_PER_SLOT; #[cfg(feature = "devnet3")] use ream_fork_choice_lean::store::compute_subnet_id; use ream_keystore::lean_keystore::ValidatorKeystore; @@ -65,8 +66,8 @@ impl ValidatorService { loop { tokio::select! { _ = interval.tick() => { - let slot = tick_count / 4; - match tick_count % 4 { + let slot = tick_count / INTERVALS_PER_SLOT; + match tick_count % INTERVALS_PER_SLOT { 0 => { // First tick (t=0): Propose a block. if slot > 0 && let Some(keystore) = self.is_proposer(slot) {