Skip to content

doppelganger clearing should use the head epoch #17332

Description

@james-prysm

Follow-up from #17285, raised in review:
#17285 (comment)

When clearing a reloaded key's doppelganger quarantine, the validator client
runs two separate calls: checkDoppelGangerForKeys (the beacon node evaluates
liveness as of its head at that moment) and clearingEpoch (a ChainHead
request whose result caps the clearing epoch, min(wall-clock epoch, head epoch)).

The problem is that the two calls can see different heads. The liveness
verdict from the first call only covers epochs up to the head the beacon node
had at that moment. If the head advances before the second call — an epoch
boundary passes, or the node finishes catching up — clearingEpoch picks up
the newer head and uses it to end the quarantine, even though the verdict it
is acting on never looked at that newest epoch.

Example: a key quarantined at epoch 5 with a 2-epoch wait may only clear once
a clean verdict covers an epoch greater than 7.

  1. checkDoppelGangerForKeys runs while the node's head is at epoch 7 →
    the "clean" verdict covers epochs ≤ 7. Not sufficient to clear.
  2. The head advances to epoch 8 before the next call.
  3. clearingEpoch returns min(8, 8) = 88 > 7, so the key clears —
    with epoch 8's liveness never having been evaluated.

The skew is bounded to one epoch (the wall-clock min cap, plus the calls
running back-to-back within one slot), but the clearing epoch and the epoch
the verdict covers should be the same value by construction.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions