diff --git a/tests/core/pyspec/eth2spec/test/electra/fork_choice/__init__.py b/tests/core/pyspec/eth2spec/test/electra/fork_choice/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/core/pyspec/eth2spec/test/electra/fork_choice/test_deposit_with_reorg.py b/tests/core/pyspec/eth2spec/test/electra/fork_choice/test_deposit_with_reorg.py new file mode 100644 index 0000000000..74c418ad60 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/electra/fork_choice/test_deposit_with_reorg.py @@ -0,0 +1,94 @@ +from eth2spec.test.helpers.block import ( + build_empty_block_for_next_slot, +) +from eth2spec.test.context import ( + with_presets, + spec_state_test, + with_electra_and_later, +) +from eth2spec.test.helpers.execution_payload import ( + compute_el_block_hash_for_block, +) +from eth2spec.test.helpers.state import ( + state_transition_and_sign_block, + next_slot, +) +from eth2spec.test.helpers.deposits import ( + prepare_deposit_request, +) +from eth2spec.test.helpers.fork_choice import ( + get_genesis_forkchoice_store_and_block, + tick_and_add_block, + apply_next_slots_with_attestations, +) +from eth2spec.test.helpers.constants import ( + MINIMAL, +) + + +@with_electra_and_later +@spec_state_test +@with_presets([MINIMAL], reason="too slow") +def test_new_validator_deposit_with_multiple_epoch_transitions(spec, state): + # signify the eth1 bridge deprecation + state.deposit_requests_start_index = state.eth1_deposit_index + + # yield anchor state and block + store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) + yield 'anchor_state', state + yield 'anchor_block', anchor_block + + test_steps = [] + + # (1) create deposit request for a new validator + deposit_request = prepare_deposit_request( + spec, len(state.validators), spec.MIN_ACTIVATION_BALANCE, signed=True) + deposit_block = build_empty_block_for_next_slot(spec, state) + deposit_block.body.execution_requests.deposits = [deposit_request] + deposit_block.body.execution_payload.block_hash = compute_el_block_hash_for_block(spec, deposit_block) + signed_deposit_block = state_transition_and_sign_block(spec, state, deposit_block) + + pending_deposit = spec.PendingDeposit( + pubkey=deposit_request.pubkey, + withdrawal_credentials=deposit_request.withdrawal_credentials, + amount=deposit_request.amount, + signature=deposit_request.signature, + slot=deposit_block.slot + ) + + assert state.pending_deposits == [pending_deposit] + + yield from tick_and_add_block(spec, store, signed_deposit_block, test_steps) + + # (2) finalize and process pending deposit on one fork + slots = 4 * spec.SLOTS_PER_EPOCH - state.slot + post_state, _, latest_block = yield from apply_next_slots_with_attestations( + spec, state, store, slots, True, True, test_steps) + + # check new validator has been created + assert post_state.pending_deposits == [] + new_validator = post_state.validators[len(post_state.validators) - 1] + assert new_validator.pubkey == pending_deposit.pubkey + assert new_validator.withdrawal_credentials == pending_deposit.withdrawal_credentials + + # (3) create a conflicting block that triggers deposit processing on another fork + prev_epoch_ancestor = store.blocks[latest_block.message.parent_root] + # important to skip last block of the epoch to make client do the epoch processing + # otherwise, client can read the post-epoch from cache + prev_epoch_ancestor = store.blocks[prev_epoch_ancestor.parent_root] + another_fork_state = store.block_states[prev_epoch_ancestor.hash_tree_root()].copy() + + assert another_fork_state.pending_deposits == [pending_deposit] + + # skip a slot to create and process a fork block + next_slot(spec, another_fork_state) + post_state, _, _ = yield from apply_next_slots_with_attestations( + spec, another_fork_state, store, 1, True, True, test_steps) + + # check new validator has been created on another fork + assert post_state.pending_deposits == [] + new_validator = post_state.validators[len(post_state.validators) - 1] + assert new_validator.pubkey == pending_deposit.pubkey + assert new_validator.withdrawal_credentials == pending_deposit.withdrawal_credentials + + yield 'steps', test_steps diff --git a/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/test_blocks.py b/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/test_blocks.py index 2640e1aec7..bad569b4b7 100644 --- a/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/test_blocks.py @@ -1,5 +1,5 @@ from eth2spec.test.helpers.block import ( - build_empty_block_for_next_slot + build_empty_block_for_next_slot, ) from eth2spec.test.context import ( spec_state_test, @@ -21,6 +21,9 @@ set_eth1_withdrawal_credential_with_balance, set_compounding_withdrawal_credential_with_balance, ) +from eth2spec.test.helpers.deposits import ( + prepare_deposit_request, +) @with_electra_and_later @@ -327,3 +330,43 @@ def test_withdrawal_and_switch_to_compounding_request_same_validator(spec, state assert spec.is_compounding_withdrawal_credential(state.validators[validator_index].withdrawal_credentials) # Ensure there was no excess balance pending deposit assert len(state.pending_deposits) == 0 + + +@with_electra_and_later +@spec_state_test +def test_deposit_request_with_same_pubkey_different_withdrawal_credentials(spec, state): + # signify the eth1 bridge deprecation + state.deposit_requests_start_index = state.eth1_deposit_index + + # prepare three deposit requests, where + # 1st and 3rd have the same pubkey but different withdrawal credentials + deposit_request_0 = prepare_deposit_request( + spec, len(state.validators), spec.MIN_ACTIVATION_BALANCE, state.eth1_deposit_index, signed=True) + deposit_request_1 = prepare_deposit_request( + spec, len(state.validators) + 1, spec.MIN_ACTIVATION_BALANCE, state.eth1_deposit_index + 1, signed=True) + deposit_request_2 = prepare_deposit_request( + spec, len(state.validators), spec.MIN_ACTIVATION_BALANCE, state.eth1_deposit_index + 2, signed=True, + withdrawal_credentials=(spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + b'\x00' * 11 + b'\x11' * 20) + ) + + # build a block with deposit requests + block = build_empty_block_for_next_slot(spec, state) + block.body.execution_requests.deposits = [deposit_request_0, deposit_request_1, deposit_request_2] + block.body.execution_payload.block_hash = compute_el_block_hash_for_block(spec, block) + + yield 'pre', state + + signed_block = state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [signed_block] + yield 'post', state + + # check deposit requests are processed correctly + for i, deposit_request in enumerate(block.body.execution_requests.deposits): + assert state.pending_deposits[i] == spec.PendingDeposit( + pubkey=deposit_request.pubkey, + withdrawal_credentials=deposit_request.withdrawal_credentials, + amount=deposit_request.amount, + signature=deposit_request.signature, + slot=signed_block.message.slot, + ) diff --git a/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/test_deposit_transition.py b/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/test_deposit_transition.py index f2d9a6f11a..b021eebe05 100644 --- a/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/test_deposit_transition.py +++ b/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/test_deposit_transition.py @@ -93,8 +93,8 @@ def prepare_state_and_block(spec, deposit_data = build_deposit_data(spec, pubkeys[keypair_index], privkeys[keypair_index], - # use max effective balance - spec.MAX_EFFECTIVE_BALANCE, + # use min activation balance + spec.MIN_ACTIVATION_BALANCE, # insecurely use pubkey as withdrawal key spec.BLS_WITHDRAWAL_PREFIX + spec.hash(pubkeys[keypair_index])[1:], signed=True) @@ -118,8 +118,8 @@ def prepare_state_and_block(spec, for offset in range(deposit_request_cnt): deposit_request = prepare_deposit_request(spec, keypair_index, - # use max effective balance - spec.MAX_EFFECTIVE_BALANCE, + # use min activation balance + spec.MIN_ACTIVATION_BALANCE, first_deposit_request_index + offset, signed=True) deposit_requests.append(deposit_request) diff --git a/tests/generators/fork_choice/main.py b/tests/generators/fork_choice/main.py index 6aa581460a..37a1d66f06 100644 --- a/tests/generators/fork_choice/main.py +++ b/tests/generators/fork_choice/main.py @@ -28,7 +28,10 @@ ]} deneb_mods = combine_mods(_new_deneb_mods, capella_mods) - electra_mods = deneb_mods # No additional Electra specific fork choice tests + _new_electra_mods = {key: 'eth2spec.test.electra.fork_choice.test_' + key for key in [ + 'deposit_with_reorg', + ]} + electra_mods = combine_mods(_new_electra_mods, deneb_mods) # Fulu adds new `is_data_available` tests _new_fulu_mods = {key: 'eth2spec.test.fulu.fork_choice.test_' + key for key in [