From 491f14c76f376fe242669a90681943b5a3c484d0 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 19 Sep 2022 11:27:09 -0600 Subject: [PATCH] some capella sanity tests --- presets/minimal/capella.yaml | 2 +- .../test_process_bls_to_execution_change.py | 29 +--- .../test_process_partial_withdrawals.py | 20 +-- .../test/capella/sanity/test_blocks.py | 135 ++++++++++++++++++ .../test/helpers/bls_to_execution_changes.py | 27 ++++ .../eth2spec/test/helpers/withdrawals.py | 14 ++ 6 files changed, 184 insertions(+), 43 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py create mode 100644 tests/core/pyspec/eth2spec/test/helpers/bls_to_execution_changes.py diff --git a/presets/minimal/capella.yaml b/presets/minimal/capella.yaml index bf78685cd..0476172a1 100644 --- a/presets/minimal/capella.yaml +++ b/presets/minimal/capella.yaml @@ -21,4 +21,4 @@ MAX_BLS_TO_EXECUTION_CHANGES: 16 # Execution # --------------------------------------------------------------- # [customized] Lower than MAX_PARTIAL_WITHDRAWALS_PER_EPOCH so not all processed in one block -MAX_WITHDRAWALS_PER_PAYLOAD: 16 +MAX_WITHDRAWALS_PER_PAYLOAD: 8 diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py index bbac7fd36..8ff02489c 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py @@ -1,5 +1,5 @@ -from eth2spec.utils import bls -from eth2spec.test.helpers.keys import pubkeys, privkeys, pubkey_to_privkey +from eth2spec.test.helpers.keys import pubkeys +from eth2spec.test.helpers.bls_to_execution_changes import get_signed_address_change from eth2spec.test.context import spec_state_test, expect_assertion_error, with_capella_and_later, always_bls @@ -37,31 +37,6 @@ def run_bls_to_execution_change_processing(spec, state, signed_address_change, v yield 'post', state -def get_signed_address_change(spec, state, validator_index=None, withdrawal_pubkey=None): - if validator_index is None: - validator_index = 0 - - if withdrawal_pubkey is None: - key_index = -1 - validator_index - withdrawal_pubkey = pubkeys[key_index] - withdrawal_privkey = privkeys[key_index] - else: - withdrawal_privkey = pubkey_to_privkey[withdrawal_pubkey] - - domain = spec.get_domain(state, spec.DOMAIN_BLS_TO_EXECUTION_CHANGE) - address_change = spec.BLSToExecutionChange( - validator_index=validator_index, - from_bls_pubkey=withdrawal_pubkey, - to_execution_address=b'\x42' * 20, - ) - - signing_root = spec.compute_signing_root(address_change, domain) - return spec.SignedBLSToExecutionChange( - message=address_change, - signature=bls.Sign(withdrawal_privkey, signing_root), - ) - - @with_capella_and_later @spec_state_test def test_success(spec, state): diff --git a/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_partial_withdrawals.py b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_partial_withdrawals.py index 431c2f2d3..7569d2862 100644 --- a/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_partial_withdrawals.py +++ b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_partial_withdrawals.py @@ -8,20 +8,10 @@ from eth2spec.test.context import ( 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_eth1_withdrawal_credential_with_balance(spec, state, index, balance): - validator = state.validators[index] - validator.withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:] - validator.effective_balance = min(balance, spec.MAX_EFFECTIVE_BALANCE) - state.balances[index] = balance - - -def set_validator_partially_withdrawable(spec, state, index, rng=random.Random(666)): - balance = spec.MAX_EFFECTIVE_BALANCE + rng.randint(1, 100000000) - set_eth1_withdrawal_credential_with_balance(spec, state, index, balance) - - assert spec.is_partially_withdrawable_validator(state.validators[index], state.balances[index]) +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): @@ -228,7 +218,7 @@ def run_random_partial_withdrawals_test(spec, state, rng): 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) + 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``, diff --git a/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py new file mode 100644 index 000000000..28c20a2cd --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py @@ -0,0 +1,135 @@ +from eth2spec.test.context import ( + with_capella_and_later, spec_state_test +) + +from eth2spec.test.helpers.state import ( + state_transition_and_sign_block, +) +from eth2spec.test.helpers.block import ( + build_empty_block_for_next_slot, build_empty_block, +) +from eth2spec.test.helpers.bls_to_execution_changes import get_signed_address_change +from eth2spec.test.helpers.withdrawals import ( + set_validator_fully_withdrawable, + set_validator_partially_withdrawable, +) +from eth2spec.test.helpers.voluntary_exits import prepare_signed_exits + + +@with_capella_and_later +@spec_state_test +def test_successful_bls_change(spec, state): + index = 0 + signed_address_change = get_signed_address_change(spec, state, validator_index=index) + pre_credentials = state.validators[index].withdrawal_credentials + yield 'pre', state + + block = build_empty_block_for_next_slot(spec, state) + block.body.bls_to_execution_changes.append(signed_address_change) + + signed_block = state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [signed_block] + yield 'post', state + + post_credentials = state.validators[index].withdrawal_credentials + assert pre_credentials != post_credentials + assert post_credentials[:1] == spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + assert post_credentials[1:12] == b'\x00' * 11 + assert post_credentials[12:] == signed_address_change.message.to_execution_address + + +@with_capella_and_later +@spec_state_test +def test_full_withdrawal_in_epoch_transition(spec, state): + index = 0 + current_epoch = spec.get_current_epoch(state) + set_validator_fully_withdrawable(spec, state, index, current_epoch) + yield 'pre', state + + # trigger epoch transition + block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH) + signed_block = state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [signed_block] + yield 'post', state + + assert state.balances[index] == 0 + + +@with_capella_and_later +@spec_state_test +def test_partial_withdrawal_in_epoch_transition(spec, state): + index = state.next_withdrawal_index + set_validator_partially_withdrawable(spec, state, index, excess_balance=1000000000000) + pre_balance = state.balances[index] + pre_withdrawal_queue_len = len(state.withdrawal_queue) + + yield 'pre', state + + # trigger epoch transition + block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH) + signed_block = state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [signed_block] + yield 'post', state + + assert state.balances[index] < pre_balance + # Potentially less than due to sync committee penalty + assert state.balances[index] <= spec.MAX_EFFECTIVE_BALANCE + # Withdrawal is processed within the context of the block so queue empty + assert len(state.withdrawal_queue) == pre_withdrawal_queue_len + + +@with_capella_and_later +@spec_state_test +def test_many_partial_withdrawals_in_epoch_transition(spec, state): + assert len(state.validators) > spec.MAX_WITHDRAWALS_PER_PAYLOAD + assert spec.MAX_PARTIAL_WITHDRAWALS_PER_EPOCH > spec.MAX_WITHDRAWALS_PER_PAYLOAD + + for i in range(spec.MAX_WITHDRAWALS_PER_PAYLOAD + 1): + index = (i + state.next_withdrawal_index) % len(state.validators) + set_validator_partially_withdrawable(spec, state, index, excess_balance=1000000000000) + + pre_withdrawal_queue_len = len(state.withdrawal_queue) + + yield 'pre', state + + # trigger epoch transition + block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH) + signed_block = state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [signed_block] + yield 'post', state + + # All new partial withdrawals processed except 1 + assert len(state.withdrawal_queue) == pre_withdrawal_queue_len + 1 + + +@with_capella_and_later +@spec_state_test +def test_exit_and_bls_change(spec, state): + # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit + state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH + + index = 0 + signed_address_change = get_signed_address_change(spec, state, validator_index=index) + signed_exit = prepare_signed_exits(spec, state, [index])[0] + + yield 'pre', state + + block = build_empty_block_for_next_slot(spec, state) + block.body.voluntary_exits.append(signed_exit) + block.body.bls_to_execution_changes.append(signed_address_change) + + signed_block = state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [signed_block] + yield 'post', state + + validator = state.validators[index] + balance = state.balances[index] + current_epoch = spec.get_current_epoch(state) + assert not spec.is_fully_withdrawable_validator(validator, balance, current_epoch) + assert validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH + assert spec.is_fully_withdrawable_validator(validator, balance, validator.withdrawable_epoch) diff --git a/tests/core/pyspec/eth2spec/test/helpers/bls_to_execution_changes.py b/tests/core/pyspec/eth2spec/test/helpers/bls_to_execution_changes.py new file mode 100644 index 000000000..61c84b515 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/helpers/bls_to_execution_changes.py @@ -0,0 +1,27 @@ +from eth2spec.utils import bls +from eth2spec.test.helpers.keys import pubkeys, privkeys, pubkey_to_privkey + + +def get_signed_address_change(spec, state, validator_index=None, withdrawal_pubkey=None): + if validator_index is None: + validator_index = 0 + + if withdrawal_pubkey is None: + key_index = -1 - validator_index + withdrawal_pubkey = pubkeys[key_index] + withdrawal_privkey = privkeys[key_index] + else: + withdrawal_privkey = pubkey_to_privkey[withdrawal_pubkey] + + domain = spec.get_domain(state, spec.DOMAIN_BLS_TO_EXECUTION_CHANGE) + address_change = spec.BLSToExecutionChange( + validator_index=validator_index, + from_bls_pubkey=withdrawal_pubkey, + to_execution_address=b'\x42' * 20, + ) + + signing_root = spec.compute_signing_root(address_change, domain) + return spec.SignedBLSToExecutionChange( + message=address_change, + signature=bls.Sign(withdrawal_privkey, signing_root), + ) diff --git a/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py b/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py index 739f5eb06..526ac0caa 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py +++ b/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py @@ -15,3 +15,17 @@ def set_validator_fully_withdrawable(spec, state, index, withdrawable_epoch=None state.balances[index] = 10000000000 assert spec.is_fully_withdrawable_validator(validator, state.balances[index], withdrawable_epoch) + + +def set_eth1_withdrawal_credential_with_balance(spec, state, index, balance): + validator = state.validators[index] + validator.withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:] + validator.effective_balance = min(balance, spec.MAX_EFFECTIVE_BALANCE) + state.balances[index] = balance + + +def set_validator_partially_withdrawable(spec, state, index, excess_balance=1000000000): + set_eth1_withdrawal_credential_with_balance(spec, state, index, spec.MAX_EFFECTIVE_BALANCE + excess_balance) + validator = state.validators[index] + + assert spec.is_partially_withdrawable_validator(validator, state.balances[index])