Skip to content

fix(archiver): handle duplicate checkpoint from L1 reorg#22252

Merged
PhilWindle merged 2 commits into
merge-train/spartanfrom
claudebox/e5247344b8df94ca-2
Apr 2, 2026
Merged

fix(archiver): handle duplicate checkpoint from L1 reorg#22252
PhilWindle merged 2 commits into
merge-train/spartanfrom
claudebox/e5247344b8df94ca-2

Conversation

@AztecBot

@AztecBot AztecBot commented Apr 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes a mainnet issue where an L1 reorg moved a checkpoint to a different L1 block, causing the archiver to re-discover it and crash with InitialCheckpointNumberNotSequentialError in an infinite loop.

When addCheckpoints receives a checkpoint that's already stored, it now:

  • Accepts it if the archive root matches (same content, just different L1 block)
  • Updates the L1 metadata (block number, timestamp, hash) and attestations
  • Throws if the archive root doesn't match (content mismatch — genuine conflict)

Changes

  • block_store.ts: Added skipOrUpdateAlreadyStoredCheckpoints method that handles duplicate checkpoints at the start of a batch. Verifies archive roots match and updates L1 info.
  • fake_l1_state.ts: Added moveCheckpointToL1Block helper for simulating L1 reorgs that move checkpoints.
  • archiver-sync.test.ts: Added e2e test handles L1 reorg that moves a checkpoint to a later L1 block in the reorg handling suite.
  • kv_archiver_store.test.ts: Added unit tests for accepting matching duplicates with updated L1 info, accepting fully-duplicate batches, and rejecting mismatching duplicates.

Test plan

  • Unit tests: 207 passed in kv_archiver_store.test.ts
  • Sync tests: 37 passed in archiver-sync.test.ts
  • Build, format, lint all pass

ClaudeBox log: https://claudebox.work/s/e5247344b8df94ca?run=2

When an L1 reorg moves a checkpoint to a different L1 block, the archiver
re-discovers it during the next scan. Previously this threw
InitialCheckpointNumberNotSequentialError because the checkpoint was already
in the store. Now addCheckpoints accepts duplicate checkpoints if their
archive root matches (same content) and updates the L1 metadata. If archives
don't match, it throws a clear error about the mismatch.
@AztecBot AztecBot added ci-draft Run CI on draft PRs. claudebox Owned by claudebox. it can push to this PR. labels Apr 1, 2026
@PhilWindle PhilWindle marked this pull request as ready for review April 2, 2026 07:45
@PhilWindle PhilWindle enabled auto-merge (squash) April 2, 2026 10:01
@PhilWindle PhilWindle merged commit 13f6840 into merge-train/spartan Apr 2, 2026
21 of 25 checks passed
@PhilWindle PhilWindle deleted the claudebox/e5247344b8df94ca-2 branch April 2, 2026 10:13
@AztecBot

AztecBot commented Apr 2, 2026

Copy link
Copy Markdown
Collaborator Author

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

AztecBot added a commit that referenced this pull request Apr 2, 2026
PhilWindle added a commit that referenced this pull request Apr 2, 2026
…2252) (#22257)

## Summary

Backport of #22252
to v4.

Fixes a mainnet issue where an L1 reorg moved a checkpoint to a
different L1 block, causing the archiver to re-discover it and crash
with `InitialCheckpointNumberNotSequentialError` in an infinite loop.

When `addCheckpoints` receives a checkpoint that's already stored, it
now:
- **Accepts it** if the archive root matches (same content, just
different L1 block)
- **Updates the L1 metadata** (block number, timestamp, hash) and
attestations
- **Throws** if the archive root doesn't match (content mismatch —
genuine conflict)

## Conflict Resolution

Cherry-pick had one conflict in `block_store.ts`:
- v4 uses inline code for extracting previous checkpoint block data;
`next` refactored this into a `getPreviousCheckpointBlock()` helper.
Kept v4's inline approach but updated to use `checkpoints[0]` reference
(correct after potential skip of already-stored checkpoints).
- Only ported `skipOrUpdateAlreadyStoredCheckpoints` method (the fix).
Dropped `getPreviousCheckpointBlock` and `validateCheckpointBlocks`
refactors that exist on `next` but not v4.

## Test Results

- 176 passed in `kv_archiver_store.test.ts`
- 33 passed in `archiver-sync.test.ts` (includes new `handles L1 reorg
that moves a checkpoint to a later L1 block` test)
- Build, format pass

ClaudeBox log: https://claudebox.work/s/21a4c138364df1ef?run=1
Comment on lines +353 to +359
// Verify the checkpoint content matches (archive root)
if (!stored.archive.root.equals(incoming.checkpoint.archive.root)) {
throw new Error(
`Checkpoint ${incoming.checkpoint.number} already exists in store but with a different archive root. ` +
`Stored: ${stored.archive.root}, incoming: ${incoming.checkpoint.archive.root}`,
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@Maddiaa0 not sure if this can cause issues with syncing a checkpoint that should override a proposed checkpoint

github-merge-queue Bot pushed a commit that referenced this pull request Apr 6, 2026
BEGIN_COMMIT_OVERRIDE
fix: deflake HA governance voting test by polling for L1/DB convergence
(#22220)
feat(world-state): add genesis timestamp support and GenesisData type
(#22201)
chore: revert: feat(world-state): add genesis timestamp support and
GenesisData type (#22201) (#22255)
fix(archiver): handle duplicate checkpoint from L1 reorg (#22252)
chore: update dashboard (#22260)
fix: remove detailed revert codes (#22274)
chore: use ESO in grafana (#22271)
chore: (A-751) robust response error handling in json-rpc client
(#22246)
fix: separate fisherman StatefulSet from rpc-node and stop archiver
pollution (#22183)
fix: restore mainnet prover agents to 4 replicas (#22305)
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 ci-draft Run CI on draft PRs. claudebox Owned by claudebox. it can push to this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants