diff --git a/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py index 3c406e524..229d8ecc5 100644 --- a/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py @@ -21,9 +21,13 @@ from eth2spec.test.helpers.withdrawals import ( from eth2spec.test.helpers.voluntary_exits import prepare_signed_exits +# +# BLSToExecutionChange +# + @with_phases([CAPELLA]) @spec_state_test -def test_successful_bls_change(spec, state): +def test_success_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 @@ -44,6 +48,82 @@ def test_successful_bls_change(spec, state): assert post_credentials[12:] == signed_address_change.message.to_execution_address +@with_phases([CAPELLA]) +@spec_state_test +def test_success_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) + + +@with_phases([CAPELLA]) +@spec_state_test +def test_invalid_duplicate_bls_changes_same_block(spec, state): + index = 0 + signed_address_change = get_signed_address_change(spec, state, validator_index=index) + yield 'pre', state + + block = build_empty_block_for_next_slot(spec, state) + + # Double BLSToExecutionChange of the same validator + for _ in range(2): + block.body.bls_to_execution_changes.append(signed_address_change) + + signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True) + + yield 'blocks', [signed_block] + yield 'post', None + + +@with_phases([CAPELLA]) +@spec_state_test +def test_invalid_two_bls_changes_of_different_addresses_same_validator_same_block(spec, state): + index = 0 + + signed_address_change_1 = get_signed_address_change(spec, state, validator_index=index, + to_execution_address=b'\x12' * 20) + signed_address_change_2 = get_signed_address_change(spec, state, validator_index=index, + to_execution_address=b'\x34' * 20) + assert signed_address_change_1 != signed_address_change_2 + + yield 'pre', state + + block = build_empty_block_for_next_slot(spec, state) + + block.body.bls_to_execution_changes.append(signed_address_change_1) + block.body.bls_to_execution_changes.append(signed_address_change_2) + + signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True) + + yield 'blocks', [signed_block] + yield 'post', None + + +# +# Withdrawals +# + @with_phases([CAPELLA]) @spec_state_test def test_full_withdrawal_in_epoch_transition(spec, state): @@ -112,35 +192,6 @@ def test_many_partial_withdrawals_in_epoch_transition(spec, state): assert len(spec.get_expected_withdrawals(state)) == 1 -@with_phases([CAPELLA]) -@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) - - def _perform_valid_withdrawal(spec, state): fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals( spec, state, num_partial_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4, 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 index 61c84b515..446fa9710 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/bls_to_execution_changes.py +++ b/tests/core/pyspec/eth2spec/test/helpers/bls_to_execution_changes.py @@ -2,7 +2,7 @@ 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): +def get_signed_address_change(spec, state, validator_index=None, withdrawal_pubkey=None, to_execution_address=None): if validator_index is None: validator_index = 0 @@ -13,11 +13,14 @@ def get_signed_address_change(spec, state, validator_index=None, withdrawal_pubk else: withdrawal_privkey = pubkey_to_privkey[withdrawal_pubkey] + if to_execution_address is None: + to_execution_address = b'\x42' * 20 + 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, + to_execution_address=to_execution_address, ) signing_root = spec.compute_signing_root(address_change, domain) diff --git a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py index b94a01f7f..0e19e65b7 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py @@ -433,7 +433,7 @@ def test_proposer_slashing(spec, state): @with_all_phases @spec_state_test -def test_double_same_proposer_slashings_same_block(spec, state): +def test_invalid_duplicate_proposer_slashings_same_block(spec, state): proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=True) slashed_index = proposer_slashing.signed_header_1.message.proposer_index assert not state.validators[slashed_index].slashed @@ -450,7 +450,7 @@ def test_double_same_proposer_slashings_same_block(spec, state): @with_all_phases @spec_state_test -def test_double_similar_proposer_slashings_same_block(spec, state): +def test_invalid_similar_proposer_slashings_same_block(spec, state): slashed_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1] # Same validator, but different slashable offences in the same block @@ -549,7 +549,7 @@ def test_attester_slashing(spec, state): @with_all_phases @spec_state_test -def test_duplicate_attester_slashing(spec, state): +def test_invalid_duplicate_attester_slashing_same_block(spec, state): if spec.MAX_ATTESTER_SLASHINGS < 2: return dump_skipping_message("Skip test if config cannot handle multiple AttesterSlashings per block") @@ -744,6 +744,27 @@ def test_deposit_in_block(spec, state): assert state.validators[validator_index].pubkey == pubkeys[validator_index] +@with_all_phases +@spec_state_test +def test_invalid_duplicate_deposit_same_block(spec, state): + validator_index = len(state.validators) + amount = spec.MAX_EFFECTIVE_BALANCE + deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True) + + yield 'pre', state + + block = build_empty_block_for_next_slot(spec, state) + + # The same deposit of the same validator + for _ in range(2): + block.body.deposits.append(deposit) + + signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True) + + yield 'blocks', [signed_block] + yield 'post', None + + @with_all_phases @spec_state_test def test_deposit_top_up(spec, state): @@ -831,6 +852,49 @@ def test_attestation(spec, state): assert spec.hash_tree_root(state.previous_epoch_participation) == pre_current_epoch_participation_root +@with_all_phases +@spec_state_test +def test_duplicate_attestation_same_block(spec, state): + next_epoch(spec, state) + + yield 'pre', state + + attestation_block = build_empty_block(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY) + + index = 0 + + attestation = get_valid_attestation(spec, state, index=index, signed=True) + + if not is_post_altair(spec): + pre_current_attestations_len = len(state.current_epoch_attestations) + + # Add to state via block transition + for _ in range(2): + attestation_block.body.attestations.append(attestation) + signed_attestation_block = state_transition_and_sign_block(spec, state, attestation_block) + + if not is_post_altair(spec): + assert len(state.current_epoch_attestations) == pre_current_attestations_len + 2 + # Epoch transition should move to previous_epoch_attestations + pre_current_attestations_root = spec.hash_tree_root(state.current_epoch_attestations) + else: + pre_current_epoch_participation_root = spec.hash_tree_root(state.current_epoch_participation) + + epoch_block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH) + signed_epoch_block = state_transition_and_sign_block(spec, state, epoch_block) + + yield 'blocks', [signed_attestation_block, signed_epoch_block] + yield 'post', state + + if not is_post_altair(spec): + assert len(state.current_epoch_attestations) == 0 + assert spec.hash_tree_root(state.previous_epoch_attestations) == pre_current_attestations_root + else: + for index in range(len(state.validators)): + assert state.current_epoch_participation[index] == spec.ParticipationFlags(0b0000_0000) + assert spec.hash_tree_root(state.previous_epoch_participation) == pre_current_epoch_participation_root + + # After SHARDING is enabled, a committee is computed for SHARD_COMMITTEE_PERIOD slots ago, # exceeding the minimal-config randao mixes memory size. # Applies to all voluntary-exit sanity block tests. @@ -866,7 +930,7 @@ def test_voluntary_exit(spec, state): @with_all_phases @spec_state_test -def test_double_validator_exit_same_block(spec, state): +def test_invalid_duplicate_validator_exit_same_block(spec, state): validator_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1] # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit