Skip to content

Commit

Permalink
make partial withdrawal randomized tests better
Browse files Browse the repository at this point in the history
  • Loading branch information
djrtwo committed Jun 3, 2022
1 parent d892e37 commit 5595953
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@
from eth2spec.test.helpers.state import next_slot


def prepare_withdrawals_queue(spec, state, num_withdrawals):
pre_queue_len = len(state.withdrawals_queue)
def prepare_withdrawal_queue(spec, state, num_withdrawals):
pre_queue_len = len(state.withdrawal_queue)

for i in range(num_withdrawals):
withdrawal = spec.Withdrawal(
index=i + 5,
address=b'\x42' * 20,
amount=200000 + i,
)
state.withdrawals_queue.append(withdrawal)
state.withdrawal_queue.append(withdrawal)

assert len(state.withdrawals_queue) == num_withdrawals + pre_queue_len
assert len(state.withdrawal_queue) == num_withdrawals + pre_queue_len


def run_withdrawals_processing(spec, state, execution_payload, valid=True):
Expand All @@ -30,8 +30,8 @@ def run_withdrawals_processing(spec, state, execution_payload, valid=True):
If ``valid == False``, run expecting ``AssertionError``
"""

pre_withdrawals_queue = state.withdrawals_queue.copy()
num_withdrawals = min(spec.MAX_WITHDRAWALS_PER_PAYLOAD, len(pre_withdrawals_queue))
pre_withdrawal_queue = state.withdrawal_queue.copy()
num_withdrawals = min(spec.MAX_WITHDRAWALS_PER_PAYLOAD, len(pre_withdrawal_queue))

yield 'pre', state
yield 'execution_payload', execution_payload
Expand All @@ -45,18 +45,18 @@ def run_withdrawals_processing(spec, state, execution_payload, valid=True):

yield 'post', state

if len(pre_withdrawals_queue) == 0:
assert len(state.withdrawals_queue) == 0
elif len(pre_withdrawals_queue) <= num_withdrawals:
assert len(state.withdrawals_queue) == 0
if len(pre_withdrawal_queue) == 0:
assert len(state.withdrawal_queue) == 0
elif len(pre_withdrawal_queue) <= num_withdrawals:
assert len(state.withdrawal_queue) == 0
else:
assert state.withdrawals_queue == pre_withdrawals_queue[num_withdrawals:]
assert state.withdrawal_queue == pre_withdrawal_queue[num_withdrawals:]


@with_capella_and_later
@spec_state_test
def test_success_empty_queue(spec, state):
assert len(state.withdrawals_queue) == 0
assert len(state.withdrawal_queue) == 0

next_slot(spec, state)
execution_payload = build_empty_execution_payload(spec, state)
Expand All @@ -67,7 +67,7 @@ def test_success_empty_queue(spec, state):
@with_capella_and_later
@spec_state_test
def test_success_one_in_queue(spec, state):
prepare_withdrawals_queue(spec, state, 1)
prepare_withdrawal_queue(spec, state, 1)

next_slot(spec, state)
execution_payload = build_empty_execution_payload(spec, state)
Expand All @@ -78,7 +78,7 @@ def test_success_one_in_queue(spec, state):
@with_capella_and_later
@spec_state_test
def test_success_max_per_slot_in_queue(spec, state):
prepare_withdrawals_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD)
prepare_withdrawal_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD)

next_slot(spec, state)
execution_payload = build_empty_execution_payload(spec, state)
Expand All @@ -89,7 +89,7 @@ def test_success_max_per_slot_in_queue(spec, state):
@with_capella_and_later
@spec_state_test
def test_success_a_lot_in_queue(spec, state):
prepare_withdrawals_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
prepare_withdrawal_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)

next_slot(spec, state)
execution_payload = build_empty_execution_payload(spec, state)
Expand All @@ -104,7 +104,7 @@ def test_success_a_lot_in_queue(spec, state):
@with_capella_and_later
@spec_state_test
def test_fail_empty_queue_non_empty_withdrawals(spec, state):
assert len(state.withdrawals_queue) == 0
assert len(state.withdrawal_queue) == 0

next_slot(spec, state)
execution_payload = build_empty_execution_payload(spec, state)
Expand All @@ -121,7 +121,7 @@ def test_fail_empty_queue_non_empty_withdrawals(spec, state):
@with_capella_and_later
@spec_state_test
def test_fail_one_in_queue_none_in_withdrawals(spec, state):
prepare_withdrawals_queue(spec, state, 1)
prepare_withdrawal_queue(spec, state, 1)

next_slot(spec, state)
execution_payload = build_empty_execution_payload(spec, state)
Expand All @@ -133,7 +133,7 @@ def test_fail_one_in_queue_none_in_withdrawals(spec, state):
@with_capella_and_later
@spec_state_test
def test_fail_one_in_queue_two_in_withdrawals(spec, state):
prepare_withdrawals_queue(spec, state, 1)
prepare_withdrawal_queue(spec, state, 1)

next_slot(spec, state)
execution_payload = build_empty_execution_payload(spec, state)
Expand All @@ -145,7 +145,7 @@ def test_fail_one_in_queue_two_in_withdrawals(spec, state):
@with_capella_and_later
@spec_state_test
def test_fail_max_per_slot_in_queue_one_less_in_withdrawals(spec, state):
prepare_withdrawals_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD)
prepare_withdrawal_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD)

next_slot(spec, state)
execution_payload = build_empty_execution_payload(spec, state)
Expand All @@ -157,7 +157,7 @@ def test_fail_max_per_slot_in_queue_one_less_in_withdrawals(spec, state):
@with_capella_and_later
@spec_state_test
def test_fail_a_lot_in_queue_too_few_in_withdrawals(spec, state):
prepare_withdrawals_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
prepare_withdrawal_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)

next_slot(spec, state)
execution_payload = build_empty_execution_payload(spec, state)
Expand All @@ -173,7 +173,7 @@ def test_fail_a_lot_in_queue_too_few_in_withdrawals(spec, state):
@with_capella_and_later
@spec_state_test
def test_fail_incorrect_dequeue_index(spec, state):
prepare_withdrawals_queue(spec, state, 1)
prepare_withdrawal_queue(spec, state, 1)

next_slot(spec, state)
execution_payload = build_empty_execution_payload(spec, state)
Expand All @@ -185,7 +185,7 @@ def test_fail_incorrect_dequeue_index(spec, state):
@with_capella_and_later
@spec_state_test
def test_fail_incorrect_dequeue_address(spec, state):
prepare_withdrawals_queue(spec, state, 1)
prepare_withdrawal_queue(spec, state, 1)

next_slot(spec, state)
execution_payload = build_empty_execution_payload(spec, state)
Expand All @@ -197,7 +197,7 @@ def test_fail_incorrect_dequeue_address(spec, state):
@with_capella_and_later
@spec_state_test
def test_fail_incorrect_dequeue_amount(spec, state):
prepare_withdrawals_queue(spec, state, 1)
prepare_withdrawal_queue(spec, state, 1)

next_slot(spec, state)
execution_payload = build_empty_execution_payload(spec, state)
Expand All @@ -209,7 +209,7 @@ def test_fail_incorrect_dequeue_amount(spec, state):
@with_capella_and_later
@spec_state_test
def test_fail_one_of_many_dequeued_incorrectly(spec, state):
prepare_withdrawals_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
prepare_withdrawal_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)

next_slot(spec, state)
execution_payload = build_empty_execution_payload(spec, state)
Expand All @@ -227,7 +227,7 @@ def test_fail_one_of_many_dequeued_incorrectly(spec, state):
@with_capella_and_later
@spec_state_test
def test_fail_many_dequeued_incorrectly(spec, state):
prepare_withdrawals_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
prepare_withdrawal_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)

next_slot(spec, state)
execution_payload = build_empty_execution_payload(spec, state)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ def set_validator_withdrawable(spec, state, index, withdrawable_epoch=None):


def run_process_full_withdrawals(spec, state, num_expected_withdrawals=None):
pre_withdrawal_index = state.withdrawal_index
pre_withdrawals_queue = state.withdrawals_queue
pre_next_withdrawal_index = state.next_withdrawal_index
pre_withdrawal_queue = state.withdrawal_queue
to_be_withdrawn_indices = [
index for index, validator in enumerate(state.validators)
if spec.is_fully_withdrawable_validator(validator, spec.get_current_epoch(state))
Expand All @@ -36,8 +36,8 @@ def run_process_full_withdrawals(spec, state, num_expected_withdrawals=None):
assert validator.fully_withdrawn_epoch == spec.get_current_epoch(state)
assert state.balances[index] == 0

assert len(state.withdrawals_queue) == len(pre_withdrawals_queue) + num_expected_withdrawals
assert state.withdrawal_index == pre_withdrawal_index + num_expected_withdrawals
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
Expand Down Expand Up @@ -67,10 +67,10 @@ def test_single_withdrawal(spec, state):
# Make one validator withdrawable
set_validator_withdrawable(spec, state, 0)

assert state.withdrawal_index == 0
assert state.next_withdrawal_index == 0
yield from run_process_full_withdrawals(spec, state, 1)

assert state.withdrawal_index == 1
assert state.next_withdrawal_index == 1


@with_capella_and_later
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
with_capella_and_later,
spec_state_test,
)
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with
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


def set_validator_partially_withdrawable(spec, state, index, rng=random.Random(666)):
Expand All @@ -16,8 +18,13 @@ def set_validator_partially_withdrawable(spec, state, index, rng=random.Random(6


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

partially_withdrawable_indices = [
index for index, validator in enumerate(state.validators)
if spec.is_partially_withdrawable_validator(validator, state.balances[index])
Expand All @@ -29,7 +36,9 @@ def run_process_partial_withdrawals(spec, state, num_expected_withdrawals=None):
else:
num_expected_withdrawals = num_partial_withdrawals

yield from run_epoch_processing_with(spec, state, 'process_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)
Expand Down Expand Up @@ -67,6 +76,8 @@ def test_success_one_partial_withdrawable_not_yet_active(spec, state):
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)


Expand All @@ -77,6 +88,9 @@ def test_success_one_partial_withdrawable_in_exit_queue(spec, state):
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)


Expand All @@ -87,16 +101,33 @@ def test_success_one_partial_withdrawable_exited(spec, state):
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_slashed(spec, state):
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)


Expand Down Expand Up @@ -136,6 +167,10 @@ def test_success_max_plus_one_withdrawable(spec, state):


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)

Expand Down Expand Up @@ -176,3 +211,9 @@ def test_random_3(spec, state):
@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))
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def get_process_calls(spec):
),
'process_sync_committee_updates', # altair
'process_full_withdrawals', # capella
'process_partial_withdrawals', # capella
# TODO: add sharding processing functions when spec stabilizes.
]

Expand Down
4 changes: 2 additions & 2 deletions tests/core/pyspec/eth2spec/test/helpers/execution_payload.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ def build_empty_execution_payload(spec, state, randao_mix=None):
transactions=empty_txs,
)
if spec.fork not in FORKS_BEFORE_CAPELLA:
num_withdrawals = min(spec.MAX_WITHDRAWALS_PER_PAYLOAD, len(state.withdrawals_queue))
payload.withdrawals = state.withdrawals_queue[:num_withdrawals]
num_withdrawals = min(spec.MAX_WITHDRAWALS_PER_PAYLOAD, len(state.withdrawal_queue))
payload.withdrawals = state.withdrawal_queue[:num_withdrawals]

# TODO: real RLP + block hash logic would be nice, requires RLP and keccak256 dependency however.
payload.block_hash = spec.Hash32(spec.hash(payload.hash_tree_root() + b"FAKE RLP HASH"))
Expand Down

0 comments on commit 5595953

Please sign in to comment.