From dd768ba707747870086e649bcf96b678aff64038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 6 Aug 2026 13:10:52 -0300 Subject: [PATCH 1/2] refactor(types): move the interval grid into ethlambda-types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ethlambda-storage derives slot and interval from `store.time()` but cannot depend on ethlambda-blockchain, so it could only document the formula in a comment rather than use the constants. Housing MILLISECONDS_PER_INTERVAL, INTERVALS_PER_SLOT and MILLISECONDS_PER_SLOT in ethlambda-types — which both crates already depend on — removes that asymmetry and keeps a second copy of a consensus-critical constant from appearing in storage. Values are unchanged; blockchain re-exports all three so existing imports keep resolving. --- crates/blockchain/src/lib.rs | 12 ++++++------ crates/common/types/src/constants.rs | 7 +++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/crates/blockchain/src/lib.rs b/crates/blockchain/src/lib.rs index 7faa1163..dc4f1268 100644 --- a/crates/blockchain/src/lib.rs +++ b/crates/blockchain/src/lib.rs @@ -68,13 +68,13 @@ pub struct BlockChainConfig { pub proposer_config: ProposerConfig, } -/// Milliseconds per interval (800ms ticks). -pub const MILLISECONDS_PER_INTERVAL: u64 = 800; -/// Number of intervals per slot (5 intervals of 800ms = 4 seconds). -pub const INTERVALS_PER_SLOT: u64 = 5; -/// Milliseconds in a slot (derived from interval duration and count). -pub const MILLISECONDS_PER_SLOT: u64 = MILLISECONDS_PER_INTERVAL * INTERVALS_PER_SLOT; +// The interval grid lives in `ethlambda-types` because `ethlambda-storage` also +// derives slots from `store.time()` and must not carry a second copy of a +// consensus-critical constant. pub use ethlambda_types::block::MAX_ATTESTATIONS_DATA; +pub use ethlambda_types::constants::{ + INTERVALS_PER_SLOT, MILLISECONDS_PER_INTERVAL, MILLISECONDS_PER_SLOT, +}; pub use sync_status::SyncStatusController; /// Future-slot tolerance for gossip attestations, expressed in intervals. /// diff --git a/crates/common/types/src/constants.rs b/crates/common/types/src/constants.rs index 3066b344..c3434d9a 100644 --- a/crates/common/types/src/constants.rs +++ b/crates/common/types/src/constants.rs @@ -8,3 +8,10 @@ /// eventually be derived from the fork version and genesis validators root. // TODO: derive dynamically once the spec defines fork identification. pub const FORK_DIGEST: &str = "12345678"; + +/// Milliseconds per interval (800ms ticks). +pub const MILLISECONDS_PER_INTERVAL: u64 = 800; +/// Number of intervals per slot (5 intervals of 800ms = 4 seconds). +pub const INTERVALS_PER_SLOT: u64 = 5; +/// Milliseconds in a slot (derived from interval duration and count). +pub const MILLISECONDS_PER_SLOT: u64 = MILLISECONDS_PER_INTERVAL * INTERVALS_PER_SLOT; From c4642214a688ce43ce68b49cd56fc6b4e6f087a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 6 Aug 2026 13:17:58 -0300 Subject: [PATCH 2/2] refactor(storage): add Store::current_slot Deriving the slot from the store clock was open-coded at three call sites in the blockchain crate, each repeating the division and its own expect message. Now that the interval grid lives in ethlambda-types, storage can own the accessor. --- crates/blockchain/src/lib.rs | 2 +- crates/blockchain/src/store.rs | 4 ++-- crates/storage/src/store.rs | 11 ++++++++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/crates/blockchain/src/lib.rs b/crates/blockchain/src/lib.rs index dc4f1268..98961c91 100644 --- a/crates/blockchain/src/lib.rs +++ b/crates/blockchain/src/lib.rs @@ -1413,7 +1413,7 @@ impl Handler for BlockChainServer { // Early aggregation only advances the current slot's group counts, so a // late- or future-slot attestation can never cross the threshold; skip // the check unless this attestation is for the store's current slot. - let current_slot = self.store.time().expect("store time exists") / INTERVALS_PER_SLOT; + let current_slot = self.store.current_slot(); if msg.attestation.data.slot == current_slot { self.maybe_start_early_aggregation(ctx).await; } diff --git a/crates/blockchain/src/store.rs b/crates/blockchain/src/store.rs index 5f2a4657..0d3b3cdd 100644 --- a/crates/blockchain/src/store.rs +++ b/crates/blockchain/src/store.rs @@ -348,7 +348,7 @@ pub fn on_tick(store: &mut Store, timestamp_ms: u64, has_proposal: bool) { .set_time(store.time().unwrap() + 1) .expect("set_time should succeed"); - let slot = store.time().unwrap() / INTERVALS_PER_SLOT; + let slot = store.current_slot(); let interval = SlotInterval::from_intervals_since_genesis(store.time().unwrap()); trace!(%slot, ?interval, "processing tick"); @@ -637,7 +637,7 @@ fn on_block_core( // Horizon is the current slot plus one whole slot of margin, so an intended // early block still imports (mirrors the attestation future-slot guard, but // with a whole-slot rather than one-interval margin). - let current_slot = store.time().expect("DB read should succeed") / INTERVALS_PER_SLOT; + let current_slot = store.current_slot(); if slot > current_slot + 1 { return Err(StoreError::BlockTooFarInFuture { block_slot: slot, diff --git a/crates/storage/src/store.rs b/crates/storage/src/store.rs index 626cbf2e..51934e73 100644 --- a/crates/storage/src/store.rs +++ b/crates/storage/src/store.rs @@ -14,6 +14,7 @@ use ethlambda_types::{ Block, BlockBody, BlockHeader, MultiMessageAggregate, SignedBlock, SingleMessageAggregate, }, checkpoint::Checkpoint, + constants::INTERVALS_PER_SLOT, genesis::GenesisConfig, primitives::{H256, HashTreeRoot as _}, state::{ChainConfig, State, anchor_pair_is_consistent}, @@ -817,9 +818,8 @@ impl Store { /// Returns the current store time in interval counts since genesis. /// - /// Each increment represents one 800ms interval. Derive slot/interval as: - /// slot = time() / INTERVALS_PER_SLOT - /// interval = time() % INTERVALS_PER_SLOT + /// Each increment represents one 800ms interval. Use [`Self::current_slot`] + /// for the slot; the interval within it is `time() % INTERVALS_PER_SLOT`. pub fn time(&self) -> Result { self.get_metadata(KEY_TIME) } @@ -829,6 +829,11 @@ impl Store { self.set_metadata(KEY_TIME, &time) } + /// The current slot, derived from the store clock. + pub fn current_slot(&self) -> u64 { + self.time().expect("store time exists") / INTERVALS_PER_SLOT + } + // ============ Config ============ /// Returns the chain configuration.