Skip to content

fix(sequencer): use wall-clock time instead of L1 block timestamp for slot estimation#21769

Merged
PhilWindle merged 2 commits intomerge-train/spartanfrom
palla/do-not-use-l1-time
Mar 20, 2026
Merged

fix(sequencer): use wall-clock time instead of L1 block timestamp for slot estimation#21769
PhilWindle merged 2 commits intomerge-train/spartanfrom
palla/do-not-use-l1-time

Conversation

@spalladino
Copy link
Contributor

Motivation

When an Ethereum slot is missed (no block produced), the next L1 block lands 24+ seconds after the previous one instead of 12. Computing the next block timestamp as getBlock().timestamp + ethereumSlotDuration produces wrong results, causing incorrect L2 slot, committee, and proposer calculations. Additionally, the archiver's sync check would block the sequencer from building when L1 blocks were delayed, even if the previous checkpoint had already been synced.

Fixes #14766

Approach

Replace all getBlock().timestamp + ethereumSlotDuration patterns with a new getNextL1SlotTimestamp(dateProvider.nowInSeconds(), constants) helper that rounds wall-clock time up to the next L1 slot boundary. This is correct regardless of missed slots. Additionally, getSyncedL2SlotNumber now uses the latest synced checkpoint slot as a second signal (alongside L1 block timestamps) to determine sync progress, so the sequencer can start building even when L1 blocks are delayed.

Changes

  • stdlib: Added getNextL1SlotTimestamp helper in epoch-helpers that computes the next L1 slot boundary from wall-clock time
  • ethereum: Renamed canProposeAtNextEthBlock to canProposeAt on RollupContract, now accepts a precomputed timestamp instead of computing it internally
  • sequencer-client: Injected DateProvider into GlobalVariableBuilder (now receives it as first arg + public client as second arg instead of creating its own). Publisher and global variable builder use getNextL1SlotTimestamp instead of getBlock().timestamp
  • epoch-cache: Updated getEpochAndSlotInNextL1Slot to use getNextL1SlotTimestamp
  • archiver: getSyncedL2SlotNumber now returns the max of L1-timestamp-based sync and checkpoint-based sync, handling missed L1 blocks via the inbox LAG mechanism
  • end-to-end (tests): Added epochs_missed_l1_slot e2e test that pauses L1 mining after a checkpoint and verifies the sequencer still builds during the pause

@spalladino spalladino added ci-no-fail-fast Sets NO_FAIL_FAST in the CI so the run is not aborted on the first failure backport-to-v4-next labels Mar 18, 2026
@spalladino spalladino force-pushed the palla/do-not-use-l1-time branch from 441cafe to 14e49b5 Compare March 18, 2026 21:36
@spalladino spalladino force-pushed the palla/do-not-use-l1-time branch from 14e49b5 to 4bbbabb Compare March 19, 2026 22:09
…proposals

When the dateProvider wall clock is ahead of the latest mined L1 block,
getNextL1SlotTimestamp can return a timestamp in a future L2 slot. The
canProposeAt simulation passes (with time override) but the actual tx
lands in an L1 block still in the previous slot, causing silent rejection.

Floor the computed timestamp with latestBlock.timestamp + slotDuration
to ensure we never target a slot beyond what the next L1 block can reach.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chain hasn't caught up to the wall clock yet (e.g., the dateProvider is one L1 slot ahead of the
* latest mined block), which would cause the propose tx to land in an L1 block with block.timestamp
* still in the previous L2 slot.
* TODO(palla): Properly fix by keeping dateProvider synced with anvil's chain time on every block.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect this to be no longer needed if #21829 works

@AztecBot
Copy link
Collaborator

Flakey Tests

🤖 says: This CI run detected 1 tests that failed, but were tolerated due to a .test_patterns.yml entry.

