fix(sequencer): use wall-clock time instead of L1 block timestamp for slot estimation#21769
Merged
PhilWindle merged 2 commits intomerge-train/spartanfrom Mar 20, 2026
Merged
Conversation
441cafe to
14e49b5
Compare
Fixes A-707
14e49b5 to
4bbbabb
Compare
…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>
spalladino
commented
Mar 20, 2026
| * 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. |
Contributor
Author
There was a problem hiding this comment.
I expect this to be no longer needed if #21829 works
Collaborator
Flakey Tests🤖 says: This CI run detected 1 tests that failed, but were tolerated due to a .test_patterns.yml entry. |
PhilWindle
approved these changes
Mar 20, 2026
Collaborator
|
❌ Failed to cherry-pick to |
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)
This was referenced Mar 20, 2026
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 + ethereumSlotDurationproduces 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 + ethereumSlotDurationpatterns with a newgetNextL1SlotTimestamp(dateProvider.nowInSeconds(), constants)helper that rounds wall-clock time up to the next L1 slot boundary. This is correct regardless of missed slots. Additionally,getSyncedL2SlotNumbernow 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
getNextL1SlotTimestamphelper in epoch-helpers that computes the next L1 slot boundary from wall-clock timecanProposeAtNextEthBlocktocanProposeAtonRollupContract, now accepts a precomputed timestamp instead of computing it internallyDateProviderintoGlobalVariableBuilder(now receives it as first arg + public client as second arg instead of creating its own). Publisher and global variable builder usegetNextL1SlotTimestampinstead ofgetBlock().timestampgetEpochAndSlotInNextL1Slotto usegetNextL1SlotTimestampgetSyncedL2SlotNumbernow returns the max of L1-timestamp-based sync and checkpoint-based sync, handling missed L1 blocks via the inbox LAG mechanismepochs_missed_l1_slote2e test that pauses L1 mining after a checkpoint and verifies the sequencer still builds during the pause