mirror of
https://github.com/status-im/eth2.0-specs.git
synced 2025-03-02 19:20:34 +00:00
handle top-ups to exiting/exited validators
This commit is contained in:
parent
f968d62459
commit
69ee35d4e3
@ -798,12 +798,27 @@ def process_pending_balance_deposits(state: BeaconState) -> None:
|
|||||||
available_for_processing = state.deposit_balance_to_consume + get_activation_exit_churn_limit(state)
|
available_for_processing = state.deposit_balance_to_consume + get_activation_exit_churn_limit(state)
|
||||||
processed_amount = 0
|
processed_amount = 0
|
||||||
next_deposit_index = 0
|
next_deposit_index = 0
|
||||||
|
deposits_to_postpone = []
|
||||||
|
|
||||||
for deposit in state.pending_balance_deposits:
|
for deposit in state.pending_balance_deposits:
|
||||||
|
validator = state.validators[deposit.index]
|
||||||
|
# Validator is exiting, postpone the deposit until after withdrawable epoch
|
||||||
|
if validator.exit_epoch < FAR_FUTURE_EPOCH:
|
||||||
|
if get_current_epoch(state) <= validator.withdrawable_epoch:
|
||||||
|
deposits_to_postpone.append(deposit)
|
||||||
|
# Deposited balance will never become active. Increase balance but do not consume churn
|
||||||
|
else:
|
||||||
|
increase_balance(state, deposit.index, deposit.amount)
|
||||||
|
# Validator is not exiting, attempt to process deposit
|
||||||
|
else:
|
||||||
|
# Deposit does not fit in the churn, no more deposit processing in this epoch.
|
||||||
if processed_amount + deposit.amount > available_for_processing:
|
if processed_amount + deposit.amount > available_for_processing:
|
||||||
break
|
break
|
||||||
|
# Deposit fits in the churn, process it. Increase balance and consume churn.
|
||||||
|
else:
|
||||||
increase_balance(state, deposit.index, deposit.amount)
|
increase_balance(state, deposit.index, deposit.amount)
|
||||||
processed_amount += deposit.amount
|
processed_amount += deposit.amount
|
||||||
|
# Regardless of how the deposit was handled, we move on in the queue.
|
||||||
next_deposit_index += 1
|
next_deposit_index += 1
|
||||||
|
|
||||||
state.pending_balance_deposits = state.pending_balance_deposits[next_deposit_index:]
|
state.pending_balance_deposits = state.pending_balance_deposits[next_deposit_index:]
|
||||||
@ -812,6 +827,8 @@ def process_pending_balance_deposits(state: BeaconState) -> None:
|
|||||||
state.deposit_balance_to_consume = Gwei(0)
|
state.deposit_balance_to_consume = Gwei(0)
|
||||||
else:
|
else:
|
||||||
state.deposit_balance_to_consume = available_for_processing - processed_amount
|
state.deposit_balance_to_consume = available_for_processing - processed_amount
|
||||||
|
|
||||||
|
state.pending_balance_deposits += deposits_to_postpone
|
||||||
```
|
```
|
||||||
|
|
||||||
#### New `process_pending_consolidations`
|
#### New `process_pending_consolidations`
|
||||||
|
@ -132,3 +132,133 @@ def test_multiple_pending_deposits_above_churn(spec, state):
|
|||||||
assert state.pending_balance_deposits == [
|
assert state.pending_balance_deposits == [
|
||||||
spec.PendingBalanceDeposit(index=2, amount=amount)
|
spec.PendingBalanceDeposit(index=2, amount=amount)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@with_electra_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_skipped_deposit_exiting_validator(spec, state):
|
||||||
|
index = 0
|
||||||
|
amount = spec.MIN_ACTIVATION_BALANCE
|
||||||
|
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=index, amount=amount))
|
||||||
|
pre_pending_balance_deposits = state.pending_balance_deposits.copy()
|
||||||
|
pre_balance = state.balances[index]
|
||||||
|
# Initiate the validator's exit
|
||||||
|
spec.initiate_validator_exit(state, index)
|
||||||
|
yield from run_epoch_processing_with(spec, state, 'process_pending_balance_deposits')
|
||||||
|
# Deposit is skipped because validator is exiting
|
||||||
|
assert state.balances[index] == pre_balance
|
||||||
|
# All deposits either processed or postponed, no leftover deposit balance to consume
|
||||||
|
assert state.deposit_balance_to_consume == 0
|
||||||
|
# The deposit is still in the queue
|
||||||
|
assert state.pending_balance_deposits == pre_pending_balance_deposits
|
||||||
|
|
||||||
|
|
||||||
|
@with_electra_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_multiple_skipped_deposits_exiting_validators(spec, state):
|
||||||
|
amount = spec.EFFECTIVE_BALANCE_INCREMENT
|
||||||
|
for i in [0, 1, 2]:
|
||||||
|
# Append pending deposit for validator i
|
||||||
|
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=i, amount=amount))
|
||||||
|
|
||||||
|
# Initiate the exit of validator i
|
||||||
|
spec.initiate_validator_exit(state, i)
|
||||||
|
pre_pending_balance_deposits = state.pending_balance_deposits.copy()
|
||||||
|
pre_balances = state.balances.copy()
|
||||||
|
yield from run_epoch_processing_with(spec, state, 'process_pending_balance_deposits')
|
||||||
|
# All deposits are postponed, no balance changes
|
||||||
|
assert state.balances == pre_balances
|
||||||
|
# All deposits are postponed, no leftover deposit balance to consume
|
||||||
|
assert state.deposit_balance_to_consume == 0
|
||||||
|
# All deposits still in the queue, in the same order
|
||||||
|
assert state.pending_balance_deposits == pre_pending_balance_deposits
|
||||||
|
|
||||||
|
|
||||||
|
@with_electra_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_multiple_pending_one_skipped(spec, state):
|
||||||
|
amount = spec.EFFECTIVE_BALANCE_INCREMENT
|
||||||
|
for i in [0, 1, 2]:
|
||||||
|
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=i, amount=amount))
|
||||||
|
pre_balances = state.balances.copy()
|
||||||
|
# Initiate the second validator's exit
|
||||||
|
spec.initiate_validator_exit(state, 1)
|
||||||
|
yield from run_epoch_processing_with(spec, state, 'process_pending_balance_deposits')
|
||||||
|
# First and last deposit are processed, second is not because of exiting
|
||||||
|
for i in [0, 2]:
|
||||||
|
assert state.balances[i] == pre_balances[i] + amount
|
||||||
|
assert state.balances[1] == pre_balances[1]
|
||||||
|
# All deposits either processed or postponed, no leftover deposit balance to consume
|
||||||
|
assert state.deposit_balance_to_consume == 0
|
||||||
|
# second deposit is still in the queue
|
||||||
|
assert state.pending_balance_deposits == [spec.PendingBalanceDeposit(index=1, amount=amount)]
|
||||||
|
|
||||||
|
|
||||||
|
@with_electra_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_mixture_of_skipped_and_above_churn(spec, state):
|
||||||
|
amount01 = spec.EFFECTIVE_BALANCE_INCREMENT
|
||||||
|
amount2 = spec.MAX_EFFECTIVE_BALANCE_ELECTRA
|
||||||
|
# First two validators have small deposit, third validators a large one
|
||||||
|
for i in [0, 1]:
|
||||||
|
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=i, amount=amount01))
|
||||||
|
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=2, amount=amount2))
|
||||||
|
pre_balances = state.balances.copy()
|
||||||
|
# Initiate the second validator's exit
|
||||||
|
spec.initiate_validator_exit(state, 1)
|
||||||
|
yield from run_epoch_processing_with(spec, state, 'process_pending_balance_deposits')
|
||||||
|
# First deposit is processed
|
||||||
|
assert state.balances[0] == pre_balances[0] + amount01
|
||||||
|
# Second deposit is postponed, third is above churn
|
||||||
|
for i in [1, 2]:
|
||||||
|
assert state.balances[i] == pre_balances[i]
|
||||||
|
# First deposit consumes some deposit balance
|
||||||
|
# Deposit balance to consume is not reset because third deposit is not processed
|
||||||
|
assert state.deposit_balance_to_consume == spec.get_activation_exit_churn_limit(state) - amount01
|
||||||
|
# second and third deposit still in the queue, but second is appended at the end
|
||||||
|
assert state.pending_balance_deposits == [spec.PendingBalanceDeposit(index=2, amount=amount2),
|
||||||
|
spec.PendingBalanceDeposit(index=1, amount=amount01)]
|
||||||
|
|
||||||
|
|
||||||
|
@with_electra_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_processing_deposit_of_withdrawable_validator(spec, state):
|
||||||
|
index = 0
|
||||||
|
amount = spec.MIN_ACTIVATION_BALANCE
|
||||||
|
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=index, amount=amount))
|
||||||
|
pre_balance = state.balances[index]
|
||||||
|
# Initiate the validator's exit
|
||||||
|
spec.initiate_validator_exit(state, index)
|
||||||
|
# Set epoch to withdrawable epoch + 1 to allow processing of the deposit
|
||||||
|
state.slot = spec.SLOTS_PER_EPOCH * (state.validators[index].withdrawable_epoch + 1)
|
||||||
|
yield from run_epoch_processing_with(spec, state, 'process_pending_balance_deposits')
|
||||||
|
# Deposit is correctly processed
|
||||||
|
assert state.balances[index] == pre_balance + amount
|
||||||
|
# No leftover deposit balance to consume when there are no deposits left to process
|
||||||
|
assert state.deposit_balance_to_consume == 0
|
||||||
|
assert state.pending_balance_deposits == []
|
||||||
|
|
||||||
|
|
||||||
|
@with_electra_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_processing_deposit_of_withdrawable_validator_does_not_get_churned(spec, state):
|
||||||
|
amount = spec.MAX_EFFECTIVE_BALANCE_ELECTRA
|
||||||
|
for i in [0, 1]:
|
||||||
|
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=i, amount=amount))
|
||||||
|
pre_balances = state.balances.copy()
|
||||||
|
# Initiate the first validator's exit
|
||||||
|
spec.initiate_validator_exit(state, 0)
|
||||||
|
# Set epoch to withdrawable epoch + 1 to allow processing of the deposit
|
||||||
|
state.slot = spec.SLOTS_PER_EPOCH * (state.validators[0].withdrawable_epoch + 1)
|
||||||
|
# Don't use run_epoch_processing_with to avoid penalties being applied
|
||||||
|
yield 'pre', state
|
||||||
|
spec.process_pending_balance_deposits(state)
|
||||||
|
yield 'post', state
|
||||||
|
# First deposit is processed though above churn limit, because validator is withdrawable
|
||||||
|
assert state.balances[0] == pre_balances[0] + amount
|
||||||
|
# Second deposit is not processed because above churn
|
||||||
|
assert state.balances[1] == pre_balances[1]
|
||||||
|
# Second deposit is not processed, so there's leftover deposit balance to consume.
|
||||||
|
# First deposit does not consume any.
|
||||||
|
assert state.deposit_balance_to_consume == spec.get_activation_exit_churn_limit(state)
|
||||||
|
assert state.pending_balance_deposits == [spec.PendingBalanceDeposit(index=1, amount=amount)]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user