\033FLAKED\033 (8;;http://ci.aztec-labs.com/bbfba05bcb4ccece�bbfba05bcb4ccece8;;�):  yarn-project/end-to-end/scripts/run_test.sh simple src/e2e_epochs/epochs_proof_fails.parallel.test.ts "does not allow submitting proof after epoch end" (275s) (code: 0) group:e2e-p2p-epoch-flakes

@PhilWindle PhilWindle merged commit fef6517 into merge-train/spartan Mar 20, 2026
11 checks passed
@PhilWindle PhilWindle deleted the palla/do-not-use-l1-time branch March 20, 2026 14:20
@AztecBot
Copy link
Collaborator

❌ Failed to cherry-pick to v4-next due to conflicts. (🤖) View backport run.

AztecBot added a commit that referenced this pull request Mar 20, 2026
…timestamp for slot estimation (#21769)

Cherry-pick of merge commit fef6517 with conflicts.
Original PR: #21769
AztecBot added a commit that referenced this pull request Mar 20, 2026
Adapted PR #21769 changes for v4-next:
- Resolved conflicts in epoch_cache.ts (kept v4-next 'now' naming, added getNextL1SlotTimestamp)
- Resolved conflicts in rollup.ts (renamed canProposeAtNextEthBlock to canProposeAt with timestamp param)
- Resolved conflicts in sequencer-publisher.ts (added dateProvider, wall-clock-based timestamp)
- Resolved conflicts in sequencer-publisher.test.ts (added EmptyL1RollupConstants mock)
- Updated all callers of renamed methods (sequencer.ts, sequencer.test.ts, e2e tests)
spalladino pushed a commit that referenced this pull request Mar 20, 2026
…est (#21848)

## Summary
Fix TypeScript compilation error in the new
`epochs_missed_l1_slot.test.ts` introduced by #21769.

The test calls `eth.timestamp()` but the method on `EthCheatCodes` is
actually `lastBlockTimestamp()`. This caused `yarn tsgo -b
--emitDeclarationOnly` to fail with:
```
error TS2339: Property 'timestamp' does not exist on type 'EthCheatCodes'.
```

## Fix
Changed `eth.timestamp()` → `eth.lastBlockTimestamp()` on line 104 of
the test file.

ClaudeBox log: https://claudebox.work/s/e62ab0794047aa8c?run=1
spalladino pushed a commit that referenced this pull request Mar 20, 2026
…xt (#21769) (#21847)

## Summary

Backport of #21769
to v4-next.

When an Ethereum slot is missed (no block produced), the next L1 block
lands 24+ seconds after the previous one instead of 12. Computing the
next block timestamp as `getBlock().timestamp + ethereumSlotDuration`
produces wrong results, causing incorrect L2 slot, committee, and
proposer calculations.

This backport replaces all `getBlock().timestamp + ethereumSlotDuration`
patterns with a new `getNextL1SlotTimestamp()` helper that rounds
wall-clock time up to the next L1 slot boundary. Additionally,
`getSyncedL2SlotNumber` now uses the latest synced checkpoint slot as a
second signal to determine sync progress.

## Changes from original PR adapted for v4-next

- Cherry-pick had 4 conflicted files (epoch_cache, rollup,
sequencer-publisher, publisher test)
- Removed `getTargetEpochAndSlotInNextL1Slot` (pipelining not present on
v4-next)
- Removed publisher rotation tests (feature not present on v4-next)
- Used `dateProvider.nowInSeconds()` directly instead of removed
`EpochCache.nowInSeconds()` method
- Renamed `canProposeAtNextEthBlock` to `canProposeAt` on RollupContract
and SequencerPublisher

Fixes #14766

ClaudeBox log: https://claudebox.work/s/76e2a4f0177d755a?run=1
github-merge-queue bot pushed a commit that referenced this pull request Mar 20, 2026
BEGIN_COMMIT_OVERRIDE
feat(p2p): add tx validator for contract instance deployment addresses
(#21771)
fix: always deploy IRM for testnet (#21755)
fix: avoid mutating caller's array via splice in snapshot sync (A-718)
(#21759)
chore: update network logs skill (#21785)
feat(archiver): validate contract instance addresses before storing
(#21787)
fix: ensure no division by 0 (#21786)
feat: support private fork releases via ci-release (#21778)
fix: restrict scenario deployments to only nightly (#21798)
fix(stdlib): zero-pad bufferFromFields when declared length exceeds
payload (#21802)
test(protocol-contracts): verify max-size bytecode fits in contract
class log (#21818)
fix: wire BOT_DA_GAS_LIMIT through helm/terraform for staging-public
(#21809)
fix: remove jest-mock-extended from worker processes + fix
parallelize_strict silent failures (#21821)
fix(archiver): throw on duplicate contract class or instance additions
(#21799)
chore: remove broadcasted function events (#21805)
fix: sync dateProvider from anvil stdout on every mined block (#21829)
fix(sequencer): use wall-clock time instead of L1 block timestamp for
slot estimation (#21769)
fix: use correct EthCheatCodes method name in epochs_missed_l1_slot test
(#21848)
feat(p2p): add tx validator for contract class id verification (#21788)
feat: publisher funding (#21631)
feat: batch chonk verifier TS integration (#21823)
fix(sequencer): remove l1 block timestamp check (#21853)
fix: use local IVC inputs for batch_verifier bench test (#21857)
fix(p2p): centralize gossipsub penalization and fix inconsistencies
(#21863)
chore: publish GitHub releases to AztecProtocol/barretenberg (#21775)
END_COMMIT_OVERRIDE
AztecBot added a commit that referenced this pull request Mar 21, 2026
BEGIN_COMMIT_OVERRIDE
chore: backport #21754 (feat!: make isContractInitialized a tri-state
enum) to v4-next (#21792)
fix(stdlib): zero-pad bufferFromFields when declared length exceeds
payload (#21802)
test(protocol-contracts): verify max-size bytecode fits in contract
class log (#21818)
chore: port P2P mesh topic deflake fix to v4-next (#21825)
fix(archiver): throw on duplicate contract class or instance additions
(#21799)
feat: sync poseidon in the browser (#21833)
chore: backport #21824 (fix(aztec-up): add sensible defaults to
installer y/n prompts) to v4-next (#21844)
fix(sequencer): backport wall-clock time for slot estimation to v4-next
(#21769) (#21847)
chore: backport PR #21788 (feat(p2p): add tx validation for contract
class id verification) to v4-next (#21852)
feat: sync poseidon browser (#21851)
END_COMMIT_OVERRIDE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-to-v4-next ci-no-fail-fast Sets NO_FAIL_FAST in the CI so the run is not aborted on the first failure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants