Skip to content

feat(slasher): slash checkpoint equivocation between P2P and L1 (A-980)#23436

Open
PhilWindle wants to merge 3 commits into
merge-train/spartanfrom
phil/a-980-slash-by-equivocation-if-l1-synced-checkpoint-does-not-match
Open

feat(slasher): slash checkpoint equivocation between P2P and L1 (A-980)#23436
PhilWindle wants to merge 3 commits into
merge-train/spartanfrom
phil/a-980-slash-by-equivocation-if-l1-synced-checkpoint-does-not-match

Conversation

@PhilWindle
Copy link
Copy Markdown
Collaborator

Summary

  • The archiver already detects when a locally-stored proposed checkpoint disagrees with the L1-confirmed checkpoint at the same slot (in tryBuildPublishedCheckpointFromProposed), but until now it only logged the divergence and evicted the local entry. Both sides are signed by the slot proposer, so the proposer equivocated.
  • Emit a new `CheckpointEquivocationDetected` event from the archiver when this divergence is detected.
  • Add `CheckpointEquivocationWatcher` in the slasher that subscribes to the event, resolves the slot proposer via the epoch cache, and emits a `DUPLICATE_PROPOSAL` want-to-slash event.
  • This closes the gap where a proposer broadcasts one checkpoint via gossip and submits a different one to L1 — the existing `AttestationPool` P2P duplicate detection can't see this because only one of the two proposals is gossiped.

Fixes A-980.

Test plan

  • New unit tests in `yarn-project/slasher/src/watchers/checkpoint_equivocation_watcher.test.ts` cover: emit on divergence, no-emit on missing proposer / zero penalty / after stop, dedup of repeat events, separate offenses per slot.
  • The end-to-end scenario already exists in `yarn-project/end-to-end/src/e2e_epochs/epochs_equivocation.test.ts`. Its TODO around asserting the slash is still gated on enabling the slasher in the harness (separate plumbing) — comment updated to reflect that detection is now implemented.

The archiver's L1 synchronizer already detects when a locally-stored
proposed checkpoint disagrees with the L1-confirmed checkpoint at the same
slot, but it only logged the divergence and evicted the local entry. Both
sides are signed by the slot proposer, so the proposer equivocated.

Emit a CheckpointEquivocationDetected event from the archiver when this
divergence is detected, and add a CheckpointEquivocationWatcher in the
slasher that subscribes to it, resolves the slot proposer via the epoch
cache, and emits a DUPLICATE_PROPOSAL want-to-slash event.

This closes the gap where a proposer broadcasts one checkpoint via gossip
and submits a different one to L1 — the existing AttestationPool duplicate
detection can't see this because only one of the two proposals is gossiped.
@PhilWindle PhilWindle marked this pull request as ready for review May 20, 2026 16:54
Enable the slasher in the equivocation harness with a non-zero
slashDuplicateProposalPenalty and poll getSlashOffenses across the live
validator nodes after the network heals. The offense fires on B/C as soon
as L1 sync exposes A's checkpoint, well before the healing assertions
complete, so the wait window is short.
…-980)

Tighten the assertion: instead of accepting any observer recording the
DUPLICATE_PROPOSAL offense, require both B and C to have it for the
submission slot.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant