Skip to content

Commit 9bf115f

Browse files
authored
Reset withdrawable epoch when depositing to an exited builder (#5373)
@brech1 [has pointed out](https://notes.ethereum.org/@brech1/crowd-validator-sweep) an easy-to-abuse way of slowing down the validator sweep. This PR implements his suggested solution of resetting the builder's withdrawable epoch on deposit top-ups so that it cannot re-use those funds to sustain this attack. My napkin math tells me that it would require 75 ETH (~$150k) per minute to sustain this attack. This solution would prevent an attack from quickly getting that ETH back to re-deposit.
1 parent 8ebaf5b commit 9bf115f

2 files changed

Lines changed: 59 additions & 2 deletions

File tree

specs/gloas/beacon-chain.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,9 +1587,16 @@ def apply_deposit_for_builder(
15871587
if is_valid_deposit_signature(pubkey, withdrawal_credentials, amount, signature):
15881588
add_builder_to_registry(state, pubkey, withdrawal_credentials, amount, slot)
15891589
else:
1590-
# Increase balance by deposit amount
15911590
builder_index = builder_pubkeys.index(pubkey)
1592-
state.builders[builder_index].balance += amount
1591+
builder = state.builders[builder_index]
1592+
1593+
# Increase balance by deposit amount
1594+
builder.balance += amount
1595+
1596+
# If exited, reset the withdrawable epoch
1597+
if builder.withdrawable_epoch != FAR_FUTURE_EPOCH:
1598+
epoch = get_current_epoch(state)
1599+
builder.withdrawable_epoch = epoch + MIN_BUILDER_WITHDRAWABILITY_DELAY
15931600
```
15941601

15951602
###### Modified `process_deposit_request`

tests/core/pyspec/eth_consensus_specs/test/gloas/block_processing/test_process_deposit_request.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,56 @@ def test_process_deposit_request__builder_top_up_large(spec, state):
205205
)
206206

207207

208+
@with_gloas_and_later
209+
@spec_state_test
210+
def test_process_deposit_request__exited_builder_top_up_resets_withdrawable_epoch(spec, state):
211+
"""
212+
Test top-up to an exited builder resets its withdrawable epoch.
213+
214+
Input State Configured:
215+
- Existing builder at index 0 that has exited (withdrawable_epoch in the past)
216+
217+
Output State Verified:
218+
- Builder balance increased (top-up)
219+
- withdrawable_epoch reset to current_epoch + MIN_BUILDER_WITHDRAWABILITY_DELAY
220+
"""
221+
builder_pubkey = state.builders[0].pubkey
222+
amount = spec.MIN_DEPOSIT_AMOUNT
223+
pre_builder_count = len(state.builders)
224+
225+
# Advance an epoch and mark builder 0 as exited (withdrawable_epoch in the past)
226+
deposit_request = prepare_process_deposit_request(
227+
spec,
228+
state,
229+
for_builder=True,
230+
pubkey=builder_pubkey,
231+
amount=amount,
232+
signed=True,
233+
advance_epochs=1,
234+
builder_modifications={0: {"withdrawable_epoch": "current_epoch-1"}},
235+
)
236+
pre_state = state.copy()
237+
expected_withdrawable_epoch = (
238+
spec.get_current_epoch(state) + spec.config.MIN_BUILDER_WITHDRAWABILITY_DELAY
239+
)
240+
# Sanity check: the exited epoch differs from the expected reset value
241+
assert state.builders[0].withdrawable_epoch != expected_withdrawable_epoch
242+
243+
yield from run_deposit_request_processing(spec, state, deposit_request)
244+
245+
assert_process_deposit_request(
246+
spec,
247+
state,
248+
pre_state,
249+
deposit_request=deposit_request,
250+
is_builder_deposit=True,
251+
expected_builder_count=pre_builder_count,
252+
expected_builder_index=0,
253+
expected_builder_balance_delta=amount,
254+
expected_builder_withdrawable_epoch=expected_withdrawable_epoch,
255+
)
256+
257+
208258
#
209259
# Invalid signature tests
210260
#

0 commit comments

Comments
 (0)