Skip to content

fix: separate refund withdrawal scheduling + add invalid deposit tax#276

Merged
matthias-wright merged 9 commits into
audit-may-2026from
m/refund-withdrawals
Jun 26, 2026
Merged

fix: separate refund withdrawal scheduling + add invalid deposit tax#276
matthias-wright merged 9 commits into
audit-may-2026from
m/refund-withdrawals

Conversation

@matthias-wright

@matthias-wright matthias-wright commented May 28, 2026

Copy link
Copy Markdown
Collaborator

Builds on #275.

Addresses #226 and #234.

Changes:

  • Separates deposit-refund withdrawals from validator withdrawals in scheduling and emission. Refunds now use the refund schedule while validator exits keep priority under their own withdrawal budget, preventing invalid-deposit refunds from consuming validator-exit capacity.
  • Introduces a protocol parameter InvalidDepositTax. Invalid deposit refunds are now split into a net refund to the depositor withdrawal credentials and a tax amount to the configured treasury address. This removes a ddos vector, where people can spam the network with invalid deposit requests.
  • Adds validation to the withdrawal queue invariants on decode.

@sebastian-osec

Copy link
Copy Markdown

This looks directionally correct for refund isolation and the withdrawal-queue decode invariants: validator withdrawals and deposit-refund withdrawals are now separated by kind, real exits keep their own capacity, process-time top-up refunds use synthetic refund keys, and decode now rejects the main inconsistent queue states.

I have two questions:

  1. max_withdrawals_per_epoch is now applied once to validator withdrawals and once to refund withdrawals, so a configured cap of N can emit up to 2N withdrawals in one terminal block. Is that intended as a per-kind cap? If the parameter is meant to remain a total per-epoch withdrawal cap, this seems like it needs either a separate refund cap or total-cap accounting with validator priority.

  2. Because this changes the persisted/checkpoint encoding of ConsensusState / PendingWithdrawal / WithdrawalQueue, this would not support upgrading nodes from a pre-PR persisted state/checkpoint unless there is a migration or explicit state reset. Is that expected for this fix?

@matthias-wright

Copy link
Copy Markdown
Collaborator Author
  1. Good point. I think this is okay for now. We could add separate limits for the two withdrawal types, but I'm trying to avoid to many merge conflicts.
  2. Yes, that is expected.

@sebastian-osec

Copy link
Copy Markdown

Mostly LGTM from me after the later commits. I think the core starvation issue is addressed: validator withdrawals and deposit-refund withdrawals now have separate schedules, validator withdrawals are selected first, and refunds no longer consume validator-exit capacity.

One optional clarification: get_withdrawals_for_epoch_with_limits now supports separate validator/refund limits, but the finalizer currently passes max_withdrawals_per_epoch for both. So if max_withdrawals_per_epoch = N, the effective behavior is up to N validator withdrawals plus up to N refund withdrawals in the same terminal block.

Is that intended as the new per-kind meaning of max_withdrawals_per_epoch? If yes, it may be worth updating the docs to make that explicit. If the intent is to keep max_withdrawals_per_epoch as a total cap, then we may want either a separate refund config or total-cap accounting with validator withdrawals taking priority.

Small doc nit: docs/deposits-and-withdrawals.md still says refund withdrawals merge into an existing pending validator withdrawal, but the new queue rejects cross-kind merges and keeps refunds separate.

@matthias-wright

Copy link
Copy Markdown
Collaborator Author

Update(5430f89):

  • Make max_withdrawals_per_epoch a total cap with exit priority

@sebastian-osec

Copy link
Copy Markdown

Looks good to me.

Only small cleanup nits:

  • finalizer/src/actor.rs still has a stale comment saying “per-kind withdrawal budgets”.
  • docs/ssz-merklization.md still describes max_withdrawals_per_epoch as “Max validator withdrawals per epoch”; it should say total withdrawals per epoch.

Other than that, I think the core issue is fixed.

@matthias-wright matthias-wright merged commit d5eace6 into audit-may-2026 Jun 26, 2026
4 checks passed
@matthias-wright matthias-wright deleted the m/refund-withdrawals branch June 26, 2026 07:53
matthias-wright added a commit that referenced this pull request Jun 26, 2026
Builds on #276 (which builds on #275).

Addresses #374.

Changes:
-Guard the Inactive deposit branch on account.balance > 0 to distinguish a staged-removal validator (still holds a credited balance) from a new-validator placeholder (balance 0). For the former, withdraw the full credited balance (balance_deduction = balance), refund the top-up separately, and let the completion path remove the account. Placeholders and active top-ups are unaffected.
-Add regression test test_last_block_topup_does_not_drop_staged_removal_balance.
-Refund staged-removal top-up untaxed.
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.

2 participants