Move old withdrawal epoch_processing tests to block_processing
This commit is contained in:
parent
494cefcda6
commit
b530dc09aa
|
@ -1,11 +1,24 @@
|
|||
import random
|
||||
|
||||
from eth2spec.test.context import (
|
||||
spec_state_test,
|
||||
expect_assertion_error,
|
||||
with_capella_and_later,
|
||||
with_presets,
|
||||
)
|
||||
from eth2spec.test.helpers.constants import MINIMAL
|
||||
from eth2spec.test.helpers.execution_payload import (
|
||||
build_empty_execution_payload,
|
||||
)
|
||||
|
||||
from eth2spec.test.context import spec_state_test, expect_assertion_error, with_capella_and_later
|
||||
from eth2spec.test.helpers.state import next_slot
|
||||
from eth2spec.test.helpers.random import (
|
||||
randomize_state,
|
||||
)
|
||||
from eth2spec.test.helpers.state import (
|
||||
next_epoch,
|
||||
next_slot,
|
||||
)
|
||||
from eth2spec.test.helpers.withdrawals import (
|
||||
set_eth1_withdrawal_credential_with_balance,
|
||||
set_validator_fully_withdrawable,
|
||||
set_validator_partially_withdrawable,
|
||||
)
|
||||
|
@ -57,7 +70,7 @@ def verify_post_state(state, spec, expected_withdrawals,
|
|||
assert state.balances[index] > spec.MAX_EFFECTIVE_BALANCE
|
||||
|
||||
|
||||
def run_withdrawals_processing(spec, state, execution_payload, valid=True):
|
||||
def run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=None, valid=True):
|
||||
"""
|
||||
Run ``process_execution_payload``, yielding:
|
||||
- pre-state ('pre')
|
||||
|
@ -67,6 +80,8 @@ def run_withdrawals_processing(spec, state, execution_payload, valid=True):
|
|||
"""
|
||||
expected_withdrawals = spec.get_expected_withdrawals(state)
|
||||
assert len(expected_withdrawals) <= spec.MAX_WITHDRAWALS_PER_PAYLOAD
|
||||
if num_expected_withdrawals is not None:
|
||||
assert len(expected_withdrawals) == num_expected_withdrawals
|
||||
|
||||
pre_state = state.copy()
|
||||
yield 'pre', state
|
||||
|
@ -433,3 +448,349 @@ def test_fail_many_dequeued_incorrectly_partial(spec, state):
|
|||
withdrawal.amount += 1
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
#
|
||||
# More full withdrawal cases
|
||||
#
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_withdrawable_epoch_but_0_balance(spec, state):
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
set_validator_fully_withdrawable(spec, state, 0, current_epoch)
|
||||
|
||||
state.validators[0].effective_balance = 10000000000
|
||||
state.balances[0] = 0
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_withdrawable_epoch_but_0_effective_balance_0_balance(spec, state):
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
set_validator_fully_withdrawable(spec, state, 0, current_epoch)
|
||||
|
||||
state.validators[0].effective_balance = 0
|
||||
state.balances[0] = 0
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_withdrawable_epoch_but_0_effective_balance_nonzero_balance(spec, state):
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
set_validator_fully_withdrawable(spec, state, 0, current_epoch)
|
||||
|
||||
state.validators[0].effective_balance = 0
|
||||
state.balances[0] = 100000000
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_no_withdrawals_but_some_next_epoch(spec, state):
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
|
||||
# Make a few validators withdrawable at the *next* epoch
|
||||
for index in range(3):
|
||||
set_validator_fully_withdrawable(spec, state, index, current_epoch + 1)
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_all_withdrawal(spec, state):
|
||||
# Make all validators withdrawable
|
||||
for index in range(len(state.validators)):
|
||||
set_validator_fully_withdrawable(spec, state, index)
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(
|
||||
spec, state, execution_payload,
|
||||
num_expected_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD)
|
||||
|
||||
|
||||
def run_random_full_withdrawals_test(spec, state, rng):
|
||||
randomize_state(spec, state, rng)
|
||||
for index in range(len(state.validators)):
|
||||
# 50% withdrawable
|
||||
if rng.choice([True, False]):
|
||||
set_validator_fully_withdrawable(spec, state, index)
|
||||
validator = state.validators[index]
|
||||
# 12.5% unset credentials
|
||||
if rng.randint(0, 7) == 0:
|
||||
validator.withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:]
|
||||
# 12.5% not enough balance
|
||||
if rng.randint(0, 7) == 0:
|
||||
state.balances[index] = 0
|
||||
# 12.5% not close enough epoch
|
||||
if rng.randint(0, 7) == 0:
|
||||
validator.withdrawable_epoch += 1
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_full_withdrawals_0(spec, state):
|
||||
yield from run_random_full_withdrawals_test(spec, state, random.Random(444))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_full_withdrawals_1(spec, state):
|
||||
yield from run_random_full_withdrawals_test(spec, state, random.Random(420))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_full_withdrawals_2(spec, state):
|
||||
yield from run_random_full_withdrawals_test(spec, state, random.Random(200))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_full_withdrawals_3(spec, state):
|
||||
yield from run_random_full_withdrawals_test(spec, state, random.Random(2000000))
|
||||
|
||||
|
||||
#
|
||||
# More partial withdrawal cases
|
||||
#
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_no_max_effective_balance(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
# To be partially withdrawable, the validator's effective balance must be maxed out
|
||||
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, spec.MAX_EFFECTIVE_BALANCE - 1)
|
||||
validator = state.validators[validator_index]
|
||||
|
||||
assert validator.effective_balance < spec.MAX_EFFECTIVE_BALANCE
|
||||
assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index])
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_no_excess_balance(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
# To be partially withdrawable, the validator needs an excess balance
|
||||
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, spec.MAX_EFFECTIVE_BALANCE)
|
||||
validator = state.validators[validator_index]
|
||||
|
||||
assert validator.effective_balance == spec.MAX_EFFECTIVE_BALANCE
|
||||
assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index])
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_excess_balance_but_no_max_effective_balance(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
validator = state.validators[validator_index]
|
||||
|
||||
# To be partially withdrawable, the validator needs both a maxed out effective balance and an excess balance
|
||||
validator.effective_balance = spec.MAX_EFFECTIVE_BALANCE - 1
|
||||
|
||||
assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index])
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable_not_yet_active(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
state.validators[validator_index].activation_epoch += 4
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable_in_exit_queue(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
state.validators[validator_index].exit_epoch = spec.get_current_epoch(state) + 1
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
assert spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state) + 1)
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable_exited(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
state.validators[validator_index].exit_epoch = spec.get_current_epoch(state)
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable_active_and_slashed(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
state.validators[validator_index].slashed = True
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
assert spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable_exited_and_slashed(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
state.validators[validator_index].slashed = True
|
||||
state.validators[validator_index].exit_epoch = spec.get_current_epoch(state)
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_two_partial_withdrawable(spec, state):
|
||||
set_validator_partially_withdrawable(spec, state, 0)
|
||||
set_validator_partially_withdrawable(spec, state, 1)
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=2)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_max_partial_withdrawable(spec, state):
|
||||
# Sanity check that this test works for this state
|
||||
assert len(state.validators) >= spec.MAX_WITHDRAWALS_PER_PAYLOAD
|
||||
|
||||
for i in range(spec.MAX_WITHDRAWALS_PER_PAYLOAD):
|
||||
set_validator_partially_withdrawable(spec, state, i)
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(
|
||||
spec, state, execution_payload, num_expected_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_presets([MINIMAL], reason="not enough validators with mainnet config")
|
||||
@spec_state_test
|
||||
def test_success_max_plus_one_withdrawable(spec, state):
|
||||
# Sanity check that this test works for this state
|
||||
assert len(state.validators) >= spec.MAX_WITHDRAWALS_PER_PAYLOAD + 1
|
||||
|
||||
# More than MAX_WITHDRAWALS_PER_PAYLOAD partially withdrawable
|
||||
for i in range(spec.MAX_WITHDRAWALS_PER_PAYLOAD + 1):
|
||||
set_validator_partially_withdrawable(spec, state, i)
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
# Should only have MAX_WITHDRAWALS_PER_PAYLOAD withdrawals created
|
||||
yield from run_withdrawals_processing(
|
||||
spec, state, execution_payload, num_expected_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD)
|
||||
|
||||
|
||||
def run_random_partial_withdrawals_test(spec, state, rng):
|
||||
for _ in range(rng.randint(0, 2)):
|
||||
next_epoch(spec, state)
|
||||
randomize_state(spec, state, rng)
|
||||
|
||||
num_validators = len(state.validators)
|
||||
state.last_withdrawal_validator_index = rng.randint(0, num_validators - 1)
|
||||
|
||||
num_partially_withdrawable = rng.randint(0, num_validators - 1)
|
||||
partially_withdrawable_indices = rng.sample(range(num_validators), num_partially_withdrawable)
|
||||
for index in partially_withdrawable_indices:
|
||||
set_validator_partially_withdrawable(spec, state, index, excess_balance=rng.randint(1, 1000000000))
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
# Note: due to the randomness and other epoch processing, some of these set as "partially withdrawable"
|
||||
# may not be partially withdrawable once we get to ``process_partial_withdrawals``,
|
||||
# thus *not* using the optional third param in this call
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_0(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(0))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_partial_withdrawals_1(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(1))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_partial_withdrawals_2(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(2))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_partial_withdrawals_3(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(3))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_partial_withdrawals_4(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(4))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_partial_withdrawals_5(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(5))
|
||||
|
|
|
@ -1,174 +0,0 @@
|
|||
from random import Random
|
||||
|
||||
from eth2spec.test.context import (
|
||||
with_capella_and_later,
|
||||
spec_state_test,
|
||||
)
|
||||
from eth2spec.test.helpers.random import (
|
||||
randomize_state,
|
||||
)
|
||||
from eth2spec.test.helpers.epoch_processing import (
|
||||
run_epoch_processing_to,
|
||||
)
|
||||
from eth2spec.test.helpers.withdrawals import (
|
||||
set_validator_fully_withdrawable,
|
||||
)
|
||||
|
||||
|
||||
def run_process_full_withdrawals(spec, state, num_expected_withdrawals=None):
|
||||
run_epoch_processing_to(spec, state, 'process_full_withdrawals')
|
||||
|
||||
pre_next_withdrawal_index = state.next_withdrawal_index
|
||||
pre_withdrawal_queue = state.withdrawal_queue.copy()
|
||||
to_be_withdrawn_indices = [
|
||||
index for index, validator in enumerate(state.validators)
|
||||
if spec.is_fully_withdrawable_validator(validator, state.balances[index], spec.get_current_epoch(state))
|
||||
]
|
||||
|
||||
if num_expected_withdrawals is not None:
|
||||
assert len(to_be_withdrawn_indices) == num_expected_withdrawals
|
||||
else:
|
||||
num_expected_withdrawals = len(to_be_withdrawn_indices)
|
||||
|
||||
yield 'pre', state
|
||||
spec.process_full_withdrawals(state)
|
||||
yield 'post', state
|
||||
|
||||
for index in to_be_withdrawn_indices:
|
||||
assert state.balances[index] == 0
|
||||
|
||||
assert len(state.withdrawal_queue) == len(pre_withdrawal_queue) + num_expected_withdrawals
|
||||
assert state.next_withdrawal_index == pre_next_withdrawal_index + num_expected_withdrawals
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_no_withdrawable_validators(spec, state):
|
||||
pre_validators = state.validators.copy()
|
||||
yield from run_process_full_withdrawals(spec, state, 0)
|
||||
|
||||
assert pre_validators == state.validators
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_withdrawable_epoch_but_0_balance(spec, state):
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
set_validator_fully_withdrawable(spec, state, 0, current_epoch)
|
||||
|
||||
state.validators[0].effective_balance = 10000000000
|
||||
state.balances[0] = 0
|
||||
|
||||
yield from run_process_full_withdrawals(spec, state, 0)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_withdrawable_epoch_but_0_effective_balance_0_balance(spec, state):
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
set_validator_fully_withdrawable(spec, state, 0, current_epoch)
|
||||
|
||||
state.validators[0].effective_balance = 0
|
||||
state.balances[0] = 0
|
||||
|
||||
yield from run_process_full_withdrawals(spec, state, 0)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_withdrawable_epoch_but_0_effective_balance_nonzero_balance(spec, state):
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
set_validator_fully_withdrawable(spec, state, 0, current_epoch)
|
||||
|
||||
state.validators[0].effective_balance = 0
|
||||
state.balances[0] = 100000000
|
||||
|
||||
yield from run_process_full_withdrawals(spec, state, 1)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_no_withdrawals_but_some_next_epoch(spec, state):
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
|
||||
# Make a few validators withdrawable at the *next* epoch
|
||||
for index in range(3):
|
||||
set_validator_fully_withdrawable(spec, state, index, current_epoch + 1)
|
||||
|
||||
yield from run_process_full_withdrawals(spec, state, 0)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_single_withdrawal(spec, state):
|
||||
# Make one validator withdrawable
|
||||
set_validator_fully_withdrawable(spec, state, 0)
|
||||
|
||||
assert state.next_withdrawal_index == 0
|
||||
yield from run_process_full_withdrawals(spec, state, 1)
|
||||
|
||||
assert state.next_withdrawal_index == 1
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_multi_withdrawal(spec, state):
|
||||
# Make a few validators withdrawable
|
||||
for index in range(3):
|
||||
set_validator_fully_withdrawable(spec, state, index)
|
||||
|
||||
yield from run_process_full_withdrawals(spec, state, 3)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_all_withdrawal(spec, state):
|
||||
# Make all validators withdrawable
|
||||
for index in range(len(state.validators)):
|
||||
set_validator_fully_withdrawable(spec, state, index)
|
||||
|
||||
yield from run_process_full_withdrawals(spec, state, len(state.validators))
|
||||
|
||||
|
||||
def run_random_full_withdrawals_test(spec, state, rng):
|
||||
randomize_state(spec, state, rng)
|
||||
for index in range(len(state.validators)):
|
||||
# 50% withdrawable
|
||||
if rng.choice([True, False]):
|
||||
set_validator_fully_withdrawable(spec, state, index)
|
||||
validator = state.validators[index]
|
||||
# 12.5% unset credentials
|
||||
if rng.randint(0, 7) == 0:
|
||||
validator.withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:]
|
||||
# 12.5% not enough balance
|
||||
if rng.randint(0, 7) == 0:
|
||||
state.balances[index] = 0
|
||||
# 12.5% not close enough epoch
|
||||
if rng.randint(0, 7) == 0:
|
||||
validator.withdrawable_epoch += 1
|
||||
|
||||
yield from run_process_full_withdrawals(spec, state, None)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_withdrawals_0(spec, state):
|
||||
yield from run_random_full_withdrawals_test(spec, state, Random(444))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_withdrawals_1(spec, state):
|
||||
yield from run_random_full_withdrawals_test(spec, state, Random(420))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_withdrawals_2(spec, state):
|
||||
yield from run_random_full_withdrawals_test(spec, state, Random(200))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_withdrawals_3(spec, state):
|
||||
yield from run_random_full_withdrawals_test(spec, state, Random(2000000))
|
|
@ -1,262 +0,0 @@
|
|||
import random
|
||||
from eth2spec.test.helpers.constants import MINIMAL
|
||||
from eth2spec.test.context import (
|
||||
with_capella_and_later,
|
||||
spec_state_test,
|
||||
with_presets,
|
||||
)
|
||||
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_to
|
||||
from eth2spec.test.helpers.state import next_epoch
|
||||
from eth2spec.test.helpers.random import randomize_state
|
||||
from eth2spec.test.helpers.withdrawals import (
|
||||
set_validator_partially_withdrawable,
|
||||
set_eth1_withdrawal_credential_with_balance,
|
||||
)
|
||||
|
||||
|
||||
def run_process_partial_withdrawals(spec, state, num_expected_withdrawals=None):
|
||||
# Run rest of epoch processing before predicting partial withdrawals as
|
||||
# balance changes can affect withdrawability
|
||||
run_epoch_processing_to(spec, state, 'process_partial_withdrawals')
|
||||
|
||||
pre_next_withdrawal_index = state.next_withdrawal_index
|
||||
pre_withdrawal_queue = state.withdrawal_queue.copy()
|
||||
|
||||
partially_withdrawable_indices = [
|
||||
index for index, validator in enumerate(state.validators)
|
||||
if spec.is_partially_withdrawable_validator(validator, state.balances[index])
|
||||
]
|
||||
num_partial_withdrawals = min(len(partially_withdrawable_indices), spec.MAX_PARTIAL_WITHDRAWALS_PER_EPOCH)
|
||||
|
||||
if num_expected_withdrawals is not None:
|
||||
assert num_partial_withdrawals == num_expected_withdrawals
|
||||
else:
|
||||
num_expected_withdrawals = num_partial_withdrawals
|
||||
|
||||
yield 'pre', state
|
||||
spec.process_partial_withdrawals(state)
|
||||
yield 'post', state
|
||||
|
||||
post_partially_withdrawable_indices = [
|
||||
index for index, validator in enumerate(state.validators)
|
||||
if spec.is_partially_withdrawable_validator(validator, state.balances[index])
|
||||
]
|
||||
|
||||
assert len(partially_withdrawable_indices) - num_partial_withdrawals == len(post_partially_withdrawable_indices)
|
||||
|
||||
assert len(state.withdrawal_queue) == len(pre_withdrawal_queue) + num_expected_withdrawals
|
||||
assert state.next_withdrawal_index == pre_next_withdrawal_index + num_expected_withdrawals
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_no_withdrawable(spec, state):
|
||||
pre_validators = state.validators.copy()
|
||||
yield from run_process_partial_withdrawals(spec, state, 0)
|
||||
|
||||
assert pre_validators == state.validators
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_no_max_effective_balance(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
# To be partially withdrawable, the validator's effective balance must be maxed out
|
||||
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, spec.MAX_EFFECTIVE_BALANCE - 1)
|
||||
validator = state.validators[validator_index]
|
||||
|
||||
assert validator.effective_balance < spec.MAX_EFFECTIVE_BALANCE
|
||||
assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index])
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, 0)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_no_excess_balance(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
# To be partially withdrawable, the validator needs an excess balance
|
||||
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, spec.MAX_EFFECTIVE_BALANCE)
|
||||
validator = state.validators[validator_index]
|
||||
|
||||
assert validator.effective_balance == spec.MAX_EFFECTIVE_BALANCE
|
||||
assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index])
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, 0)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_excess_balance_but_no_max_effective_balance(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
validator = state.validators[validator_index]
|
||||
|
||||
# To be partially withdrawable, the validator needs both a maxed out effective balance and an excess balance
|
||||
validator.effective_balance = spec.MAX_EFFECTIVE_BALANCE - 1
|
||||
|
||||
assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index])
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, 0)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, 1)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable_not_yet_active(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
state.validators[validator_index].activation_epoch += 4
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, 1)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable_in_exit_queue(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
state.validators[validator_index].exit_epoch = spec.get_current_epoch(state) + 1
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
assert spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state) + 1)
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, 1)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable_exited(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
state.validators[validator_index].exit_epoch = spec.get_current_epoch(state)
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, 1)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable_active_and_slashed(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
state.validators[validator_index].slashed = True
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
assert spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, 1)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable_exited_and_slashed(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
state.validators[validator_index].slashed = True
|
||||
state.validators[validator_index].exit_epoch = spec.get_current_epoch(state)
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, 1)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_two_partial_withdrawable(spec, state):
|
||||
set_validator_partially_withdrawable(spec, state, 0)
|
||||
set_validator_partially_withdrawable(spec, state, 1)
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, 2)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_max_partial_withdrawable(spec, state):
|
||||
# Sanity check that this test works for this state
|
||||
assert len(state.validators) >= spec.MAX_PARTIAL_WITHDRAWALS_PER_EPOCH
|
||||
|
||||
for i in range(spec.MAX_PARTIAL_WITHDRAWALS_PER_EPOCH):
|
||||
set_validator_partially_withdrawable(spec, state, i)
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, spec.MAX_PARTIAL_WITHDRAWALS_PER_EPOCH)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_presets([MINIMAL], reason="not enough validators with mainnet config")
|
||||
@spec_state_test
|
||||
def test_success_max_plus_one_withdrawable(spec, state):
|
||||
# Sanity check that this test works for this state
|
||||
assert len(state.validators) >= spec.MAX_PARTIAL_WITHDRAWALS_PER_EPOCH + 1
|
||||
|
||||
# More than MAX_PARTIAL_WITHDRAWALS_PER_EPOCH partially withdrawable
|
||||
for i in range(spec.MAX_PARTIAL_WITHDRAWALS_PER_EPOCH + 1):
|
||||
set_validator_partially_withdrawable(spec, state, i)
|
||||
|
||||
# Should only have MAX_PARTIAL_WITHDRAWALS_PER_EPOCH withdrawals created
|
||||
yield from run_process_partial_withdrawals(spec, state, spec.MAX_PARTIAL_WITHDRAWALS_PER_EPOCH)
|
||||
|
||||
|
||||
def run_random_partial_withdrawals_test(spec, state, rng):
|
||||
for _ in range(rng.randint(0, 2)):
|
||||
next_epoch(spec, state)
|
||||
randomize_state(spec, state, rng)
|
||||
|
||||
num_validators = len(state.validators)
|
||||
state.next_partial_withdrawal_validator_index = rng.randint(0, num_validators - 1)
|
||||
|
||||
num_partially_withdrawable = rng.randint(0, num_validators - 1)
|
||||
partially_withdrawable_indices = rng.sample(range(num_validators), num_partially_withdrawable)
|
||||
for index in partially_withdrawable_indices:
|
||||
set_validator_partially_withdrawable(spec, state, index, excess_balance=rng.randint(1, 1000000000))
|
||||
|
||||
# Note: due to the randomness and other epoch processing, some of these set as "partially withdrawable"
|
||||
# may not be partially withdrawable once we get to ``process_partial_withdrawals``,
|
||||
# thus *not* using the optional third param in this call
|
||||
yield from run_process_partial_withdrawals(spec, state)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_0(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(0))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_1(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(1))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_2(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(2))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_3(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(3))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_4(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(4))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_5(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(5))
|
Loading…
Reference in New Issue