From 34693f2db6adcfddb4d988816502b9ea3751f078 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 10 May 2023 01:06:51 +0800 Subject: [PATCH] Add basic tests --- tests/core/pyspec/eth2spec/test/context.py | 6 ++- .../pyspec/eth2spec/test/eip7002/__init__.py | 0 .../test/eip7002/block_processing/__init__.py | 0 .../test_process_execution_layer_exit.py | 40 ++++++++++++++++ .../eth2spec/test/eip7002/sanity/__init__.py | 0 .../test/eip7002/sanity/test_blocks.py | 46 +++++++++++++++++++ .../pyspec/eth2spec/test/helpers/constants.py | 2 + .../test/helpers/execution_layer_exits.py | 37 +++++++++++++++ .../test/helpers/execution_payload.py | 32 +++++++++++-- .../pyspec/eth2spec/test/helpers/forks.py | 8 +++- .../pyspec/eth2spec/test/helpers/genesis.py | 6 ++- .../eth2spec/test/helpers/withdrawals.py | 9 +++- 12 files changed, 178 insertions(+), 8 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/eip7002/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/eip7002/block_processing/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/eip7002/block_processing/test_process_execution_layer_exit.py create mode 100644 tests/core/pyspec/eth2spec/test/eip7002/sanity/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/eip7002/sanity/test_blocks.py create mode 100644 tests/core/pyspec/eth2spec/test/helpers/execution_layer_exits.py diff --git a/tests/core/pyspec/eth2spec/test/context.py b/tests/core/pyspec/eth2spec/test/context.py index 901fd273a..18d24c1cc 100644 --- a/tests/core/pyspec/eth2spec/test/context.py +++ b/tests/core/pyspec/eth2spec/test/context.py @@ -9,12 +9,13 @@ from eth2spec.bellatrix import mainnet as spec_bellatrix_mainnet, minimal as spe from eth2spec.capella import mainnet as spec_capella_mainnet, minimal as spec_capella_minimal from eth2spec.deneb import mainnet as spec_deneb_mainnet, minimal as spec_deneb_minimal from eth2spec.eip6110 import mainnet as spec_eip6110_mainnet, minimal as spec_eip6110_minimal +from eth2spec.eip7002 import mainnet as spec_eip7002_mainnet, minimal as spec_eip7002_minimal from eth2spec.utils import bls from .exceptions import SkippedTest from .helpers.constants import ( PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, - EIP6110, + EIP6110, EIP7002, MINIMAL, MAINNET, ALL_PHASES, ALL_FORK_UPGRADES, @@ -82,6 +83,7 @@ spec_targets: Dict[PresetBaseName, Dict[SpecForkName, Spec]] = { CAPELLA: spec_capella_minimal, DENEB: spec_deneb_minimal, EIP6110: spec_eip6110_minimal, + EIP7002: spec_eip7002_minimal, }, MAINNET: { PHASE0: spec_phase0_mainnet, @@ -90,6 +92,7 @@ spec_targets: Dict[PresetBaseName, Dict[SpecForkName, Spec]] = { CAPELLA: spec_capella_mainnet, DENEB: spec_deneb_mainnet, EIP6110: spec_eip6110_mainnet, + EIP7002: spec_eip7002_mainnet, }, } @@ -433,6 +436,7 @@ with_bellatrix_and_later = with_all_phases_from(BELLATRIX) with_capella_and_later = with_all_phases_from(CAPELLA) with_deneb_and_later = with_all_phases_from(DENEB) with_eip6110_and_later = with_all_phases_from(EIP6110) +with_eip7002_and_later = with_all_phases_from(EIP7002) def _get_preset_targets(kw): diff --git a/tests/core/pyspec/eth2spec/test/eip7002/__init__.py b/tests/core/pyspec/eth2spec/test/eip7002/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/eip7002/block_processing/__init__.py b/tests/core/pyspec/eth2spec/test/eip7002/block_processing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/eip7002/block_processing/test_process_execution_layer_exit.py b/tests/core/pyspec/eth2spec/test/eip7002/block_processing/test_process_execution_layer_exit.py new file mode 100644 index 000000000..74f05dd5f --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/eip7002/block_processing/test_process_execution_layer_exit.py @@ -0,0 +1,40 @@ +from eth2spec.test.context import spec_state_test, with_eip7002_and_later +from eth2spec.test.helpers.execution_layer_exits import run_execution_layer_exit_processing +from eth2spec.test.helpers.withdrawals import set_eth1_withdrawal_credential_with_balance + + +@with_eip7002_and_later +@spec_state_test +def test_basic_exit(spec, state): + # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit + state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH + + current_epoch = spec.get_current_epoch(state) + validator_index = spec.get_active_validator_indices(state, current_epoch)[0] + address = b'\x22' * 20 + set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, address=address) + execution_layer_exit = spec.ExecutionLayerExit( + source_address=address, + validator_index=validator_index, + ) + + yield from run_execution_layer_exit_processing(spec, state, execution_layer_exit) + + +@with_eip7002_and_later +@spec_state_test +def test_incorrect_source_address(spec, state): + # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit + state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH + + current_epoch = spec.get_current_epoch(state) + validator_index = spec.get_active_validator_indices(state, current_epoch)[0] + address = b'\x22' * 20 + incorrect_address = b'\x33' * 20 + set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, address=address) + execution_layer_exit = spec.ExecutionLayerExit( + source_address=incorrect_address, + validator_index=validator_index, + ) + + yield from run_execution_layer_exit_processing(spec, state, execution_layer_exit, success=False) diff --git a/tests/core/pyspec/eth2spec/test/eip7002/sanity/__init__.py b/tests/core/pyspec/eth2spec/test/eip7002/sanity/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/eip7002/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/eip7002/sanity/test_blocks.py new file mode 100644 index 000000000..6d0b753f0 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/eip7002/sanity/test_blocks.py @@ -0,0 +1,46 @@ +from eth2spec.test.helpers.block import ( + build_empty_block_for_next_slot +) +from eth2spec.test.context import ( + spec_state_test, + with_eip7002_and_later, +) +from eth2spec.test.helpers.execution_payload import ( + compute_el_block_hash, +) +from eth2spec.test.helpers.state import ( + state_transition_and_sign_block +) +from eth2spec.test.helpers.withdrawals import ( + set_eth1_withdrawal_credential_with_balance, +) + + +@with_eip7002_and_later +@spec_state_test +def test_basic_exit(spec, state): + # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit + state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH + + current_epoch = spec.get_current_epoch(state) + validator_index = spec.get_active_validator_indices(state, current_epoch)[0] + address = b'\x22' * 20 + set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, address=address) + execution_layer_exit = spec.ExecutionLayerExit( + source_address=address, + validator_index=validator_index, + ) + + yield 'pre', state + + assert state.validators[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH + + block = build_empty_block_for_next_slot(spec, state) + block.body.execution_payload.exits = [execution_layer_exit] + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) + signed_block = state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [signed_block] + yield 'post', state + + assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH diff --git a/tests/core/pyspec/eth2spec/test/helpers/constants.py b/tests/core/pyspec/eth2spec/test/helpers/constants.py index 2140c96e4..f41e53305 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/constants.py +++ b/tests/core/pyspec/eth2spec/test/helpers/constants.py @@ -16,6 +16,7 @@ SHARDING = SpecForkName('sharding') CUSTODY_GAME = SpecForkName('custody_game') DAS = SpecForkName('das') EIP6110 = SpecForkName('eip6110') +EIP7002 = SpecForkName('eip7002') # The forks that pytest can run with. ALL_PHASES = ( @@ -23,6 +24,7 @@ ALL_PHASES = ( PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, # Experimental patches EIP6110, + EIP7002, ) # The forks that output to the test vectors. TESTGEN_FORKS = (PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110) diff --git a/tests/core/pyspec/eth2spec/test/helpers/execution_layer_exits.py b/tests/core/pyspec/eth2spec/test/helpers/execution_layer_exits.py new file mode 100644 index 000000000..f6f7328ae --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/helpers/execution_layer_exits.py @@ -0,0 +1,37 @@ +from eth2spec.test.context import expect_assertion_error + + +# +# Run processing +# + + +def run_execution_layer_exit_processing(spec, state, execution_layer_exit, valid=True, success=True): + """ + Run ``process_execution_layer_exit``, yielding: + - pre-state ('pre') + - execution_layer_exit ('execution_layer_exit') + - post-state ('post'). + If ``valid == False``, run expecting ``AssertionError`` + """ + validator_index = execution_layer_exit.validator_index + + yield 'pre', state + yield 'execution_layer_exit', execution_layer_exit + + if not valid: + expect_assertion_error(lambda: spec.process_execution_layer_exit(state, execution_layer_exit)) + yield 'post', None + return + + pre_exit_epoch = state.validators[validator_index].exit_epoch + + spec.process_execution_layer_exit(state, execution_layer_exit) + + yield 'post', state + + if success: + assert pre_exit_epoch == spec.FAR_FUTURE_EPOCH + assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH + else: + assert state.validators[validator_index].exit_epoch == pre_exit_epoch diff --git a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py index 747d678ef..31125dfc3 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py @@ -8,6 +8,7 @@ from eth2spec.test.helpers.forks import ( is_post_capella, is_post_deneb, is_post_eip6110, + is_post_eip7002, ) @@ -34,6 +35,8 @@ def get_execution_payload_header(spec, execution_payload): payload_header.excess_data_gas = execution_payload.excess_data_gas if is_post_eip6110(spec): payload_header.deposit_receipts_root = spec.hash_tree_root(execution_payload.deposit_receipts) + if is_post_eip7002(spec): + payload_header.exits_root = spec.hash_tree_root(execution_payload.exits) return payload_header @@ -55,7 +58,8 @@ def compute_el_header_block_hash(spec, payload_header, transactions_trie_root, withdrawals_trie_root=None, - deposit_receipts_trie_root=None): + deposit_receipts_trie_root=None, + exits_trie_root=None): """ Computes the RLP execution block hash described by an `ExecutionPayloadHeader`. """ @@ -103,6 +107,9 @@ def compute_el_header_block_hash(spec, # deposit_receipts_root assert deposit_receipts_trie_root is not None execution_payload_header_rlp.append((Binary(32, 32), deposit_receipts_trie_root)) + if is_post_eip7002(spec): + # exits_trie_root + execution_payload_header_rlp.append((Binary(32, 32), exits_trie_root)) sedes = List([schema for schema, _ in execution_payload_header_rlp]) values = [value for _, value in execution_payload_header_rlp] @@ -112,7 +119,7 @@ def compute_el_header_block_hash(spec, # https://eips.ethereum.org/EIPS/eip-4895 -def get_withdrawal_rlp(spec, withdrawal): +def get_withdrawal_rlp(withdrawal): withdrawal_rlp = [ # index (big_endian_int, withdrawal.index), @@ -129,6 +136,20 @@ def get_withdrawal_rlp(spec, withdrawal): return encode(values, sedes) +# https://eips.ethereum.org/EIPS/eip-7002 +def get_exit_rlp(exit): + exit_rlp = [ + # source_address + (Binary(20, 20), exit.source_address), + # validator_index + (big_endian_int, exit.validator_index), + ] + + sedes = List([schema for schema, _ in exit_rlp]) + values = [value for _, value in exit_rlp] + return encode(values, sedes) + + def get_deposit_receipt_rlp(spec, deposit_receipt): deposit_receipt_rlp = [ # pubkey @@ -153,13 +174,17 @@ def compute_el_block_hash(spec, payload): withdrawals_trie_root = None deposit_receipts_trie_root = None + exits_trie_root = None if is_post_capella(spec): - withdrawals_encoded = [get_withdrawal_rlp(spec, withdrawal) for withdrawal in payload.withdrawals] + withdrawals_encoded = [get_withdrawal_rlp(withdrawal) for withdrawal in payload.withdrawals] withdrawals_trie_root = compute_trie_root_from_indexed_data(withdrawals_encoded) if is_post_eip6110(spec): deposit_receipts_encoded = [get_deposit_receipt_rlp(spec, receipt) for receipt in payload.deposit_receipts] deposit_receipts_trie_root = compute_trie_root_from_indexed_data(deposit_receipts_encoded) + if is_post_eip7002(spec): + exits_encoded = [get_exit_rlp(exit) for exit in payload.exits] + exits_trie_root = compute_trie_root_from_indexed_data(exits_encoded) payload_header = get_execution_payload_header(spec, payload) @@ -169,6 +194,7 @@ def compute_el_block_hash(spec, payload): transactions_trie_root, withdrawals_trie_root, deposit_receipts_trie_root, + exits_trie_root, ) diff --git a/tests/core/pyspec/eth2spec/test/helpers/forks.py b/tests/core/pyspec/eth2spec/test/helpers/forks.py index 5e97522db..2cca62aa6 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/forks.py +++ b/tests/core/pyspec/eth2spec/test/helpers/forks.py @@ -1,10 +1,12 @@ from .constants import ( PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, - EIP6110, + EIP6110, EIP7002, ) def is_post_fork(a, b): + if a == EIP7002: + return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP7002] if a == EIP6110: return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110] if a == DENEB: @@ -38,3 +40,7 @@ def is_post_deneb(spec): def is_post_eip6110(spec): return is_post_fork(spec.fork, EIP6110) + + +def is_post_eip7002(spec): + return is_post_fork(spec.fork, EIP7002) diff --git a/tests/core/pyspec/eth2spec/test/helpers/genesis.py b/tests/core/pyspec/eth2spec/test/helpers/genesis.py index fea259013..933f4318a 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/genesis.py +++ b/tests/core/pyspec/eth2spec/test/helpers/genesis.py @@ -5,7 +5,7 @@ from eth2spec.test.helpers.execution_payload import ( compute_el_header_block_hash, ) from eth2spec.test.helpers.forks import ( - is_post_altair, is_post_bellatrix, is_post_capella, is_post_eip6110, + is_post_altair, is_post_bellatrix, is_post_capella, is_post_eip6110, is_post_eip7002, ) from eth2spec.test.helpers.keys import pubkeys @@ -49,11 +49,14 @@ def get_sample_genesis_execution_payload_header(spec, transactions_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") withdrawals_trie_root = None deposit_receipts_trie_root = None + exits_trie_root = None if is_post_capella(spec): withdrawals_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") if is_post_eip6110(spec): deposit_receipts_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + if is_post_eip7002(spec): + exits_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") payload_header.block_hash = compute_el_header_block_hash( spec, @@ -61,6 +64,7 @@ def get_sample_genesis_execution_payload_header(spec, transactions_trie_root, withdrawals_trie_root, deposit_receipts_trie_root, + exits_trie_root, ) return payload_header diff --git a/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py b/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py index aebe49f26..9ebb05ba0 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py +++ b/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py @@ -20,9 +20,14 @@ def set_validator_fully_withdrawable(spec, state, index, withdrawable_epoch=None assert spec.is_fully_withdrawable_validator(validator, state.balances[index], withdrawable_epoch) -def set_eth1_withdrawal_credential_with_balance(spec, state, index, balance): +def set_eth1_withdrawal_credential_with_balance(spec, state, index, balance=None, address=None): + if balance is None: + balance = spec.MAX_EFFECTIVE_BALANCE + if address is None: + address = b'\x11' * 20 + validator = state.validators[index] - validator.withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:] + validator.withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + b'\x00' * 11 + address validator.effective_balance = min(balance, spec.MAX_EFFECTIVE_BALANCE) state.balances[index] = balance