Commit a2f6580
authored
fix(blockchain): align tick scheduling to genesis, not unix epoch (lambdaclass#392)
## Problem
`handle_tick` (`crates/blockchain/src/lib.rs`) scheduled the next tick
at the next **unix-epoch** multiple of `MILLISECONDS_PER_INTERVAL`:
```rust
let ms_to_next_interval =
MILLISECONDS_PER_INTERVAL - (ms_since_epoch % MILLISECONDS_PER_INTERVAL);
```
But `on_tick` derives `slot`/`interval` from time **since genesis**:
```rust
let time_since_genesis_ms = timestamp_ms.saturating_sub(genesis_time_ms);
let interval = (time_since_genesis_ms % MILLISECONDS_PER_SLOT) / MILLISECONDS_PER_INTERVAL;
```
Genesis is a whole-second timestamp and `1000 % 800 != 0`, so the
genesis interval grid is offset from the epoch grid. After the first
(genesis-aligned) tick, every subsequent tick snapped onto the epoch
grid, firing `(800 - genesis_ms % 800) % 800` ms late. For the default
`config.yaml` (`GENESIS_TIME: 1770407233`), `genesis_ms % 800 == 200`,
so every interval fired **600 ms late**, drifting all interval-driven
duties (proposal, attestation, aggregation, safe-target) off their
intended slot boundaries.
## Fix
Measure the remaining time **relative to genesis**, extracted into a
pure, testable helper that mirrors leanSpec
`SlotClock.seconds_until_next_interval`:
```rust
fn ms_until_next_interval(now_ms: u64, genesis_time_ms: u64) -> u64 {
let Some(ms_since_genesis) = now_ms.checked_sub(genesis_time_ms) else {
return genesis_time_ms - now_ms; // before genesis: wait until genesis
};
MILLISECONDS_PER_INTERVAL - (ms_since_genesis % MILLISECONDS_PER_INTERVAL)
}
```
### Comparison with leanSpec
| | leanSpec `SlotClock` (correct) | ethlambda (before) |
|---|---|---|
| reference grid | `now - genesis` | `now` (unix epoch) |
| boundary formula | `INTERVAL - (elapsed_ms % INTERVAL)` | `INTERVAL -
(epoch_ms % INTERVAL)` |
| before genesis | waits until genesis | n/a |
| exactly on boundary | schedules a full interval ahead | same |
## Tests
Added a `#[cfg(test)] mod tests` covering the helper:
- `lands_on_genesis_relative_boundary` /
`fix_keeps_ticks_aligned_to_genesis_grid` — ticks stay on the genesis
grid and the interval index advances by exactly one.
- `buggy_epoch_formula_fires_intervals_late` — regression test pinning
the old epoch-relative lateness.
- `exact_boundary_schedules_full_interval_ahead`,
`before_genesis_waits_until_genesis`, `mid_interval_waits_for_remainder`
— edge cases.
Verified the genesis-grid tests **fail** with the old epoch-relative
formula and **pass** with the fix. `cargo fmt`, `cargo clippy`, and the
lib test suite are clean.1 parent db05d78 commit a2f6580
1 file changed
Lines changed: 14 additions & 5 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
56 | 56 | | |
57 | 57 | | |
58 | 58 | | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
59 | 68 | | |
60 | 69 | | |
61 | 70 | | |
| |||
635 | 644 | | |
636 | 645 | | |
637 | 646 | | |
638 | | - | |
639 | | - | |
640 | | - | |
641 | | - | |
642 | | - | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
643 | 652 | | |
644 | 653 | | |
645 | 654 | | |
| |||
0 commit comments