fix(sequencer): re-check parent checkpoint validity before pipelined L1 submission#22586
Merged
spalladino merged 7 commits intomerge-train/spartanfrom Apr 21, 2026
Conversation
c554311 to
9aa1168
Compare
Maddiaa0
reviewed
Apr 21, 2026
| private async waitForSyncedL2SlotNumber(waitForSlot: SlotNumber): Promise<boolean> { | ||
| const targetSlotStart = Number(getTimestampForSlot(this.targetSlot, this.l1Constants)); | ||
| const targetSlotEndMs = (targetSlotStart + this.l1Constants.slotDuration) * 1000; | ||
| const syncDelayTolerance = this.l1Constants.slotDuration * 1000; |
Member
There was a problem hiding this comment.
a tolerance of an entire l2 slot feels very long
Contributor
Author
There was a problem hiding this comment.
Agree. I intended an L1 slot but mistyped. Should've left this to Claude :-P
Contributor
Author
There was a problem hiding this comment.
Ended up setting to two ethereum slots in case there's a missed L1 slot
Maddiaa0
reviewed
Apr 21, 2026
| * - If we built without a proposed parent: no new checkpoint must have appeared for that slot. | ||
| * If the parent has invalid attestations, enqueues an invalidation. Returns whether to proceed with the proposal. | ||
| */ | ||
| protected async waitForParentCheckpointOnL1(): Promise<boolean> { |
Member
There was a problem hiding this comment.
name here doesn't quite capture all the work done by this function. maybe ensureParentCheckpointValidity?
Contributor
Author
There was a problem hiding this comment.
Renamed to waitForValidParentCheckpointOnL1 - IMO emphasis should be on the waiting part
10ff310 to
44206f5
Compare
…L1 submission With pipelining, the sequencer optimistically builds on top of a proposed parent checkpoint. At submission time, we now wait for the parent to land on L1 and verify it matches expectations (hash, attestation validity). If invalid, the pipelined work is discarded and an invalidation is enqueued instead. Fixes A-921 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The pipelining test was missing mocks for getSyncedL2SlotNumber and getL2Tips, causing waitForParentCheckpointOnL1 to hang in a retryUntil loop until the 120s Jest timeout. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Avoid infinite loop in waitForSyncedL2SlotNumber when target slot has already expired (retryUntil treats timeout=0 as never-timeout) - Rename checkpoint-parent-mismatch event to pipelined-checkpoint-discarded since it also fires for archiver-sync-timeout - Replace `as any` casts in unit tests with proper InvalidateCheckpointRequest type Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
44206f5 to
9990a8b
Compare
This was referenced Apr 21, 2026
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
With pipelining enabled, the sequencer optimistically builds a checkpoint on top of a proposed parent. If that parent checkpoint lands on L1 with invalid attestations, the pipelined checkpoint was never invalidating it — the invalidation was cleared at build time under the assumption the parent would handle it.
Approach
At submission time (after the pipelining sleep), the sequencer now waits for the parent checkpoint to land on L1, then verifies it matches expectations: correct hash, valid attestations, and no unexpected checkpoints appeared. If the parent is invalid, the pipelined work is discarded and an invalidation is enqueued instead. The
skipInvalidateBlockAsProposerconfig is respected so the committee member fallback path still works.Changes
waitForAttestationsAndEnqueueSubmissionAsyncto deferenqueueCheckpointForSubmissionuntil after parent validation when pipelining. AddedwaitForParentCheckpointOnL1which polls the archiver and checks 5 failure conditions: archiver sync timeout, parent not on L1, parent hash mismatch, parent invalid attestations, unexpected parent appeared. AddedenqueueInvalidationForParenthelper that respectsskipInvalidateBlockAsProposer.checkpoint-parent-mismatchevent with slot, checkpoint number, and reason.recordPipelineParentCheckpointMismatchcounter with reason attribute.SEQUENCER_PIPELINE_PARENT_CHECKPOINT_MISMATCH_COUNTmetric definition.executeAndAwait, asserting publisher actions and emitted events.enableProposerPipelining: true, inboxLag: 2) inepochs_invalidate_blocktests. Updatedproposer invalidates multiple checkpointstest to verify the invalidation happens promptly (proposer path, not committee fallback).Fixes A-909
Fixes A-921