Skip to content

Slashing change#5

Open
fradamt wants to merge 7 commits into
masterfrom
slashing-change
Open

Slashing change#5
fradamt wants to merge 7 commits into
masterfrom
slashing-change

Conversation

@fradamt

@fradamt fradamt commented May 18, 2026

Copy link
Copy Markdown
Owner

No description provided.

fradamt added 3 commits May 7, 2026 17:29
…poch

Eliminates the correlation-penalty escape attack where late-exiting
validators evade penalties when their `withdrawable_epoch` is pushed past
the rolling `state.slashings` ring buffer (~67% of slashed stake escapes
under a 25% mass slashing at current parameters).

Decouples the correlation penalty from queue-driven `withdrawable_epoch`
timing by:

- Renaming `Validator.activation_eligibility_epoch` -> `slashing_epoch`
  (the activation eligibility queue is removed in Gloas, since
  `process_pending_deposits` already gates on deposit-slot finality).
- Adding `slash_validator(state, index, slashing_epoch, ...)` which records
  the offense epoch on the validator. `process_attester_slashing` passes
  `attestation_1.data.target.epoch`; `process_proposer_slashing` passes
  `compute_epoch_at_slot(header_1.slot)`.
- Replacing `process_slashings` / `process_slashings_reset` with
  `process_correlation_penalties`, which fires for slashed validators with
  `withdrawable_epoch == current_epoch + 1` (one epoch before the sweep can
  drain their balance). The cohort is the sum of effective balances of all
  slashed validators sharing the same `slashing_epoch`.
- Introducing `MIN_SLASHED_VALIDATOR_WITHDRAWABILITY_DELAY` as the
  post-slashing exit-delay floor (mainnet 8192, minimal 64), parallel to
  `MIN_VALIDATOR_WITHDRAWABILITY_DELAY`.

`state.slashings` is retained as a vestigial zeroed vector for SSZ gindex
stability with light-client `FINALIZED_ROOT_GINDEX_ELECTRA` etc.

`upgrade_to_gloas` derives `slashing_epoch` from `withdrawable_epoch` for
pre-fork slashed validators (exact for queue-empty case), preserving the
invariant `slashed => slashing_epoch != FAR_FUTURE_EPOCH`.

The branch is research-track; the no-early-exit invariant (verified for
voluntary exits, EIP-7002 / EIP-7251 requests, pending consolidations and
partial withdrawals) means slashed validators can only drain via the full
sweep, immediately after `process_correlation_penalties` fires.
Replaces the cohort-by-live-effective-balance model with a per-period
accumulator stored in the existing `state.slashings` field, repurposed:

- `state.slashings` is resized from `Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR]`
  (per-epoch ring) to `Vector[Gwei, PERIODS_PER_SLASHINGS_VECTOR]` (per-period
  accumulator). Field position unchanged, so light-client SSZ gindices are
  preserved.
- New presets `EPOCHS_PER_SLASHING_PERIOD` (2**12 mainnet, 2**5 minimal)
  and `PERIODS_PER_SLASHINGS_VECTOR` (2**4 = 16, both presets).
- `slash_validator` accumulates `validator.effective_balance` into the
  period bucket of the offense's `slashing_epoch`.
- `compute_correlation_penalty` reads three adjacent buckets
  `(period - 1, period, period + 1)` modulo `PERIODS_PER_SLASHINGS_VECTOR`,
  giving a ~3 * EPOCHS_PER_SLASHING_PERIOD correlation window centred on
  the offense epoch. O(1), no `state.validators` scan.
- `process_slashings_reset` is repurposed to zero the bucket about to be
  reused at every period boundary.
- `MIN_SLASHED_VALIDATOR_WITHDRAWABILITY_DELAY` floor doc-bound to
  `2 * EPOCHS_PER_SLASHING_PERIOD` so the three-bucket window is fully
  accumulated when the penalty fires.

Why this fixes the prior live-balance design:

The previous `compute_correlation_penalty` summed `effective_balance`
over slashed validators with the matching `slashing_epoch`. That cohort
sum decays during the post-slashing delay (attestation/inactivity
penalties drift `effective_balance` down) and collapses entirely once
co-attackers are swept (their balance and effective_balance reach zero).
A queue-tail validator's penalty would fire after most co-attackers have
already been swept, leaving them effectively unpenalised. The
accumulator captures the snapshot at slashing time and persists across
the delay window.

Migration in `upgrade_to_gloas`: pre-fork slashed-but-not-withdrawn
validators all get `slashing_epoch = epoch` (transition epoch), forming
a single migration cohort; carried-over `sum(pre.slashings)` is placed
in the corresponding bucket of the new vector.

`process_slashings` remains deprecated (replaced by
`process_correlation_penalties`); `process_slashings_reset` is no longer
deprecated since gloas now overrides it.
- Adds a "Slashing correlation rework" subsection to the Gloas
  Introduction summarising the pre-Gloas escape problem and the
  per-period accumulator that replaces it. Names the key spec entities
  a reader needs to find (`slash_validator`, `compute_correlation_penalty`,
  `process_correlation_penalties`, `process_slashings_reset`) and notes
  the migration approach in `upgrade_to_gloas`.
- Adds a function-level note to `process_proposer_slashing` covering the
  Gloas:slashing-change change (passing `slashing_epoch` explicitly)
  alongside the existing Gloas:EIP7732 change.
mkalinin and others added 4 commits May 20, 2026 12:55
Co-authored-by: fradamt <104826920+fradamt@users.noreply.github.com>
@github-actions github-actions Bot added the heze label May 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants