diff --git a/.circleci/config.yml b/.circleci/config.yml index 6ec1195d44..6afea6a2be 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -181,6 +181,19 @@ jobs: command: make test fork=eip7441 - store_test_results: path: tests/core/pyspec/test-reports + test-eip7732: + docker: + - image: cimg/python:3.12-node + working_directory: ~/specs-repo + steps: + - restore_cache: + key: v3-specs-repo-{{ .Branch }}-{{ .Revision }} + - restore_pyspec_cached_venv + - run: + name: Run py-tests + command: make test fork=eip7732 + - store_test_results: + path: tests/core/pyspec/test-reports lint: docker: - image: cimg/python:3.12-node @@ -227,6 +240,9 @@ workflows: - test-eip7441: requires: - install_pyspec_test + - test-eip7732: + requires: + - install_pyspec_test - lint: requires: - install_pyspec_test diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 3995d0d4ae..c4e6e78043 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -61,7 +61,7 @@ jobs: needs: [lint] strategy: matrix: - version: ["phase0", "altair", "bellatrix", "capella", "deneb", "electra", "fulu", "eip7441"] + version: ["phase0", "altair", "bellatrix", "capella", "deneb", "electra", "fulu", "eip7441", "eip7732"] steps: - name: Checkout repository uses: actions/checkout@v4 diff --git a/presets/minimal/eip-7732.yaml b/presets/minimal/eip-7732.yaml index 751c1325a5..f865fd05cc 100644 --- a/presets/minimal/eip-7732.yaml +++ b/presets/minimal/eip-7732.yaml @@ -6,4 +6,4 @@ PTC_SIZE: 2 # 2**2 (= 4) MAX_PAYLOAD_ATTESTATIONS: 4 -KZG_COMMITMENT_INCLUSION_PROOF_DEPTH_EIP7732: 13 +KZG_COMMITMENT_INCLUSION_PROOF_DEPTH_EIP7732: 14 diff --git a/specs/_features/eip7732/beacon-chain.md b/specs/_features/eip7732/beacon-chain.md index 72e175ae4d..f3ec449b2e 100644 --- a/specs/_features/eip7732/beacon-chain.md +++ b/specs/_features/eip7732/beacon-chain.md @@ -57,6 +57,8 @@ - [Execution payload processing](#execution-payload-processing) - [New `verify_execution_payload_envelope_signature`](#new-verify_execution_payload_envelope_signature) - [New `process_execution_payload`](#new-process_execution_payload) +- [Testing](#testing) + - [Modified `is_merge_transition_complete`](#modified-is_merge_transition_complete-1) @@ -716,9 +718,9 @@ def process_execution_payload(state: BeaconState, for operation in operations: fn(state, operation) - for_ops(requests.deposit_requests, process_deposit_request) - for_ops(requests.withdrawal_requests, process_withdrawal_request) - for_ops(requests.consolidation_requests, process_consolidation_request) + for_ops(requests.deposits, process_deposit_request) + for_ops(requests.withdrawals, process_withdrawal_request) + for_ops(requests.consolidations, process_consolidation_request) # Cache the execution payload header and proposer state.latest_block_hash = payload.block_hash @@ -728,3 +730,17 @@ def process_execution_payload(state: BeaconState, if verify: assert envelope.state_root == hash_tree_root(state) ``` + +## Testing + +### Modified `is_merge_transition_complete` + +The function `is_merge_transition_complete` is modified for test purposes only to include the hash tree root of the empty KZG commitment list + +```python +def is_merge_transition_complete(state: BeaconState) -> bool: + header = ExecutionPayloadHeader() + kzgs = List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]() + header.blob_kzg_commitments_root = kzgs.hash_tree_root() + + return state.latest_execution_payload_header != header \ No newline at end of file diff --git a/specs/_features/eip7732/fork-choice.md b/specs/_features/eip7732/fork-choice.md index 8845127018..974a568474 100644 --- a/specs/_features/eip7732/fork-choice.md +++ b/specs/_features/eip7732/fork-choice.md @@ -35,6 +35,7 @@ - [`seconds_into_slot`](#seconds_into_slot) - [Modified `on_tick_per_slot`](#modified-on_tick_per_slot) - [`on_payload_attestation_message`](#on_payload_attestation_message) + - [Modified `validate_merge_block`](#modified-validate_merge_block) @@ -573,3 +574,34 @@ def on_payload_attestation_message( store.payload_withhold_boost_root = block.parent_root store.payload_withhold_boost_full = is_parent_node_full(store, block) ``` + +### Modified `validate_merge_block` + +The function `validate_merge_block` is modified for test purposes + +```python +def validate_merge_block(block: BeaconBlock) -> None: + """ + Check the parent PoW block of execution payload is a valid terminal PoW block. + + Note: Unavailable PoW block(s) may later become available, + and a client software MAY delay a call to ``validate_merge_block`` + until the PoW block(s) become available. + """ + if TERMINAL_BLOCK_HASH != Hash32(): + # If `TERMINAL_BLOCK_HASH` is used as an override, the activation epoch must be reached. + assert compute_epoch_at_slot(block.slot) >= TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH + assert block.body.signed_execution_payload_header.message.parent_block_hash == TERMINAL_BLOCK_HASH + return + + pow_block = get_pow_block(block.body.signed_execution_payload_header.message.parent_block_hash) + # Check if `pow_block` is available + assert pow_block is not None + pow_parent = get_pow_block(pow_block.parent_hash) + # Check if `pow_parent` is available + assert pow_parent is not None + # Check if `pow_block` is a valid terminal PoW block + assert is_valid_terminal_pow_block(pow_block, pow_parent) + + + diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_execution_payload.py index 325431e7a9..934c94c484 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_execution_payload.py @@ -13,9 +13,12 @@ expect_assertion_error, spec_state_test, with_bellatrix_and_later, + with_bellatrix_until_eip7732, with_phases, ) +from eth2spec.test.helpers.keys import privkeys from eth2spec.test.helpers.state import next_slot +from eth2spec.test.helpers.forks import is_post_eip7732 def run_execution_payload_processing(spec, state, execution_payload, valid=True, execution_valid=True): @@ -28,11 +31,34 @@ def run_execution_payload_processing(spec, state, execution_payload, valid=True, If ``valid == False``, run expecting ``AssertionError`` """ # Before Deneb, only `body.execution_payload` matters. `BeaconBlockBody` is just a wrapper. - body = spec.BeaconBlockBody(execution_payload=execution_payload) + # after EIP-7732 the execution payload is no longer in the body + if is_post_eip7732(spec): + envelope = spec.ExecutionPayloadEnvelope( + payload=execution_payload, + beacon_block_root=state.latest_block_header.hash_tree_root(), + payload_withheld=False, + ) + post_state = state.copy() + post_state.latest_block_hash = execution_payload.block_hash + post_state.latest_full_slot = state.slot + envelope.state_root = post_state.hash_tree_root() + privkey = privkeys[envelope.builder_index] + signature = spec.get_execution_payload_envelope_signature( + state, + envelope, + privkey, + ) + signed_envelope = spec.SignedExecutionPayloadEnvelope( + message=envelope, + signature=signature, + ) + else: + body = spec.BeaconBlockBody(execution_payload=execution_payload) yield 'pre', state yield 'execution', {'execution_valid': execution_valid} - yield 'body', body + if not is_post_eip7732(spec): + yield 'body', body called_new_block = False @@ -40,22 +66,33 @@ class TestEngine(spec.NoopExecutionEngine): def verify_and_notify_new_payload(self, new_payload_request) -> bool: nonlocal called_new_block, execution_valid called_new_block = True - assert new_payload_request.execution_payload == body.execution_payload + assert new_payload_request.execution_payload == execution_payload return execution_valid if not valid: - expect_assertion_error(lambda: spec.process_execution_payload(state, body, TestEngine())) + if is_post_eip7732(spec): + expect_assertion_error(lambda: spec.process_execution_payload(state, signed_envelope, TestEngine())) + else: + expect_assertion_error(lambda: spec.process_execution_payload(state, body, TestEngine())) yield 'post', None return - spec.process_execution_payload(state, body, TestEngine()) + if is_post_eip7732(spec): + spec.process_execution_payload(state, signed_envelope, TestEngine()) + else: + spec.process_execution_payload(state, body, TestEngine()) # Make sure we called the engine assert called_new_block yield 'post', state - assert state.latest_execution_payload_header == get_execution_payload_header(spec, body.execution_payload) + if is_post_eip7732(spec): + assert state.latest_full_slot == state.slot + assert state.latest_block_hash == execution_payload.block_hash + else: + assert state.latest_execution_payload_header == get_execution_payload_header( + spec, state, body.execution_payload) def run_success_test(spec, state): @@ -65,7 +102,7 @@ def run_success_test(spec, state): yield from run_execution_payload_processing(spec, state, execution_payload) -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_success_first_payload(spec, state): state = build_state_with_incomplete_transition(spec, state) @@ -73,7 +110,7 @@ def test_success_first_payload(spec, state): yield from run_success_test(spec, state) -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_success_regular_payload(spec, state): state = build_state_with_complete_transition(spec, state) @@ -89,14 +126,14 @@ def run_gap_slot_test(spec, state): yield from run_execution_payload_processing(spec, state, execution_payload) -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_success_first_payload_with_gap_slot(spec, state): state = build_state_with_incomplete_transition(spec, state) yield from run_gap_slot_test(spec, state) -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_success_regular_payload_with_gap_slot(spec, state): state = build_state_with_complete_transition(spec, state) @@ -111,14 +148,14 @@ def run_bad_execution_test(spec, state): yield from run_execution_payload_processing(spec, state, execution_payload, valid=False, execution_valid=False) -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_invalid_bad_execution_first_payload(spec, state): state = build_state_with_incomplete_transition(spec, state) yield from run_bad_execution_test(spec, state) -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_invalid_bad_execution_regular_payload(spec, state): state = build_state_with_complete_transition(spec, state) @@ -255,14 +292,14 @@ def run_non_empty_extra_data_test(spec, state): assert state.latest_execution_payload_header.extra_data == execution_payload.extra_data -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_non_empty_extra_data_first_payload(spec, state): state = build_state_with_incomplete_transition(spec, state) yield from run_non_empty_extra_data_test(spec, state) -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_non_empty_extra_data_regular_payload(spec, state): state = build_state_with_complete_transition(spec, state) @@ -284,14 +321,14 @@ def run_non_empty_transactions_test(spec, state): assert state.latest_execution_payload_header.transactions_root == execution_payload.transactions.hash_tree_root() -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_non_empty_transactions_first_payload(spec, state): state = build_state_with_incomplete_transition(spec, state) yield from run_non_empty_extra_data_test(spec, state) -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_non_empty_transactions_regular_payload(spec, state): state = build_state_with_complete_transition(spec, state) @@ -310,14 +347,14 @@ def run_zero_length_transaction_test(spec, state): assert state.latest_execution_payload_header.transactions_root == execution_payload.transactions.hash_tree_root() -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_zero_length_transaction_first_payload(spec, state): state = build_state_with_incomplete_transition(spec, state) yield from run_zero_length_transaction_test(spec, state) -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_zero_length_transaction_regular_payload(spec, state): state = build_state_with_complete_transition(spec, state) @@ -328,6 +365,11 @@ def run_randomized_non_validated_execution_fields_test(spec, state, rng, executi next_slot(spec, state) execution_payload = build_randomized_execution_payload(spec, state, rng) + if is_post_eip7732(spec): + state.latest_execution_payload_header.block_hash = execution_payload.block_hash + state.latest_execution_payload_header.gas_limit = execution_payload.gas_limit + state.latest_block_hash = execution_payload.parent_hash + yield from run_execution_payload_processing( spec, state, execution_payload, @@ -335,7 +377,7 @@ def run_randomized_non_validated_execution_fields_test(spec, state, rng, executi ) -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_randomized_non_validated_execution_fields_first_payload__execution_valid(spec, state): rng = Random(1111) @@ -343,7 +385,7 @@ def test_randomized_non_validated_execution_fields_first_payload__execution_vali yield from run_randomized_non_validated_execution_fields_test(spec, state, rng) -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_randomized_non_validated_execution_fields_regular_payload__execution_valid(spec, state): rng = Random(2222) @@ -351,7 +393,7 @@ def test_randomized_non_validated_execution_fields_regular_payload__execution_va yield from run_randomized_non_validated_execution_fields_test(spec, state, rng) -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_invalid_randomized_non_validated_execution_fields_first_payload__execution_invalid(spec, state): rng = Random(3333) @@ -359,7 +401,7 @@ def test_invalid_randomized_non_validated_execution_fields_first_payload__execut yield from run_randomized_non_validated_execution_fields_test(spec, state, rng, execution_valid=False) -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_invalid_randomized_non_validated_execution_fields_regular_payload__execution_invalid(spec, state): rng = Random(4444) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/fork_choice/test_should_override_forkchoice_update.py b/tests/core/pyspec/eth2spec/test/bellatrix/fork_choice/test_should_override_forkchoice_update.py index 6c8e33e954..dbbeb6ae0f 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/fork_choice/test_should_override_forkchoice_update.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/fork_choice/test_should_override_forkchoice_update.py @@ -1,6 +1,6 @@ from eth2spec.test.context import ( spec_state_test, - with_bellatrix_and_later, + with_bellatrix_until_eip7732, with_presets, ) from eth2spec.test.helpers.constants import ( @@ -28,7 +28,7 @@ ) -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test @with_presets([MINIMAL], reason="too slow") def test_should_override_forkchoice_update__false(spec, state): @@ -73,7 +73,7 @@ def test_should_override_forkchoice_update__false(spec, state): yield 'steps', test_steps -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_should_override_forkchoice_update__true(spec, state): test_steps = [] diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/bellatrix/sanity/test_blocks.py index 6ac76ca596..46852af131 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/sanity/test_blocks.py @@ -11,13 +11,13 @@ ) from eth2spec.test.context import ( BELLATRIX, - with_bellatrix_and_later, + with_bellatrix_until_eip7732, with_phases, spec_state_test, ) -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_empty_block_transition_no_tx(spec, state): yield 'pre', state @@ -31,7 +31,7 @@ def test_empty_block_transition_no_tx(spec, state): yield 'post', state -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_block_transition_randomized_payload(spec, state): yield 'pre', state diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py index 2d1aaced91..9eb45ff394 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py @@ -1,6 +1,6 @@ from eth2spec.test.context import ( spec_state_test, - with_bellatrix_and_later, + with_bellatrix_until_eip7732, ) from eth2spec.test.helpers.attestations import ( state_transition_with_full_block, @@ -28,7 +28,7 @@ ) -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_from_syncing_to_invalid(spec, state): test_steps = [] diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/unittests/test_transition.py b/tests/core/pyspec/eth2spec/test/bellatrix/unittests/test_transition.py index 4a7c5d4a17..f90f1447ca 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/unittests/test_transition.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/unittests/test_transition.py @@ -5,7 +5,8 @@ ) from eth2spec.test.context import ( spec_state_test, - with_bellatrix_and_later + with_bellatrix_and_later, + with_bellatrix_until_eip7732, ) @@ -32,7 +33,7 @@ def test_success_merge_complete(spec, state): ] -@with_bellatrix_and_later +@with_bellatrix_until_eip7732 @spec_state_test def test_is_merge_block_and_is_execution_enabled(spec, state): for result in expected_results: diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/unittests/test_validate_merge_block.py b/tests/core/pyspec/eth2spec/test/bellatrix/unittests/test_validate_merge_block.py index d519dfac0f..925a04489b 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/unittests/test_validate_merge_block.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/unittests/test_validate_merge_block.py @@ -4,11 +4,13 @@ build_empty_block_for_next_slot, ) from eth2spec.test.helpers.execution_payload import ( + build_empty_execution_payload, compute_el_block_hash, ) from eth2spec.test.helpers.pow_block import ( prepare_random_pow_chain, ) +from eth2spec.test.helpers.forks import is_post_eip7732 from eth2spec.test.context import ( spec_state_test, with_bellatrix_and_later, @@ -59,8 +61,15 @@ def test_validate_merge_block_success(spec, state): pow_chain.head(-1).total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) pow_chain.head().total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY block = build_empty_block_for_next_slot(spec, state) - block.body.execution_payload.parent_hash = pow_chain.head().block_hash - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state) + if is_post_eip7732(spec): + payload = build_empty_execution_payload(spec, state) + block.body.signed_execution_payload_header.message.parent_block_hash = pow_chain.head().block_hash + block.body.signed_execution_payload_header.message.block_hash = compute_el_block_hash( + spec, payload, state) + else: + block.body.execution_payload.parent_hash = pow_chain.head().block_hash + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state) + run_validate_merge_block(spec, pow_chain, block) @@ -80,8 +89,15 @@ def test_validate_merge_block_fail_parent_block_lookup(spec, state): pow_chain = prepare_random_pow_chain(spec, 1) pow_chain.head().total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY block = build_empty_block_for_next_slot(spec, state) - block.body.execution_payload.parent_hash = pow_chain.head().block_hash - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state) + if is_post_eip7732(spec): + payload = build_empty_execution_payload(spec, state) + block.body.signed_execution_payload_header.message.parent_block_hash = pow_chain.head().block_hash + block.body.signed_execution_payload_header.message.block_hash = compute_el_block_hash( + spec, payload, state) + else: + block.body.execution_payload.parent_hash = pow_chain.head().block_hash + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state) + run_validate_merge_block(spec, pow_chain, block, valid=False) @@ -92,8 +108,15 @@ def test_validate_merge_block_fail_after_terminal(spec, state): pow_chain.head(-1).total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY pow_chain.head().total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + uint256(1) block = build_empty_block_for_next_slot(spec, state) - block.body.execution_payload.parent_hash = pow_chain.head().block_hash - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state) + if is_post_eip7732(spec): + payload = build_empty_execution_payload(spec, state) + block.body.signed_execution_payload_header.message.parent_block_hash = pow_chain.head().block_hash + block.body.signed_execution_payload_header.message.block_hash = compute_el_block_hash( + spec, payload, state) + else: + block.body.execution_payload.parent_hash = pow_chain.head().block_hash + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state) + run_validate_merge_block(spec, pow_chain, block, valid=False) @@ -109,8 +132,15 @@ def test_validate_merge_block_tbh_override_success(spec, state): pow_chain.head().total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) pow_chain.head().block_hash = TERMINAL_BLOCK_HASH block = build_empty_block_for_next_slot(spec, state) - block.body.execution_payload.parent_hash = pow_chain.head().block_hash - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state) + if is_post_eip7732(spec): + payload = build_empty_execution_payload(spec, state) + block.body.signed_execution_payload_header.message.parent_block_hash = pow_chain.head().block_hash + block.body.signed_execution_payload_header.message.block_hash = compute_el_block_hash( + spec, payload, state) + else: + block.body.execution_payload.parent_hash = pow_chain.head().block_hash + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state) + run_validate_merge_block(spec, pow_chain, block) @@ -125,8 +155,15 @@ def test_validate_merge_block_fail_parent_hash_is_not_tbh(spec, state): pow_chain.head(-1).total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) pow_chain.head().total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY block = build_empty_block_for_next_slot(spec, state) - block.body.execution_payload.parent_hash = pow_chain.head().block_hash - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state) + if is_post_eip7732(spec): + payload = build_empty_execution_payload(spec, state) + block.body.signed_execution_payload_header.message.parent_block_hash = pow_chain.head().block_hash + block.body.signed_execution_payload_header.message.block_hash = compute_el_block_hash( + spec, payload, state) + else: + block.body.execution_payload.parent_hash = pow_chain.head().block_hash + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state) + run_validate_merge_block(spec, pow_chain, block, valid=False) @@ -142,8 +179,15 @@ def test_validate_merge_block_terminal_block_hash_fail_activation_not_reached(sp pow_chain.head().total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY pow_chain.head().block_hash = TERMINAL_BLOCK_HASH block = build_empty_block_for_next_slot(spec, state) - block.body.execution_payload.parent_hash = pow_chain.head().block_hash - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state) + if is_post_eip7732(spec): + payload = build_empty_execution_payload(spec, state) + block.body.signed_execution_payload_header.message.parent_block_hash = pow_chain.head().block_hash + block.body.signed_execution_payload_header.message.block_hash = compute_el_block_hash( + spec, payload, state) + else: + block.body.execution_payload.parent_hash = pow_chain.head().block_hash + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state) + run_validate_merge_block(spec, pow_chain, block, valid=False) @@ -158,6 +202,13 @@ def test_validate_merge_block_fail_activation_not_reached_parent_hash_is_not_tbh pow_chain.head(-1).total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) pow_chain.head().total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY block = build_empty_block_for_next_slot(spec, state) - block.body.execution_payload.parent_hash = pow_chain.head().block_hash - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state) + if is_post_eip7732(spec): + payload = build_empty_execution_payload(spec, state) + block.body.signed_execution_payload_header.message.parent_block_hash = pow_chain.head().block_hash + block.body.signed_execution_payload_header.message.block_hash = compute_el_block_hash( + spec, payload, state) + else: + block.body.execution_payload.parent_hash = pow_chain.head().block_hash + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state) + run_validate_merge_block(spec, pow_chain, block, valid=False) diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py index 1abb93e62c..de455bacb1 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py @@ -1,6 +1,6 @@ from eth2spec.test.context import ( spec_state_test, - with_capella_and_later, + with_capella_until_eip7732, ) from eth2spec.test.helpers.forks import is_post_electra from eth2spec.test.helpers.state import next_epoch_via_block @@ -11,7 +11,7 @@ from eth2spec.test.helpers.withdrawals import set_validator_fully_withdrawable -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_success_top_up_to_withdrawn_validator(spec, state): validator_index = 0 diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py index 26691bd9b5..3a762fba3a 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py @@ -4,6 +4,7 @@ spec_state_test, with_presets, with_capella_and_later, + with_capella_until_eip7732, ) from eth2spec.test.helpers.constants import MAINNET, MINIMAL from eth2spec.test.helpers.execution_payload import ( @@ -27,7 +28,7 @@ ) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_success_zero_expected_withdrawals(spec, state): assert len(get_expected_withdrawals(spec, state)) == 0 @@ -38,7 +39,7 @@ def test_success_zero_expected_withdrawals(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_success_one_full_withdrawal(spec, state): fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals( @@ -55,7 +56,7 @@ def test_success_one_full_withdrawal(spec, state): partial_withdrawals_indices=partial_withdrawals_indices) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_success_one_partial_withdrawal(spec, state): fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals( @@ -75,7 +76,7 @@ def test_success_one_partial_withdrawal(spec, state): ) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_success_mixed_fully_and_partial_withdrawable(spec, state): num_full_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD // 2 @@ -96,7 +97,7 @@ def test_success_mixed_fully_and_partial_withdrawable(spec, state): partial_withdrawals_indices=partial_withdrawals_indices) -@with_capella_and_later +@with_capella_until_eip7732 @with_presets([MAINNET], reason="too few validators with minimal config") @spec_state_test def test_success_all_fully_withdrawable_in_one_sweep(spec, state): @@ -115,7 +116,7 @@ def test_success_all_fully_withdrawable_in_one_sweep(spec, state): partial_withdrawals_indices=partial_withdrawals_indices) -@with_capella_and_later +@with_capella_until_eip7732 @with_presets([MINIMAL], reason="too many validators with mainnet config") @spec_state_test def test_success_all_fully_withdrawable(spec, state): @@ -134,7 +135,7 @@ def test_success_all_fully_withdrawable(spec, state): partial_withdrawals_indices=partial_withdrawals_indices) -@with_capella_and_later +@with_capella_until_eip7732 @with_presets([MAINNET], reason="too few validators with minimal config") @spec_state_test def test_success_all_partially_withdrawable_in_one_sweep(spec, state): @@ -153,7 +154,7 @@ def test_success_all_partially_withdrawable_in_one_sweep(spec, state): partial_withdrawals_indices=partial_withdrawals_indices) -@with_capella_and_later +@with_capella_until_eip7732 @with_presets([MINIMAL], reason="too many validators with mainnet config") @spec_state_test def test_success_all_partially_withdrawable(spec, state): @@ -176,7 +177,7 @@ def test_success_all_partially_withdrawable(spec, state): # Failure cases in which the number of withdrawals in the execution_payload is incorrect # -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_invalid_non_withdrawable_non_empty_withdrawals(spec, state): next_slot(spec, state) @@ -193,7 +194,7 @@ def test_invalid_non_withdrawable_non_empty_withdrawals(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_invalid_one_expected_full_withdrawal_and_none_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, rng=random.Random(42), num_full_withdrawals=1) @@ -206,7 +207,7 @@ def test_invalid_one_expected_full_withdrawal_and_none_in_withdrawals(spec, stat yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_invalid_one_expected_partial_withdrawal_and_none_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, rng=random.Random(42), num_partial_withdrawals=1) @@ -219,7 +220,7 @@ def test_invalid_one_expected_partial_withdrawal_and_none_in_withdrawals(spec, s yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_invalid_one_expected_full_withdrawal_and_duplicate_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, rng=random.Random(42), num_full_withdrawals=2) @@ -232,7 +233,7 @@ def test_invalid_one_expected_full_withdrawal_and_duplicate_in_withdrawals(spec, yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_invalid_two_expected_partial_withdrawal_and_duplicate_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, rng=random.Random(42), num_partial_withdrawals=2) @@ -245,7 +246,7 @@ def test_invalid_two_expected_partial_withdrawal_and_duplicate_in_withdrawals(sp yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_invalid_max_per_slot_full_withdrawals_and_one_less_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, rng=random.Random(42), @@ -259,7 +260,7 @@ def test_invalid_max_per_slot_full_withdrawals_and_one_less_in_withdrawals(spec, yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_invalid_max_per_slot_partial_withdrawals_and_one_less_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, rng=random.Random(42), @@ -273,7 +274,7 @@ def test_invalid_max_per_slot_partial_withdrawals_and_one_less_in_withdrawals(sp yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_invalid_a_lot_fully_withdrawable_too_few_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, rng=random.Random(42), @@ -287,7 +288,7 @@ def test_invalid_a_lot_fully_withdrawable_too_few_in_withdrawals(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_invalid_a_lot_partially_withdrawable_too_few_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, rng=random.Random(42), @@ -301,7 +302,7 @@ def test_invalid_a_lot_partially_withdrawable_too_few_in_withdrawals(spec, state yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_invalid_a_lot_mixed_withdrawable_in_queue_too_few_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, rng=random.Random(42), @@ -320,7 +321,7 @@ def test_invalid_a_lot_mixed_withdrawable_in_queue_too_few_in_withdrawals(spec, # Failure cases in which the withdrawals in the execution_payload are incorrect # -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_invalid_incorrect_withdrawal_index(spec, state): prepare_expected_withdrawals(spec, state, rng=random.Random(42), @@ -334,7 +335,7 @@ def test_invalid_incorrect_withdrawal_index(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_invalid_incorrect_address_full(spec, state): prepare_expected_withdrawals(spec, state, rng=random.Random(42), @@ -348,7 +349,7 @@ def test_invalid_incorrect_address_full(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_invalid_incorrect_address_partial(spec, state): prepare_expected_withdrawals(spec, state, rng=random.Random(42), @@ -362,7 +363,7 @@ def test_invalid_incorrect_address_partial(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_invalid_incorrect_amount_full(spec, state): prepare_expected_withdrawals(spec, state, rng=random.Random(42), num_full_withdrawals=1) @@ -375,7 +376,7 @@ def test_invalid_incorrect_amount_full(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_invalid_incorrect_amount_partial(spec, state): prepare_expected_withdrawals(spec, state, rng=random.Random(42), num_full_withdrawals=1) @@ -388,7 +389,7 @@ def test_invalid_incorrect_amount_partial(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_invalid_one_of_many_incorrectly_full(spec, state): prepare_expected_withdrawals(spec, state, rng=random.Random(42), @@ -408,7 +409,7 @@ def test_invalid_one_of_many_incorrectly_full(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_invalid_one_of_many_incorrectly_partial(spec, state): prepare_expected_withdrawals(spec, state, rng=random.Random(42), @@ -428,7 +429,7 @@ def test_invalid_one_of_many_incorrectly_partial(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_invalid_many_incorrectly_full(spec, state): prepare_expected_withdrawals(spec, state, rng=random.Random(42), @@ -448,7 +449,7 @@ def test_invalid_many_incorrectly_full(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_invalid_many_incorrectly_partial(spec, state): prepare_expected_withdrawals(spec, state, rng=random.Random(42), @@ -472,7 +473,7 @@ def test_invalid_many_incorrectly_partial(spec, state): # More full withdrawal cases # -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_withdrawable_epoch_but_0_balance(spec, state): current_epoch = spec.get_current_epoch(state) @@ -486,7 +487,7 @@ def test_withdrawable_epoch_but_0_balance(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_withdrawable_epoch_but_0_effective_balance_0_balance(spec, state): current_epoch = spec.get_current_epoch(state) @@ -500,7 +501,7 @@ def test_withdrawable_epoch_but_0_effective_balance_0_balance(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_withdrawable_epoch_but_0_effective_balance_nonzero_balance(spec, state): current_epoch = spec.get_current_epoch(state) @@ -514,7 +515,7 @@ def test_withdrawable_epoch_but_0_effective_balance_nonzero_balance(spec, state) yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_no_withdrawals_but_some_next_epoch(spec, state): current_epoch = spec.get_current_epoch(state) @@ -528,7 +529,7 @@ def test_no_withdrawals_but_some_next_epoch(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_all_withdrawal(spec, state): # Make all validators withdrawable @@ -564,25 +565,25 @@ def run_random_full_withdrawals_test(spec, state, rng): yield from run_withdrawals_processing(spec, state, execution_payload) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_random_full_withdrawals_0(spec, state): yield from run_random_full_withdrawals_test(spec, state, random.Random(444)) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_random_full_withdrawals_1(spec, state): yield from run_random_full_withdrawals_test(spec, state, random.Random(420)) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_random_full_withdrawals_2(spec, state): yield from run_random_full_withdrawals_test(spec, state, random.Random(200)) -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_random_full_withdrawals_3(spec, state): yield from run_random_full_withdrawals_test(spec, state, random.Random(2000000)) diff --git a/tests/core/pyspec/eth2spec/test/capella/light_client/test_single_merkle_proof.py b/tests/core/pyspec/eth2spec/test/capella/light_client/test_single_merkle_proof.py index 7f414ab285..7c39236fe8 100644 --- a/tests/core/pyspec/eth2spec/test/capella/light_client/test_single_merkle_proof.py +++ b/tests/core/pyspec/eth2spec/test/capella/light_client/test_single_merkle_proof.py @@ -1,6 +1,6 @@ from eth2spec.test.context import ( spec_state_test, - with_capella_and_later, + with_capella_until_eip7732, with_test_suite_name, ) from eth2spec.test.helpers.attestations import ( @@ -9,7 +9,7 @@ @with_test_suite_name("BeaconBlockBody") -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_execution_merkle_proof(spec, state): block = state_transition_with_full_block(spec, state, True, False) 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 c6d1f0cc59..796364c802 100644 --- a/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py @@ -1,8 +1,9 @@ import random from eth2spec.test.helpers.constants import MINIMAL -from eth2spec.test.helpers.forks import is_post_electra +from eth2spec.test.helpers.forks import is_post_electra, is_post_eip7732 from eth2spec.test.context import ( with_capella_and_later, + with_capella_until_eip7732, spec_state_test, with_presets, ) @@ -38,7 +39,7 @@ # `is_execution_enabled` has been removed from Capella # -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_invalid_is_execution_enabled_false(spec, state): # Set `latest_execution_payload_header` to empty @@ -199,7 +200,7 @@ def test_invalid_two_bls_changes_of_different_addresses_same_validator_same_bloc # Withdrawals # -@with_capella_and_later +@with_capella_until_eip7732 @spec_state_test def test_full_withdrawal_in_epoch_transition(spec, state): index = 0 @@ -308,7 +309,12 @@ def test_withdrawal_success_two_blocks(spec, state): block = build_empty_block_for_next_slot(spec, state) signed_block_2 = state_transition_and_sign_block(spec, state, block) - assert state.next_withdrawal_index == pre_next_withdrawal_index + spec.MAX_WITHDRAWALS_PER_PAYLOAD * 2 + # after EIP-7732 the second block does not perform any withdrawals because + # there was no payload processed + if is_post_eip7732(spec): + assert state.next_withdrawal_index == pre_next_withdrawal_index + spec.MAX_WITHDRAWALS_PER_PAYLOAD + else: + assert state.next_withdrawal_index == pre_next_withdrawal_index + spec.MAX_WITHDRAWALS_PER_PAYLOAD * 2 yield 'blocks', [signed_block_1, signed_block_2] yield 'post', state diff --git a/tests/core/pyspec/eth2spec/test/context.py b/tests/core/pyspec/eth2spec/test/context.py index 1653921cae..d80966381f 100644 --- a/tests/core/pyspec/eth2spec/test/context.py +++ b/tests/core/pyspec/eth2spec/test/context.py @@ -9,7 +9,7 @@ from .helpers.constants import ( PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, ELECTRA, FULU, - EIP7441, + EIP7441, EIP7732, MINIMAL, ALL_PHASES, POST_FORK_OF, @@ -422,7 +422,8 @@ def with_all_phases(fn): def with_all_phases_from(earliest_phase, all_phases=ALL_PHASES): """ - A decorator factory for running a tests with every phase except the ones listed + A decorator factory for running a tests with every phase starting at `earliest_phase` + excluding the ones listed. """ def decorator(fn): return with_phases([phase for phase in all_phases if is_post_fork(phase, earliest_phase)])(fn) @@ -452,6 +453,17 @@ def decorator(fn): return decorator +def with_all_phases_from_to_except(earliest_phase, latest_phase, except_phases=None): + """ + A decorator factory for running a tests with every phase starting at `earliest_phase` + and ending at `latest_phase` excluding the ones listed. + """ + def decorator(fn): + return with_phases([phase for phase in ALL_PHASES if phase not in except_phases and + is_post_fork(phase, earliest_phase) and not + is_post_fork(phase, latest_phase)])(fn) + + def with_all_phases_except(exclusion_phases): """ A decorator factory for running a tests with every phase except the ones listed @@ -576,6 +588,12 @@ def wrapper(*args, spec: Spec, **kw): with_fulu_and_later = with_all_phases_from(FULU, all_phases=ALLOWED_TEST_RUNNER_FORKS) with_eip7441_and_later = with_all_phases_from(EIP7441, all_phases=ALLOWED_TEST_RUNNER_FORKS) +with_altair_until_eip7732 = with_all_phases_from_to(ALTAIR, EIP7732) +with_bellatrix_until_eip7732 = with_all_phases_from_to(BELLATRIX, EIP7732) +with_capella_until_eip7732 = with_all_phases_from_to(CAPELLA, EIP7732) +with_deneb_until_eip7732 = with_all_phases_from_to(DENEB, EIP7732) +with_electra_until_eip7732 = with_all_phases_from_to(ELECTRA, EIP7732) + class quoted_str(str): pass diff --git a/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_execution_payload.py index e9405e84f9..7266de8ae4 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_execution_payload.py @@ -8,8 +8,10 @@ from eth2spec.test.context import ( spec_state_test, expect_assertion_error, - with_deneb_and_later + with_deneb_and_later, ) +from eth2spec.test.helpers.keys import privkeys +from eth2spec.test.helpers.forks import is_post_eip7732 from eth2spec.test.helpers.blob import ( get_sample_blob_tx, get_max_blob_count, @@ -26,11 +28,41 @@ def run_execution_payload_processing(spec, state, execution_payload, blob_kzg_co - post-state ('post'). If ``valid == False``, run expecting ``AssertionError`` """ - # Before Deneb, only `body.execution_payload` matters. `BeaconBlockBody` is just a wrapper. - body = spec.BeaconBlockBody( - blob_kzg_commitments=blob_kzg_commitments, - execution_payload=execution_payload - ) + + # after EIP-7732 the execution payload is no longer in the body + if is_post_eip7732(spec): + envelope = spec.ExecutionPayloadEnvelope( + payload=execution_payload, + payload_withheld=False, + blob_kzg_commitments=blob_kzg_commitments, + ) + kzg_list = spec.List[spec.KZGCommitment, spec.MAX_BLOB_COMMITMENTS_PER_BLOCK](blob_kzg_commitments) + state.latest_execution_payload_header.blob_kzg_commitments_root = kzg_list.hash_tree_root() + post_state = state.copy() + previous_state_root = state.hash_tree_root() + if post_state.latest_block_header.state_root == spec.Root(): + post_state.latest_block_header.state_root = previous_state_root + envelope.beacon_block_root = post_state.latest_block_header.hash_tree_root() + + post_state.latest_block_hash = execution_payload.block_hash + post_state.latest_full_slot = state.slot + envelope.state_root = post_state.hash_tree_root() + privkey = privkeys[envelope.builder_index] + signature = spec.get_execution_payload_envelope_signature( + state, + envelope, + privkey, + ) + signed_envelope = spec.SignedExecutionPayloadEnvelope( + message=envelope, + signature=signature, + ) + body = spec.BeaconBlockBody() + else: + body = spec.BeaconBlockBody( + blob_kzg_commitments=blob_kzg_commitments, + execution_payload=execution_payload, + ) yield 'pre', state yield 'execution', {'execution_valid': execution_valid} @@ -42,22 +74,32 @@ class TestEngine(spec.NoopExecutionEngine): def verify_and_notify_new_payload(self, new_payload_request) -> bool: nonlocal called_new_block, execution_valid called_new_block = True - assert new_payload_request.execution_payload == body.execution_payload + assert new_payload_request.execution_payload == execution_payload return execution_valid if not valid: - expect_assertion_error(lambda: spec.process_execution_payload(state, body, TestEngine())) + if is_post_eip7732(spec): + expect_assertion_error(lambda: spec.process_execution_payload(state, signed_envelope, TestEngine())) + else: + expect_assertion_error(lambda: spec.process_execution_payload(state, body, TestEngine())) yield 'post', None return - spec.process_execution_payload(state, body, TestEngine()) + if is_post_eip7732(spec): + spec.process_execution_payload(state, signed_envelope, TestEngine()) + else: + spec.process_execution_payload(state, body, TestEngine()) # Make sure we called the engine assert called_new_block yield 'post', state - assert state.latest_execution_payload_header == get_execution_payload_header(spec, body.execution_payload) + if is_post_eip7732(spec): + assert state.latest_block_hash == execution_payload.block_hash + assert state.latest_full_slot == state.slot + else: + assert state.latest_execution_payload_header == get_execution_payload_header(spec, state, execution_payload) """ @@ -81,6 +123,10 @@ def test_incorrect_blob_tx_type(spec, state): execution_payload.transactions = [opaque_tx] execution_payload.block_hash = compute_el_block_hash(spec, execution_payload, state) + # Make the first block full in EIP-7732 + if is_post_eip7732(spec): + state.latest_execution_payload_header.block_hash = execution_payload.block_hash + yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments) @@ -98,6 +144,9 @@ def test_incorrect_transaction_length_1_extra_byte(spec, state): execution_payload.transactions = [opaque_tx] execution_payload.block_hash = compute_el_block_hash(spec, execution_payload, state) + # Make the first block full in EIP-7732 + if is_post_eip7732(spec): + state.latest_execution_payload_header.block_hash = execution_payload.block_hash yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments) @@ -115,6 +164,9 @@ def test_incorrect_transaction_length_1_byte_short(spec, state): execution_payload.transactions = [opaque_tx] execution_payload.block_hash = compute_el_block_hash(spec, execution_payload, state) + # Make the first block full in EIP-7732 + if is_post_eip7732(spec): + state.latest_execution_payload_header.block_hash = execution_payload.block_hash yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments) @@ -132,6 +184,9 @@ def test_incorrect_transaction_length_empty(spec, state): execution_payload.transactions = [opaque_tx] execution_payload.block_hash = compute_el_block_hash(spec, execution_payload, state) + # Make the first block full in EIP-7732 + if is_post_eip7732(spec): + state.latest_execution_payload_header.block_hash = execution_payload.block_hash yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments) @@ -149,6 +204,9 @@ def test_incorrect_transaction_length_32_extra_bytes(spec, state): execution_payload.transactions = [opaque_tx] execution_payload.block_hash = compute_el_block_hash(spec, execution_payload, state) + # Make the first block full in EIP-7732 + if is_post_eip7732(spec): + state.latest_execution_payload_header.block_hash = execution_payload.block_hash yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments) @@ -165,6 +223,9 @@ def test_no_transactions_with_commitments(spec, state): execution_payload.transactions = [] execution_payload.block_hash = compute_el_block_hash(spec, execution_payload, state) + # Make the first block full in EIP-7732 + if is_post_eip7732(spec): + state.latest_execution_payload_header.block_hash = execution_payload.block_hash yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments) @@ -182,6 +243,9 @@ def test_incorrect_commitment(spec, state): execution_payload.transactions = [opaque_tx] execution_payload.block_hash = compute_el_block_hash(spec, execution_payload, state) + # Make the first block full in EIP-7732 + if is_post_eip7732(spec): + state.latest_execution_payload_header.block_hash = execution_payload.block_hash yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments) @@ -199,6 +263,9 @@ def test_no_commitments_for_transactions(spec, state): execution_payload.transactions = [opaque_tx] execution_payload.block_hash = compute_el_block_hash(spec, execution_payload, state) + if is_post_eip7732(spec): + state.latest_execution_payload_header.block_hash = execution_payload.block_hash + yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments) @@ -216,6 +283,9 @@ def test_incorrect_commitments_order(spec, state): execution_payload.transactions = [opaque_tx] execution_payload.block_hash = compute_el_block_hash(spec, execution_payload, state) + # Make the first block full in EIP-7732 + if is_post_eip7732(spec): + state.latest_execution_payload_header.block_hash = execution_payload.block_hash yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments) @@ -235,6 +305,9 @@ def test_incorrect_transaction_no_blobs_but_with_commitments(spec, state): execution_payload.transactions = [opaque_tx] execution_payload.block_hash = compute_el_block_hash(spec, execution_payload, state) + if is_post_eip7732(spec): + state.latest_execution_payload_header.block_hash = execution_payload.block_hash + # the transaction doesn't contain any blob, but commitments are provided yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments) @@ -250,6 +323,9 @@ def test_incorrect_block_hash(spec, state): execution_payload.block_hash = b'\x12' * 32 # incorrect block hash # CL itself doesn't verify EL block hash + # Make the first block full in EIP-7732 + if is_post_eip7732(spec): + state.latest_execution_payload_header.block_hash = execution_payload.block_hash yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments) @@ -267,6 +343,9 @@ def test_zeroed_commitment(spec, state): execution_payload.transactions = [opaque_tx] execution_payload.block_hash = compute_el_block_hash(spec, execution_payload, state) + # Make the first block full in EIP-7732 + if is_post_eip7732(spec): + state.latest_execution_payload_header.block_hash = execution_payload.block_hash yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments) @@ -283,6 +362,9 @@ def test_invalid_correct_input__execution_invalid(spec, state): execution_payload.transactions = [opaque_tx] execution_payload.block_hash = compute_el_block_hash(spec, execution_payload, state) + # Make the first block full in EIP-7732 + if is_post_eip7732(spec): + state.latest_execution_payload_header.block_hash = execution_payload.block_hash yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments, valid=False, execution_valid=False) @@ -297,4 +379,7 @@ def test_invalid_exceed_max_blobs_per_block(spec, state): execution_payload.transactions = [opaque_tx] execution_payload.block_hash = compute_el_block_hash(spec, execution_payload, state) + # Make the first block full in EIP-7732 + if is_post_eip7732(spec): + state.latest_execution_payload_header.block_hash = execution_payload.block_hash yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments, valid=False) diff --git a/tests/core/pyspec/eth2spec/test/deneb/fork_choice/test_on_block.py b/tests/core/pyspec/eth2spec/test/deneb/fork_choice/test_on_block.py index 2845af4634..41d3a5e56f 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/fork_choice/test_on_block.py +++ b/tests/core/pyspec/eth2spec/test/deneb/fork_choice/test_on_block.py @@ -8,6 +8,7 @@ from eth2spec.test.helpers.constants import ( DENEB, FULU, + EIP7732, ) from eth2spec.test.helpers.block import ( @@ -39,7 +40,9 @@ def get_block_with_blob(spec, state, rng=None): return block, blobs, blob_kzg_proofs -@with_all_phases_from_except(DENEB, [FULU]) +# TODO(jtraglia): Use with_all_phases_from_to_except after EIP7732 is based on Fulu. +# This applies to every other test in this file too. +@with_all_phases_from_except(DENEB, [FULU, EIP7732]) @spec_state_test def test_simple_blob_data(spec, state): rng = Random(1234) @@ -74,7 +77,7 @@ def test_simple_blob_data(spec, state): yield 'steps', test_steps -@with_all_phases_from_except(DENEB, [FULU]) +@with_all_phases_from_except(DENEB, [FULU, EIP7732]) @spec_state_test def test_invalid_incorrect_proof(spec, state): rng = Random(1234) @@ -102,7 +105,7 @@ def test_invalid_incorrect_proof(spec, state): yield 'steps', test_steps -@with_all_phases_from_except(DENEB, [FULU]) +@with_all_phases_from_except(DENEB, [FULU, EIP7732]) @spec_state_test def test_invalid_data_unavailable(spec, state): rng = Random(1234) @@ -130,7 +133,7 @@ def test_invalid_data_unavailable(spec, state): yield 'steps', test_steps -@with_all_phases_from_except(DENEB, [FULU]) +@with_all_phases_from_except(DENEB, [FULU, EIP7732]) @spec_state_test def test_invalid_wrong_proofs_length(spec, state): rng = Random(1234) @@ -158,7 +161,7 @@ def test_invalid_wrong_proofs_length(spec, state): yield 'steps', test_steps -@with_all_phases_from_except(DENEB, [FULU]) +@with_all_phases_from_except(DENEB, [FULU, EIP7732]) @spec_state_test def test_invalid_wrong_blobs_length(spec, state): rng = Random(1234) diff --git a/tests/core/pyspec/eth2spec/test/deneb/merkle_proof/test_single_merkle_proof.py b/tests/core/pyspec/eth2spec/test/deneb/merkle_proof/test_single_merkle_proof.py index 6deccd31e2..e88e938628 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/merkle_proof/test_single_merkle_proof.py +++ b/tests/core/pyspec/eth2spec/test/deneb/merkle_proof/test_single_merkle_proof.py @@ -12,6 +12,7 @@ from eth2spec.test.helpers.execution_payload import ( compute_el_block_hash, ) +from eth2spec.test.helpers.forks import is_post_eip7732 from eth2spec.test.helpers.blob import ( get_sample_blob_tx, ) @@ -34,22 +35,49 @@ def _run_blob_kzg_commitment_merkle_proof_test(spec, state, rng=None): mode=RandomizationMode, chaos=True, ) - block.body.blob_kzg_commitments = blob_kzg_commitments - block.body.execution_payload.transactions = [opaque_tx] - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state) + if is_post_eip7732(spec): + blob_kzg_commitments = spec.List[spec.KZGCommitment, spec.MAX_BLOB_COMMITMENTS_PER_BLOCK]( + blob_kzg_commitments + ) + kzg_root = blob_kzg_commitments.hash_tree_root() + block.body.signed_execution_payload_header.message.blob_kzg_commitments_root = kzg_root + else: + block.body.blob_kzg_commitments = blob_kzg_commitments + block.body.execution_payload.transactions = [opaque_tx] + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state) + signed_block = sign_block(spec, state, block, proposer_index=0) - blob_sidecars = spec.get_blob_sidecars(signed_block, blobs, proofs) + if is_post_eip7732(spec): + blob_sidecars = spec.get_blob_sidecars(signed_block, blobs, blob_kzg_commitments, proofs) + else: + blob_sidecars = spec.get_blob_sidecars(signed_block, blobs, proofs) blob_index = 0 blob_sidecar = blob_sidecars[blob_index] yield "object", block.body kzg_commitment_inclusion_proof = blob_sidecar.kzg_commitment_inclusion_proof - gindex = spec.get_generalized_index(spec.BeaconBlockBody, 'blob_kzg_commitments', blob_index) + + if is_post_eip7732(spec): + inner_gindex = spec.get_generalized_index( + spec.List[spec.KZGCommitment, spec.MAX_BLOB_COMMITMENTS_PER_BLOCK], + blob_index + ) + outer_gindex = spec.get_generalized_index( + spec.BeaconBlockBody, + "signed_execution_payload_header", + "message", + "blob_kzg_commitments_root", + ) + gindex = spec.concat_generalized_indices(outer_gindex, inner_gindex) + else: + gindex = spec.get_generalized_index(spec.BeaconBlockBody, 'blob_kzg_commitments', blob_index) + yield "proof", { "leaf": "0x" + blob_sidecar.kzg_commitment.hash_tree_root().hex(), "leaf_index": gindex, "branch": ['0x' + root.hex() for root in kzg_commitment_inclusion_proof] } + assert spec.is_valid_merkle_branch( leaf=blob_sidecar.kzg_commitment.hash_tree_root(), branch=blob_sidecar.kzg_commitment_inclusion_proof, diff --git a/tests/core/pyspec/eth2spec/test/deneb/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/deneb/sanity/test_blocks.py index 19c0fcd0c5..79e5261f1a 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/deneb/sanity/test_blocks.py @@ -8,7 +8,7 @@ ) from eth2spec.test.context import ( spec_state_test, - with_deneb_and_later, + with_deneb_until_eip7732, ) from eth2spec.test.helpers.execution_payload import ( compute_el_block_hash, @@ -52,55 +52,55 @@ def run_block_with_blobs(spec, state, blob_count, tx_count=1, blob_gas_used=1, e yield 'post', state if valid else None -@with_deneb_and_later +@with_deneb_until_eip7732 @spec_state_test def test_zero_blob(spec, state): yield from run_block_with_blobs(spec, state, blob_count=0) -@with_deneb_and_later +@with_deneb_until_eip7732 @spec_state_test def test_one_blob(spec, state): yield from run_block_with_blobs(spec, state, blob_count=1) -@with_deneb_and_later +@with_deneb_until_eip7732 @spec_state_test def test_one_blob_two_txs(spec, state): yield from run_block_with_blobs(spec, state, blob_count=1, tx_count=2) -@with_deneb_and_later +@with_deneb_until_eip7732 @spec_state_test def test_one_blob_max_txs(spec, state): yield from run_block_with_blobs(spec, state, blob_count=1, tx_count=get_max_blob_count(spec)) -@with_deneb_and_later +@with_deneb_until_eip7732 @spec_state_test def test_invalid_one_blob_max_plus_one_txs(spec, state): yield from run_block_with_blobs(spec, state, blob_count=1, tx_count=get_max_blob_count(spec) + 1, valid=False) -@with_deneb_and_later +@with_deneb_until_eip7732 @spec_state_test def test_max_blobs_per_block(spec, state): yield from run_block_with_blobs(spec, state, blob_count=get_max_blob_count(spec)) -@with_deneb_and_later +@with_deneb_until_eip7732 @spec_state_test def test_invalid_max_blobs_per_block_two_txs(spec, state): yield from run_block_with_blobs(spec, state, blob_count=get_max_blob_count(spec), tx_count=2, valid=False) -@with_deneb_and_later +@with_deneb_until_eip7732 @spec_state_test def test_invalid_exceed_max_blobs_per_block(spec, state): yield from run_block_with_blobs(spec, state, blob_count=get_max_blob_count(spec) + 1, valid=False) -@with_deneb_and_later +@with_deneb_until_eip7732 @spec_state_test def test_mix_blob_tx_and_non_blob_tx(spec, state): yield from run_block_with_blobs(spec, state, blob_count=1, tx_count=1, non_blob_tx_count=1) diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/test_config_invariants.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/test_config_invariants.py index 1f44257856..a901adcb58 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/unittests/test_config_invariants.py +++ b/tests/core/pyspec/eth2spec/test/deneb/unittests/test_config_invariants.py @@ -3,6 +3,7 @@ spec_test, with_deneb_and_later, ) +from eth2spec.test.helpers.forks import is_post_eip7732 @with_deneb_and_later @@ -24,5 +25,19 @@ def test_networking(spec): # Start with the same size, but `BLOB_SIDECAR_SUBNET_COUNT` could potentially increase later. assert spec.config.BLOB_SIDECAR_SUBNET_COUNT == spec.config.MAX_BLOBS_PER_BLOCK for i in range(spec.MAX_BLOB_COMMITMENTS_PER_BLOCK): - gindex = spec.get_generalized_index(spec.BeaconBlockBody, 'blob_kzg_commitments', i) - assert spec.floorlog2(gindex) == spec.KZG_COMMITMENT_INCLUSION_PROOF_DEPTH + if is_post_eip7732(spec): + inner_gindex = spec.get_generalized_index( + spec.List[spec.KZGCommitment, spec.MAX_BLOB_COMMITMENTS_PER_BLOCK], + i + ) + outer_gindex = spec.get_generalized_index( + spec.BeaconBlockBody, + "signed_execution_payload_header", + "message", + "blob_kzg_commitments_root", + ) + gindex = spec.concat_generalized_indices(outer_gindex, inner_gindex) + assert spec.floorlog2(gindex) == spec.KZG_COMMITMENT_INCLUSION_PROOF_DEPTH_EIP7732 + else: + gindex = spec.get_generalized_index(spec.BeaconBlockBody, 'blob_kzg_commitments', i) + assert spec.floorlog2(gindex) == spec.KZG_COMMITMENT_INCLUSION_PROOF_DEPTH diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py index 0e02ab8c3c..0976d93cec 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py +++ b/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py @@ -6,6 +6,7 @@ from eth2spec.test.helpers.execution_payload import ( compute_el_block_hash, ) +from eth2spec.test.helpers.forks import is_post_eip7732 from eth2spec.test.helpers.blob import ( get_sample_blob_tx, ) @@ -24,13 +25,22 @@ def _get_sample_sidecars(spec, state, rng): opaque_tx_2, blobs_2, blob_kzg_commitments_2, proofs_2 = get_sample_blob_tx(spec, blob_count=blob_count, rng=rng) assert opaque_tx_1 != opaque_tx_2 - block.body.blob_kzg_commitments = blob_kzg_commitments_1 + blob_kzg_commitments_2 - block.body.execution_payload.transactions = [opaque_tx_1, opaque_tx_2] - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state) + if is_post_eip7732(spec): + blob_kzg_commitments = spec.List[spec.KZGCommitment, spec.MAX_BLOB_COMMITMENTS_PER_BLOCK]( + blob_kzg_commitments_1 + blob_kzg_commitments_2 + ) + kzg_root = blob_kzg_commitments.hash_tree_root() + block.body.signed_execution_payload_header.message.blob_kzg_commitments_root = kzg_root + else: + block.body.blob_kzg_commitments = blob_kzg_commitments_1 + blob_kzg_commitments_2 + block.body.execution_payload.transactions = [opaque_tx_1, opaque_tx_2] + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state) blobs = blobs_1 + blobs_2 proofs = proofs_1 + proofs_2 signed_block = sign_block(spec, state, block, proposer_index=0) + if is_post_eip7732(spec): + return spec.get_blob_sidecars(signed_block, blobs, blob_kzg_commitments, proofs) return spec.get_blob_sidecars(signed_block, blobs, proofs) 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 index 74c418ad60..8efdf1fc36 100644 --- 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 @@ -4,7 +4,7 @@ from eth2spec.test.context import ( with_presets, spec_state_test, - with_electra_and_later, + with_electra_until_eip7732, ) from eth2spec.test.helpers.execution_payload import ( compute_el_block_hash_for_block, @@ -26,7 +26,8 @@ ) -@with_electra_and_later +# TODO(jtraglia): In eip7732, how do we set execution requests in the payload envelope? +@with_electra_until_eip7732 @spec_state_test @with_presets([MINIMAL], reason="too slow") def test_new_validator_deposit_with_multiple_epoch_transitions(spec, state): 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 bad569b4b7..64bb633369 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 @@ -3,7 +3,7 @@ ) from eth2spec.test.context import ( spec_state_test, - with_electra_and_later, + with_electra_until_eip7732, ) from eth2spec.test.helpers.bls_to_execution_changes import ( get_signed_address_change, @@ -26,7 +26,7 @@ ) -@with_electra_and_later +@with_electra_until_eip7732 @spec_state_test def test_basic_el_withdrawal_request(spec, state): # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit @@ -55,7 +55,7 @@ def test_basic_el_withdrawal_request(spec, state): assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH -@with_electra_and_later +@with_electra_until_eip7732 @spec_state_test def test_basic_btec_and_el_withdrawal_request_in_same_block(spec, state): # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit @@ -98,7 +98,7 @@ def test_basic_btec_and_el_withdrawal_request_in_same_block(spec, state): assert is_execution_address and is_correct_source_address -@with_electra_and_later +@with_electra_until_eip7732 @spec_state_test def test_basic_btec_before_el_withdrawal_request(spec, state): # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit @@ -145,7 +145,7 @@ def test_basic_btec_before_el_withdrawal_request(spec, state): assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH -@with_electra_and_later +@with_electra_until_eip7732 @spec_state_test def test_cl_exit_and_el_withdrawal_request_in_same_block(spec, state): # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit @@ -178,7 +178,7 @@ def test_cl_exit_and_el_withdrawal_request_in_same_block(spec, state): assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH -@with_electra_and_later +@with_electra_until_eip7732 @spec_state_test def test_multiple_el_partial_withdrawal_requests_same_validator(spec, state): # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit @@ -216,7 +216,7 @@ def test_multiple_el_partial_withdrawal_requests_same_validator(spec, state): assert state.validators[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH -@with_electra_and_later +@with_electra_until_eip7732 @spec_state_test def test_multiple_el_partial_withdrawal_requests_different_validator(spec, state): # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit @@ -256,7 +256,7 @@ def test_multiple_el_partial_withdrawal_requests_different_validator(spec, state assert state.validators[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH -@with_electra_and_later +@with_electra_until_eip7732 @spec_state_test def test_withdrawal_and_withdrawal_request_same_validator(spec, state): # Give a validator an excess balance @@ -293,7 +293,7 @@ def test_withdrawal_and_withdrawal_request_same_validator(spec, state): assert len(state.pending_partial_withdrawals) == 0 -@with_electra_and_later +@with_electra_until_eip7732 @spec_state_test def test_withdrawal_and_switch_to_compounding_request_same_validator(spec, state): # Give a validator an excess balance @@ -332,7 +332,7 @@ def test_withdrawal_and_switch_to_compounding_request_same_validator(spec, state assert len(state.pending_deposits) == 0 -@with_electra_and_later +@with_electra_until_eip7732 @spec_state_test def test_deposit_request_with_same_pubkey_different_withdrawal_credentials(spec, state): # signify the eth1 bridge deprecation diff --git a/tests/core/pyspec/eth2spec/test/helpers/attestations.py b/tests/core/pyspec/eth2spec/test/helpers/attestations.py index 0774ba8333..b31fd36a3e 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/attestations.py +++ b/tests/core/pyspec/eth2spec/test/helpers/attestations.py @@ -3,7 +3,11 @@ from typing import List from eth2spec.test.context import expect_assertion_error -from eth2spec.test.helpers.state import state_transition_and_sign_block, next_epoch, next_slot +from eth2spec.test.helpers.state import ( + payload_state_transition_no_store, + state_transition_and_sign_block, + next_epoch, next_slot, +) from eth2spec.test.helpers.block import build_empty_block_for_next_slot from eth2spec.test.helpers.forks import is_post_altair, is_post_deneb, is_post_electra from eth2spec.test.helpers.keys import privkeys @@ -262,6 +266,7 @@ def next_slots_with_attestations(spec, participation_fn, ) signed_blocks.append(signed_block) + payload_state_transition_no_store(spec, post_state, signed_block.message) return state, signed_blocks, post_state diff --git a/tests/core/pyspec/eth2spec/test/helpers/constants.py b/tests/core/pyspec/eth2spec/test/helpers/constants.py index 7c592b6692..c5bf965461 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/constants.py +++ b/tests/core/pyspec/eth2spec/test/helpers/constants.py @@ -35,15 +35,16 @@ # Formal forks *MAINNET_FORKS, ELECTRA, - # Experimental patches FULU, + # Experimental patches + EIP7732, ) # The forks that have light client specs LIGHT_CLIENT_TESTING_FORKS = (*[item for item in MAINNET_FORKS if item != PHASE0], ELECTRA) # The forks that output to the test vectors. -TESTGEN_FORKS = (*MAINNET_FORKS, ELECTRA, FULU, EIP7441) +TESTGEN_FORKS = (*MAINNET_FORKS, ELECTRA, FULU, EIP7441, EIP7732) # Forks allowed in the test runner `--fork` flag, to fail fast in case of typos -ALLOWED_TEST_RUNNER_FORKS = (*ALL_PHASES, EIP7441, EIP7732) +ALLOWED_TEST_RUNNER_FORKS = (*ALL_PHASES, EIP7441) # NOTE: the same definition as in `pysetup/md_doc_paths.py` PREVIOUS_FORK_OF = { diff --git a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py index 80684b9e6e..6f01b8132e 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py @@ -16,17 +16,15 @@ ) -def get_execution_payload_header(spec, execution_payload): +def get_execution_payload_header(spec, state, execution_payload): if is_post_eip7732(spec): return spec.ExecutionPayloadHeader( parent_block_hash=execution_payload.parent_hash, - parent_block_root=spec.Root(), # TODO: Fix this + parent_block_root=state.latest_block_header.hash_tree_root(), block_hash=execution_payload.block_hash, gas_limit=execution_payload.gas_limit, - builder_index=spec.ValidatorIndex(0), # TODO: Fix this - slot=spec.Slot(0), # TODO: Fix this - value=spec.Gwei(0), # TODO: Fix this - blob_kzg_commitments_root=spec.Root() # TODO: Fix this + slot=state.slot, + blob_kzg_commitments_root=state.latest_execution_payload_header.blob_kzg_commitments_root, ) payload_header = spec.ExecutionPayloadHeader( @@ -224,7 +222,7 @@ def compute_el_block_hash_with_new_fields(spec, payload, parent_beacon_block_roo if not is_post_deneb(spec): parent_beacon_block_root = None - payload_header = get_execution_payload_header(spec, payload) + payload_header = get_execution_payload_header(spec, spec.BeaconState(), payload) return compute_el_header_block_hash( spec, @@ -267,15 +265,22 @@ def build_empty_post_eip7732_execution_payload_header(spec, state): if not is_post_eip7732(spec): return parent_block_root = hash_tree_root(state.latest_block_header) + kzg_list = spec.List[spec.KZGCommitment, spec.MAX_BLOB_COMMITMENTS_PER_BLOCK]() + epoch = spec.get_current_epoch(state) + builder_index = None + for index in spec.get_active_validator_indices(state, epoch): + if not state.validators[index].slashed: + builder_index = index + assert builder_index is not None return spec.ExecutionPayloadHeader( parent_block_hash=state.latest_block_hash, parent_block_root=parent_block_root, block_hash=spec.Hash32(), gas_limit=spec.uint64(0), - builder_index=spec.ValidatorIndex(0), + builder_index=builder_index, slot=state.slot, value=spec.Gwei(0), - blob_kzg_commitments_root=spec.Root() + blob_kzg_commitments_root=kzg_list.hash_tree_root(), ) @@ -283,7 +288,7 @@ def build_empty_signed_execution_payload_header(spec, state): if not is_post_eip7732(spec): return message = build_empty_post_eip7732_execution_payload_header(spec, state) - privkey = privkeys[0] + privkey = privkeys[message.builder_index] signature = spec.get_execution_payload_header_signature(state, message, privkey) return spec.SignedExecutionPayloadHeader( message=message, @@ -309,6 +314,7 @@ def build_empty_execution_payload(spec, state, randao_mix=None): logs_bloom=spec.ByteVector[spec.BYTES_PER_LOGS_BLOOM](), # TODO: zeroed logs bloom for empty logs ok? prev_randao=randao_mix, gas_used=0, # empty block, 0 gas + gas_limit=latest.gas_limit, timestamp=timestamp, extra_data=spec.ByteList[spec.MAX_EXTRA_DATA_BYTES](), transactions=empty_txs, @@ -358,7 +364,12 @@ def build_randomized_execution_payload(spec, state, rng): def build_state_with_incomplete_transition(spec, state): - state = build_state_with_execution_payload_header(spec, state, spec.ExecutionPayloadHeader()) + header = spec.ExecutionPayloadHeader() + if is_post_eip7732(spec): + kzgs = spec.List[spec.KZGCommitment, spec.MAX_BLOB_COMMITMENTS_PER_BLOCK]() + header.blob_kzg_commitments_root = kzgs.hash_tree_root() + + state = build_state_with_execution_payload_header(spec, state, header) assert not spec.is_merge_transition_complete(state) return state @@ -366,7 +377,7 @@ def build_state_with_incomplete_transition(spec, state): def build_state_with_complete_transition(spec, state): pre_state_payload = build_empty_execution_payload(spec, state) - payload_header = get_execution_payload_header(spec, pre_state_payload) + payload_header = get_execution_payload_header(spec, state, pre_state_payload) state = build_state_with_execution_payload_header(spec, state, payload_header) assert spec.is_merge_transition_complete(state) @@ -377,7 +388,6 @@ def build_state_with_complete_transition(spec, state): def build_state_with_execution_payload_header(spec, state, execution_payload_header): pre_state = state.copy() pre_state.latest_execution_payload_header = execution_payload_header - return pre_state diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py index 33e8535502..8dfe1d1ef5 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py @@ -2,11 +2,24 @@ from eth_utils import encode_hex from eth2spec.test.exceptions import BlockNotFoundException +from eth2spec.test.helpers.forks import is_post_eip7732 from eth2spec.test.helpers.attestations import ( next_epoch_with_attestations, next_slots_with_attestations, state_transition_with_full_block, ) +from eth2spec.test.helpers.state import ( + payload_state_transition, + payload_state_transition_no_store, +) + + +def check_head_against_root(spec, store, root): + head = spec.get_head(store) + if is_post_eip7732(spec): + assert head.root == root + else: + assert head == root class BlobData(NamedTuple): @@ -53,7 +66,7 @@ def get_anchor_root(spec, state): def tick_and_add_block(spec, store, signed_block, test_steps, valid=True, merge_block=False, block_not_found=False, is_optimistic=False, blob_data=None): - pre_state = store.block_states[signed_block.message.parent_root] + pre_state = get_store_full_state(spec, store, signed_block.message.parent_root) if merge_block: assert spec.is_merge_transition_block(pre_state, signed_block.message.body) @@ -121,7 +134,12 @@ def get_genesis_forkchoice_store(spec, genesis_state): def get_genesis_forkchoice_store_and_block(spec, genesis_state): assert genesis_state.slot == spec.GENESIS_SLOT genesis_block = spec.BeaconBlock(state_root=genesis_state.hash_tree_root()) - return spec.get_forkchoice_store(genesis_state, genesis_block), genesis_block + if is_post_eip7732(spec): + genesis_block.body.signed_execution_payload_header.message.block_hash = genesis_state.latest_block_hash + store = spec.get_forkchoice_store(genesis_state, genesis_block) + if is_post_eip7732(spec): + store.execution_payload_states = store.block_states.copy() + return store, genesis_block def get_block_file_name(block): @@ -160,7 +178,14 @@ def run_on_block(spec, store, signed_block, valid=True): assert False spec.on_block(store, signed_block) - assert store.blocks[signed_block.message.hash_tree_root()] == signed_block.message + root = signed_block.message.hash_tree_root() + assert store.blocks[root] == signed_block.message + + +def get_store_full_state(spec, store, root): + if is_post_eip7732(spec): + return store.execution_payload_states[root] + return store.block_states[root] def add_block(spec, @@ -267,6 +292,12 @@ def add_attester_slashing(spec, store, attester_slashing, test_steps, valid=True def get_formatted_head_output(spec, store): head = spec.get_head(store) + if is_post_eip7732(spec): + return { + 'slot': int(head.slot), + 'root': encode_hex(head.root), + } + slot = store.blocks[head].slot return { 'slot': int(slot), @@ -315,11 +346,15 @@ def apply_next_epoch_with_attestations(spec, for signed_block in new_signed_blocks: block = signed_block.message yield from tick_and_add_block(spec, store, signed_block, test_steps) + payload_state_transition(spec, store, signed_block.message) block_root = block.hash_tree_root() assert store.blocks[block_root] == block last_signed_block = signed_block - assert store.block_states[block_root].hash_tree_root() == post_state.hash_tree_root() + if is_post_eip7732(spec): + assert store.execution_payload_states[block_root].hash_tree_root() == post_state.hash_tree_root() + else: + assert store.block_states[block_root].hash_tree_root() == post_state.hash_tree_root() return post_state, store, last_signed_block @@ -337,11 +372,15 @@ def apply_next_slots_with_attestations(spec, for signed_block in new_signed_blocks: block = signed_block.message yield from tick_and_add_block(spec, store, signed_block, test_steps) + payload_state_transition(spec, store, signed_block.message) block_root = block.hash_tree_root() assert store.blocks[block_root] == block last_signed_block = signed_block - assert store.block_states[block_root].hash_tree_root() == post_state.hash_tree_root() + if is_post_eip7732(spec): + assert store.execution_payload_states[block_root].hash_tree_root() == post_state.hash_tree_root() + else: + assert store.block_states[block_root].hash_tree_root() == post_state.hash_tree_root() return post_state, store, last_signed_block @@ -373,6 +412,8 @@ def find_next_justifying_slot(spec, participation_fn, ) signed_blocks.append(signed_block) + payload_state_transition_no_store(spec, temp_state, signed_block.message) + if is_ready_to_justify(spec, temp_state): justifying_slot = temp_state.slot diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_transition.py b/tests/core/pyspec/eth2spec/test/helpers/fork_transition.py index fa900a656b..b0fe953702 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_transition.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_transition.py @@ -33,6 +33,7 @@ get_next_fork_transition, is_post_bellatrix, is_post_electra, + is_post_eip7732, ) from eth2spec.test.helpers.state import ( next_slot, @@ -48,6 +49,10 @@ from eth2spec.test.helpers.consolidations import ( prepare_switch_to_compounding_request, ) +from eth2spec.test.helpers.execution_payload import ( + build_empty_execution_payload, + compute_el_block_hash, +) class OperationType(Enum): @@ -61,14 +66,19 @@ class OperationType(Enum): CONSOLIDATION_REQUEST = auto() -def _set_operations_by_dict(spec, block, operation_dict): +# TODO(jtraglia): Pretty sure this doesn't play well with eip7732. Needs some work. +def _set_operations_by_dict(spec, block, operation_dict, state): for key, value in operation_dict.items(): # to handle e.g. `execution_requests.deposits` and `deposits` obj = block.body for attr in key.split('.')[:-1]: obj = getattr(obj, attr) setattr(obj, key.split('.')[-1], value) - if is_post_bellatrix(spec): + if is_post_eip7732(spec): + payload = build_empty_execution_payload(spec, state) + block.body.signed_execution_payload_header.message.block_hash = compute_el_block_hash( + spec, payload, state) + elif is_post_bellatrix(spec): block.body.execution_payload.block_hash = compute_el_block_hash_for_block(spec, block) @@ -93,7 +103,7 @@ def _state_transition_and_sign_block_at_slot(spec, block.body.sync_aggregate = sync_aggregate if operation_dict: - _set_operations_by_dict(spec, block, operation_dict) + _set_operations_by_dict(spec, block, operation_dict, state) assert state.latest_block_header.slot < block.slot assert state.slot == block.slot @@ -409,7 +419,7 @@ def _check_state(): if is_right_before_fork: # add a block with operation. block = build_empty_block_for_next_slot(spec, state) - _set_operations_by_dict(spec, block, operation_dict) + _set_operations_by_dict(spec, block, operation_dict, state) signed_block = state_transition_and_sign_block(spec, state, block) blocks.append(pre_tag(signed_block)) diff --git a/tests/core/pyspec/eth2spec/test/helpers/genesis.py b/tests/core/pyspec/eth2spec/test/helpers/genesis.py index 0d9d9c579f..55e999879f 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/genesis.py +++ b/tests/core/pyspec/eth2spec/test/helpers/genesis.py @@ -7,7 +7,8 @@ compute_el_header_block_hash, ) from eth2spec.test.helpers.forks import ( - is_post_altair, is_post_bellatrix, is_post_capella, is_post_deneb, is_post_electra, is_post_eip7441, + is_post_altair, is_post_bellatrix, is_post_capella, is_post_deneb, + is_post_electra, is_post_eip7441, is_post_eip7732, ) from eth2spec.test.helpers.keys import pubkeys from eth2spec.test.helpers.eip7441 import ( @@ -49,10 +50,25 @@ def build_mock_validator(spec, i: int, balance: int): return validator -def get_sample_genesis_execution_payload_header(spec, +def get_post_eip7732_genesis_execution_payload_header(spec, slot, eth1_block_hash): + kzgs = spec.List[spec.KZGCommitment, spec.MAX_BLOB_COMMITMENTS_PER_BLOCK]() + header = spec.ExecutionPayloadHeader( + parent_block_hash=b'\x30' * 32, + parent_block_root=b'\x00' * 32, + block_hash=eth1_block_hash, + gas_limit=30000000, + slot=slot, + blob_kzg_commitments_root=kzgs.hash_tree_root(), + ) + return header + + +def get_sample_genesis_execution_payload_header(spec, slot, eth1_block_hash=None): if eth1_block_hash is None: eth1_block_hash = b'\x55' * 32 + if is_post_eip7732(spec): + return get_post_eip7732_genesis_execution_payload_header(spec, slot, eth1_block_hash) payload_header = spec.ExecutionPayloadHeader( parent_hash=b'\x30' * 32, fee_recipient=b'\x42' * 20, @@ -105,6 +121,10 @@ def create_genesis_state(spec, validator_balances, activation_threshold): previous_version = getattr(spec.config, f"{previous_fork.upper()}_FORK_VERSION") current_version = getattr(spec.config, f"{spec.fork.upper()}_FORK_VERSION") + genesis_block_body = spec.BeaconBlockBody() + if is_post_eip7732(spec): + genesis_block_body.signed_execution_payload_header.message.block_hash = eth1_block_hash + state = spec.BeaconState( genesis_time=0, eth1_deposit_index=len(validator_balances), @@ -118,7 +138,7 @@ def create_genesis_state(spec, validator_balances, activation_threshold): current_version=current_version, epoch=spec.GENESIS_EPOCH, ), - latest_block_header=spec.BeaconBlockHeader(body_root=spec.hash_tree_root(spec.BeaconBlockBody())), + latest_block_header=spec.BeaconBlockHeader(body_root=spec.hash_tree_root(genesis_block_body)), randao_mixes=[eth1_block_hash] * spec.EPOCHS_PER_HISTORICAL_VECTOR, ) @@ -150,6 +170,7 @@ def create_genesis_state(spec, validator_balances, activation_threshold): # Initialize the execution payload header (with block number and genesis time set to 0) state.latest_execution_payload_header = get_sample_genesis_execution_payload_header( spec, + spec.compute_start_slot_at_epoch(spec.GENESIS_EPOCH), eth1_block_hash=eth1_block_hash, ) @@ -178,4 +199,9 @@ def create_genesis_state(spec, validator_balances, activation_threshold): state.pending_partial_withdrawals = [] state.pending_consolidations = [] + if is_post_eip7732(spec): + withdrawals = spec.List[spec.Withdrawal, spec.MAX_WITHDRAWALS_PER_PAYLOAD]() + state.latest_withdrawals_root = withdrawals.hash_tree_root() + state.latest_block_hash = state.latest_execution_payload_header.block_hash # last block is full + return state diff --git a/tests/core/pyspec/eth2spec/test/helpers/state.py b/tests/core/pyspec/eth2spec/test/helpers/state.py index 72fca0b14f..d1524fd940 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/state.py +++ b/tests/core/pyspec/eth2spec/test/helpers/state.py @@ -1,6 +1,6 @@ from eth2spec.test.context import expect_assertion_error from eth2spec.test.helpers.block import apply_empty_block, sign_block, transition_unsigned_block -from eth2spec.test.helpers.forks import is_post_altair +from eth2spec.test.helpers.forks import is_post_altair, is_post_eip7732 from eth2spec.test.helpers.voluntary_exits import get_unslashed_exited_validators @@ -91,6 +91,27 @@ def get_state_root(spec, state, slot) -> bytes: return state.state_roots[slot % spec.SLOTS_PER_HISTORICAL_ROOT] +def payload_state_transition_no_store(spec, state, block): + if is_post_eip7732(spec): + # cache the latest block header + previous_state_root = state.hash_tree_root() + if state.latest_block_header.state_root == spec.Root(): + state.latest_block_header.state_root = previous_state_root + # also perform the state transition as if the payload was revealed + state.latest_block_hash = block.body.signed_execution_payload_header.message.block_hash + state.latest_full_slot = block.slot + return state + + +def payload_state_transition(spec, store, block): + root = block.hash_tree_root() + state = store.block_states[root].copy() + if is_post_eip7732(spec): + payload_state_transition_no_store(spec, state, block) + store.execution_payload_states[root] = state + return state + + def state_transition_and_sign_block(spec, state, block, expect_fail=False): """ State transition via the provided ``block`` diff --git a/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py b/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py index 1c9dec0060..a30f56690e 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py +++ b/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py @@ -1,4 +1,5 @@ from eth2spec.test.helpers.forks import is_post_electra +from eth2spec.test.helpers.forks import is_post_eip7732 def get_expected_withdrawals(spec, state): @@ -202,7 +203,10 @@ def run_withdrawals_processing(spec, state, execution_payload, num_expected_with if not valid: try: - spec.process_withdrawals(state, execution_payload) + if is_post_eip7732(spec): + spec.process_withdrawals(state) + else: + spec.process_withdrawals(state, execution_payload) raise AssertionError('expected an assertion error, but got none.') except AssertionError: pass @@ -210,7 +214,10 @@ def run_withdrawals_processing(spec, state, execution_payload, num_expected_with yield 'post', None return - spec.process_withdrawals(state, execution_payload) + if is_post_eip7732(spec): + spec.process_withdrawals(state) + else: + spec.process_withdrawals(state, execution_payload) yield 'post', state diff --git a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_block_header.py b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_block_header.py index a1f1e09a0b..1679206638 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_block_header.py +++ b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_block_header.py @@ -4,7 +4,12 @@ from eth2spec.test.helpers.block import build_empty_block_for_next_slot from eth2spec.test.helpers.execution_payload import compute_el_block_hash_for_block from eth2spec.test.helpers.forks import is_post_bellatrix +from eth2spec.test.helpers.forks import is_post_eip7732 from eth2spec.test.helpers.state import next_slot +from eth2spec.test.helpers.execution_payload import ( + build_empty_execution_payload, + compute_el_block_hash, +) def prepare_state_for_header_processing(spec, state): @@ -67,7 +72,11 @@ def test_invalid_proposer_index(spec, state): def test_invalid_parent_root(spec, state): block = build_empty_block_for_next_slot(spec, state) block.parent_root = b'\12' * 32 # invalid prev root - if is_post_bellatrix(spec): + if is_post_eip7732(spec): + payload = build_empty_execution_payload(spec, state) + block.body.signed_execution_payload_header.message.block_hash = compute_el_block_hash( + spec, payload, state) + elif is_post_bellatrix(spec): block.body.execution_payload.block_hash = compute_el_block_hash_for_block(spec, block) yield from run_block_header_processing(spec, state, block, valid=False) @@ -85,7 +94,11 @@ def test_invalid_multiple_blocks_single_slot(spec, state): child_block = block.copy() child_block.parent_root = block.hash_tree_root() - if is_post_bellatrix(spec): + if is_post_eip7732(spec): + payload = build_empty_execution_payload(spec, state) + child_block.body.signed_execution_payload_header.message.block_hash = compute_el_block_hash( + spec, payload, state) + elif is_post_bellatrix(spec): child_block.body.execution_payload.block_hash = compute_el_block_hash_for_block(spec, child_block) yield from run_block_header_processing(spec, state, child_block, prepare_state=False, valid=False) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_ex_ante.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_ex_ante.py index 9978f7d46c..4cf83ac07c 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_ex_ante.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_ex_ante.py @@ -11,7 +11,9 @@ build_empty_block, ) from eth2spec.test.helpers.constants import MAINNET +from eth2spec.test.helpers.forks import is_post_eip7732 from eth2spec.test.helpers.fork_choice import ( + check_head_against_root, get_genesis_forkchoice_store_and_block, on_tick_and_append_step, add_attestation, @@ -20,6 +22,7 @@ ) from eth2spec.test.helpers.state import ( state_transition_and_sign_block, + payload_state_transition, ) @@ -28,7 +31,13 @@ def _apply_base_block_a(spec, state, store, test_steps): block = build_empty_block(spec, state, slot=state.slot + 1) signed_block_a = state_transition_and_sign_block(spec, state, block) yield from tick_and_add_block(spec, store, signed_block_a, test_steps) - assert spec.get_head(store) == signed_block_a.message.hash_tree_root() + payload_state_transition(spec, store, signed_block_a.message) + head = spec.get_head(store) + expected_root = signed_block_a.message.hash_tree_root() + if is_post_eip7732(spec): + assert head.root == expected_root + else: + check_head_against_root(spec, store, signed_block_a.message.hash_tree_root()) @with_altair_and_later @@ -85,15 +94,17 @@ def _filter_participant_set(participants): time = state_c.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time on_tick_and_append_step(spec, store, time, test_steps) yield from add_block(spec, store, signed_block_c, test_steps) - assert spec.get_head(store) == signed_block_c.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_c.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block_c.message) # Block B received at N+2 — C is head due to proposer score boost yield from add_block(spec, store, signed_block_b, test_steps) - assert spec.get_head(store) == signed_block_c.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_c.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block_b.message) # Attestation_1 received at N+2 — C is head yield from add_attestation(spec, store, attestation, test_steps) - assert spec.get_head(store) == signed_block_c.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_c.message.hash_tree_root()) yield 'steps', test_steps @@ -162,11 +173,13 @@ def test_ex_ante_attestations_is_greater_than_proposer_boost_with_boost(spec, st time = state_c.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time on_tick_and_append_step(spec, store, time, test_steps) yield from add_block(spec, store, signed_block_c, test_steps) - assert spec.get_head(store) == signed_block_c.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_c.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block_c.message) # Block B received at N+2 — C is head due to proposer score boost yield from add_block(spec, store, signed_block_b, test_steps) - assert spec.get_head(store) == signed_block_c.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_c.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block_c.message) # Attestation_set_1 at slot `N + 1` voting for block B proposer_boost_root = signed_block_b.message.hash_tree_root() @@ -186,7 +199,7 @@ def _filter_participant_set(participants): # Attestation_set_1 received at N+2 — B is head because B's attestation_score > C's proposer_score. # (B's proposer_score = C's attestation_score = 0) yield from add_attestation(spec, store, attestation, test_steps) - assert spec.get_head(store) == signed_block_b.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_b.message.hash_tree_root()) yield 'steps', test_steps @@ -239,17 +252,20 @@ def test_ex_ante_sandwich_without_attestations(spec, state): time = state_c.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time on_tick_and_append_step(spec, store, time, test_steps) yield from add_block(spec, store, signed_block_c, test_steps) - assert spec.get_head(store) == signed_block_c.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_c.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block_c.message) # Block B received at N+2 — C is head, it has proposer score boost yield from add_block(spec, store, signed_block_b, test_steps) - assert spec.get_head(store) == signed_block_c.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_c.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block_b.message) # Block D received at N+3 - D is head, it has proposer score boost time = state_d.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time on_tick_and_append_step(spec, store, time, test_steps) yield from add_block(spec, store, signed_block_d, test_steps) - assert spec.get_head(store) == signed_block_d.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_d.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block_d.message) yield 'steps', test_steps @@ -316,21 +332,24 @@ def _filter_participant_set(participants): time = state_c.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time on_tick_and_append_step(spec, store, time, test_steps) yield from add_block(spec, store, signed_block_c, test_steps) - assert spec.get_head(store) == signed_block_c.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_c.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block_c.message) # Block B received at N+2 — C is head, it has proposer score boost yield from add_block(spec, store, signed_block_b, test_steps) - assert spec.get_head(store) == signed_block_c.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_c.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block_b.message) # Attestation_1 received at N+3 — C is head time = state_d.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time on_tick_and_append_step(spec, store, time, test_steps) yield from add_attestation(spec, store, attestation, test_steps) - assert spec.get_head(store) == signed_block_c.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_c.message.hash_tree_root()) # Block D received at N+3 - D is head, it has proposer score boost yield from add_block(spec, store, signed_block_d, test_steps) - assert spec.get_head(store) == signed_block_d.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_d.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block_d.message) yield 'steps', test_steps @@ -386,11 +405,13 @@ def test_ex_ante_sandwich_with_boost_not_sufficient(spec, state): time = state_c.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time on_tick_and_append_step(spec, store, time, test_steps) yield from add_block(spec, store, signed_block_c, test_steps) - assert spec.get_head(store) == signed_block_c.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_c.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block_c.message) # Block B received at N+2 — C is head, it has proposer score boost yield from add_block(spec, store, signed_block_b, test_steps) - assert spec.get_head(store) == signed_block_c.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_c.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block_b.message) # Attestation_set_1 at N+2 voting for block C proposer_boost_root = signed_block_c.message.hash_tree_root() @@ -412,10 +433,11 @@ def _filter_participant_set(participants): time = state_d.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time on_tick_and_append_step(spec, store, time, test_steps) yield from add_attestation(spec, store, attestation, test_steps) - assert spec.get_head(store) == signed_block_c.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_c.message.hash_tree_root()) # Block D received at N+3 - C is head, D's boost not sufficient! yield from add_block(spec, store, signed_block_d, test_steps) - assert spec.get_head(store) == signed_block_c.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_c.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block_d.message) yield 'steps', test_steps diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_get_head.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_get_head.py index 886fcbd209..62a378ab8e 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_get_head.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_get_head.py @@ -1,8 +1,8 @@ import random - from eth2spec.test.context import ( spec_state_test, with_altair_and_later, + with_altair_until_eip7732, with_presets, ) from eth2spec.test.helpers.attestations import get_valid_attestation, next_epoch_with_attestations @@ -14,6 +14,7 @@ from eth2spec.test.helpers.fork_choice import ( add_attester_slashing, add_block, + check_head_against_root, get_anchor_root, get_genesis_forkchoice_store_and_block, get_formatted_head_output, @@ -26,10 +27,12 @@ ) from eth2spec.test.helpers.forks import ( is_post_altair, + is_post_eip7732, ) from eth2spec.test.helpers.state import ( next_slots, next_epoch, + payload_state_transition, state_transition_and_sign_block, ) @@ -44,7 +47,8 @@ def test_genesis(spec, state): yield 'anchor_block', anchor_block anchor_root = get_anchor_root(spec, state) - assert spec.get_head(store) == anchor_root + check_head_against_root(spec, store, anchor_root) + test_steps.append({ 'checks': { 'genesis_time': int(store.genesis_time), @@ -68,20 +72,21 @@ def test_chain_no_attestations(spec, state): yield 'anchor_block', anchor_block anchor_root = get_anchor_root(spec, state) - assert spec.get_head(store) == anchor_root + check_head_against_root(spec, store, anchor_root) output_head_check(spec, store, test_steps) # On receiving a block of `GENESIS_SLOT + 1` slot block_1 = build_empty_block_for_next_slot(spec, state) signed_block_1 = state_transition_and_sign_block(spec, state, block_1) yield from tick_and_add_block(spec, store, signed_block_1, test_steps) + payload_state_transition(spec, store, signed_block_1.message) # On receiving a block of next epoch block_2 = build_empty_block_for_next_slot(spec, state) signed_block_2 = state_transition_and_sign_block(spec, state, block_2) yield from tick_and_add_block(spec, store, signed_block_2, test_steps) - - assert spec.get_head(store) == spec.hash_tree_root(block_2) + check_head_against_root(spec, store, spec.hash_tree_root(block_2)) + payload_state_transition(spec, store, signed_block_2.message) output_head_check(spec, store, test_steps) yield 'steps', test_steps @@ -98,7 +103,7 @@ def test_split_tie_breaker_no_attestations(spec, state): yield 'anchor_state', state yield 'anchor_block', anchor_block anchor_root = get_anchor_root(spec, state) - assert spec.get_head(store) == anchor_root + check_head_against_root(spec, store, anchor_root) output_head_check(spec, store, test_steps) # Create block at slot 1 @@ -117,10 +122,12 @@ def test_split_tie_breaker_no_attestations(spec, state): on_tick_and_append_step(spec, store, time, test_steps) yield from add_block(spec, store, signed_block_1, test_steps) + payload_state_transition(spec, store, signed_block_1.message) yield from add_block(spec, store, signed_block_2, test_steps) + payload_state_transition(spec, store, signed_block_2.message) highest_root = max(spec.hash_tree_root(block_1), spec.hash_tree_root(block_2)) - assert spec.get_head(store) == highest_root + check_head_against_root(spec, store, highest_root) output_head_check(spec, store, test_steps) yield 'steps', test_steps @@ -137,7 +144,7 @@ def test_shorter_chain_but_heavier_weight(spec, state): yield 'anchor_state', state yield 'anchor_block', anchor_block anchor_root = get_anchor_root(spec, state) - assert spec.get_head(store) == anchor_root + check_head_against_root(spec, store, anchor_root) output_head_check(spec, store, test_steps) # build longer tree @@ -146,6 +153,7 @@ def test_shorter_chain_but_heavier_weight(spec, state): long_block = build_empty_block_for_next_slot(spec, long_state) signed_long_block = state_transition_and_sign_block(spec, long_state, long_block) yield from tick_and_add_block(spec, store, signed_long_block, test_steps) + payload_state_transition(spec, store, signed_long_block.message) # build short tree short_state = genesis_state.copy() @@ -153,14 +161,15 @@ def test_shorter_chain_but_heavier_weight(spec, state): short_block.body.graffiti = b'\x42' * 32 signed_short_block = state_transition_and_sign_block(spec, short_state, short_block) yield from tick_and_add_block(spec, store, signed_short_block, test_steps) + payload_state_transition(spec, store, signed_short_block.message) # Since the long chain has higher proposer_score at slot 1, the latest long block is the head - assert spec.get_head(store) == spec.hash_tree_root(long_block) + check_head_against_root(spec, store, spec.hash_tree_root(long_block)) short_attestation = get_valid_attestation(spec, short_state, short_block.slot, signed=True) yield from tick_and_run_on_attestation(spec, store, short_attestation, test_steps) - assert spec.get_head(store) == spec.hash_tree_root(short_block) + check_head_against_root(spec, store, spec.hash_tree_root(short_block)) output_head_check(spec, store, test_steps) yield 'steps', test_steps @@ -176,7 +185,7 @@ def test_filtered_block_tree(spec, state): yield 'anchor_state', state yield 'anchor_block', anchor_block anchor_root = get_anchor_root(spec, state) - assert spec.get_head(store) == anchor_root + check_head_against_root(spec, store, anchor_root) output_head_check(spec, store, test_steps) # transition state past initial couple of epochs @@ -191,12 +200,13 @@ def test_filtered_block_tree(spec, state): on_tick_and_append_step(spec, store, current_time, test_steps) for signed_block in signed_blocks: yield from add_block(spec, store, signed_block, test_steps) + payload_state_transition(spec, store, signed_block.message) assert store.justified_checkpoint == state.current_justified_checkpoint # the last block in the branch should be the head expected_head_root = spec.hash_tree_root(signed_blocks[-1].message) - assert spec.get_head(store) == expected_head_root + check_head_against_root(spec, store, expected_head_root) output_head_check(spec, store, test_steps) # @@ -205,7 +215,10 @@ def test_filtered_block_tree(spec, state): # # build a chain without attestations off of previous justified block - non_viable_state = store.block_states[store.justified_checkpoint.root].copy() + if is_post_eip7732(spec): + non_viable_state = store.execution_payload_states[store.justified_checkpoint.root].copy() + else: + non_viable_state = store.block_states[store.justified_checkpoint.root].copy() # ensure that next wave of votes are for future epoch next_epoch(spec, non_viable_state) @@ -232,18 +245,20 @@ def test_filtered_block_tree(spec, state): # include rogue block and associated attestations in the store yield from add_block(spec, store, signed_rogue_block, test_steps) + payload_state_transition(spec, store, signed_rogue_block.message) for attestation in attestations: yield from tick_and_run_on_attestation(spec, store, attestation, test_steps) # ensure that get_head still returns the head from the previous branch - assert spec.get_head(store) == expected_head_root + check_head_against_root(spec, store, expected_head_root) output_head_check(spec, store, test_steps) yield 'steps', test_steps -@with_altair_and_later +# This test is skipped in EIP-7732 because the block's slot decides first on weight ties +@with_altair_until_eip7732 @spec_state_test def test_proposer_boost_correct_head(spec, state): test_steps = [] @@ -254,7 +269,7 @@ def test_proposer_boost_correct_head(spec, state): yield 'anchor_state', state yield 'anchor_block', anchor_block anchor_root = get_anchor_root(spec, state) - assert spec.get_head(store) == anchor_root + check_head_against_root(spec, store, anchor_root) output_head_check(spec, store, test_steps) # Build block that serves as head ONLY on timely arrival, and ONLY in that slot @@ -281,19 +296,19 @@ def test_proposer_boost_correct_head(spec, state): # Process block_2 yield from add_block(spec, store, signed_block_2, test_steps) assert store.proposer_boost_root == spec.Root() - assert spec.get_head(store) == spec.hash_tree_root(block_2) + check_head_against_root(spec, store, spec.hash_tree_root(block_2)) # Process block_1 on timely arrival # The head should temporarily change to block_1 yield from add_block(spec, store, signed_block_1, test_steps) assert store.proposer_boost_root == spec.hash_tree_root(block_1) - assert spec.get_head(store) == spec.hash_tree_root(block_1) + check_head_against_root(spec, store, spec.hash_tree_root(block_1)) # After block_1.slot, the head should revert to block_2 time = store.genesis_time + (block_1.slot + 1) * spec.config.SECONDS_PER_SLOT on_tick_and_append_step(spec, store, time, test_steps) assert store.proposer_boost_root == spec.Root() - assert spec.get_head(store) == spec.hash_tree_root(block_2) + check_head_against_root(spec, store, spec.hash_tree_root(block_2)) output_head_check(spec, store, test_steps) yield 'steps', test_steps @@ -310,12 +325,12 @@ def test_discard_equivocations_on_attester_slashing(spec, state): yield 'anchor_state', state yield 'anchor_block', anchor_block anchor_root = get_anchor_root(spec, state) - assert spec.get_head(store) == anchor_root + check_head_against_root(spec, store, anchor_root) output_head_check(spec, store, test_steps) # Build block that serves as head before discarding equivocations state_1 = genesis_state.copy() - next_slots(spec, state_1, 3) + next_slots(spec, state_1, 2) block_1 = build_empty_block_for_next_slot(spec, state_1) signed_block_1 = state_transition_and_sign_block(spec, state_1, block_1) @@ -334,7 +349,7 @@ def test_discard_equivocations_on_attester_slashing(spec, state): # Build block that serves as head after discarding equivocations state_2 = genesis_state.copy() - next_slots(spec, state_2, 2) + next_slots(spec, state_2, 3) block_2 = build_empty_block_for_next_slot(spec, state_2) signed_block_2 = state_transition_and_sign_block(spec, state_2.copy(), block_2) rng = random.Random(1001) @@ -347,26 +362,27 @@ def test_discard_equivocations_on_attester_slashing(spec, state): time = store.genesis_time + (block_eqv.slot + 2) * spec.config.SECONDS_PER_SLOT on_tick_and_append_step(spec, store, time, test_steps) - # Process block_2 - yield from add_block(spec, store, signed_block_2, test_steps) - assert store.proposer_boost_root == spec.Root() - assert spec.get_head(store) == spec.hash_tree_root(block_2) - # Process block_1 - # The head should remain block_2 yield from add_block(spec, store, signed_block_1, test_steps) + payload_state_transition(spec, store, signed_block_1.message) + assert store.proposer_boost_root == spec.Root() + check_head_against_root(spec, store, spec.hash_tree_root(block_1)) + + # Process block_2 head should switch to block_2 + yield from add_block(spec, store, signed_block_2, test_steps) + payload_state_transition(spec, store, signed_block_2.message) assert store.proposer_boost_root == spec.Root() - assert spec.get_head(store) == spec.hash_tree_root(block_2) + check_head_against_root(spec, store, spec.hash_tree_root(block_2)) # Process attestation # The head should change to block_1 yield from add_attestation(spec, store, attestation, test_steps) - assert spec.get_head(store) == spec.hash_tree_root(block_1) + check_head_against_root(spec, store, spec.hash_tree_root(block_1)) # Process attester_slashing # The head should revert to block_2 yield from add_attester_slashing(spec, store, attester_slashing, test_steps) - assert spec.get_head(store) == spec.hash_tree_root(block_2) + check_head_against_root(spec, store, spec.hash_tree_root(block_2)) output_head_check(spec, store, test_steps) yield 'steps', test_steps @@ -406,11 +422,15 @@ def test_discard_equivocations_slashed_validator_censoring(spec, state): anchor_state = state.copy() # Generate an anchor block with correct state root anchor_block = spec.BeaconBlock(state_root=anchor_state.hash_tree_root()) + if is_post_eip7732(spec): + anchor_block.body.signed_execution_payload_header.message.block_hash = anchor_state.latest_block_hash yield 'anchor_state', anchor_state yield 'anchor_block', anchor_block # Get a new store with the anchor state & anchor block store = spec.get_forkchoice_store(anchor_state, anchor_block) + if is_post_eip7732(spec): + store.execution_payload_states = store.block_states.copy() # Now generate the store checks current_time = anchor_state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time @@ -434,7 +454,9 @@ def test_discard_equivocations_slashed_validator_censoring(spec, state): # Add both blocks to the store yield from tick_and_add_block(spec, store, signed_block_1, test_steps) + payload_state_transition(spec, store, signed_block_1.message) yield from tick_and_add_block(spec, store, signed_block_2, test_steps) + payload_state_transition(spec, store, signed_block_2.message) # Find out which block will win in tie breaking if spec.hash_tree_root(block_1) < spec.hash_tree_root(block_2): @@ -452,7 +474,7 @@ def test_discard_equivocations_slashed_validator_censoring(spec, state): on_tick_and_append_step(spec, store, current_time, test_steps) # Check that block with higher root wins - assert spec.get_head(store) == block_high_root + check_head_against_root(spec, store, block_high_root) # Create attestation for block with lower root attestation = get_valid_attestation(spec, block_low_root_post_state, slot=eqv_slot, index=0, signed=True) @@ -463,7 +485,7 @@ def test_discard_equivocations_slashed_validator_censoring(spec, state): # Add attestation to the store yield from add_attestation(spec, store, attestation, test_steps) # Check that block with higher root still wins - assert spec.get_head(store) == block_high_root + check_head_against_root(spec, store, block_high_root) output_head_check(spec, store, test_steps) yield 'steps', test_steps @@ -524,6 +546,7 @@ def test_voting_source_within_two_epoch(spec, state): # Now add the fork to the store for signed_block in signed_blocks: yield from tick_and_add_block(spec, store, signed_block, test_steps) + payload_state_transition(spec, store, signed_block.message) assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 5 assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 4 assert store.finalized_checkpoint.epoch == 3 @@ -540,7 +563,7 @@ def test_voting_source_within_two_epoch(spec, state): last_fork_block_root, store.finalized_checkpoint.epoch ) - assert spec.get_head(store) == last_fork_block_root + check_head_against_root(spec, store, last_fork_block_root) yield 'steps', test_steps @@ -602,10 +625,13 @@ def test_voting_source_beyond_two_epoch(spec, state): # Store the head before adding the fork to the store correct_head = spec.get_head(store) + if is_post_eip7732(spec): + correct_head = correct_head.root # Now add the fork to the store for signed_block in signed_blocks: yield from tick_and_add_block(spec, store, signed_block, test_steps) + payload_state_transition(spec, store, signed_block.message) assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 6 assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 5 assert store.finalized_checkpoint.epoch == 4 @@ -624,7 +650,7 @@ def test_voting_source_beyond_two_epoch(spec, state): last_fork_block_root, store.finalized_checkpoint.epoch ) - assert spec.get_head(store) == correct_head + check_head_against_root(spec, store, correct_head) yield 'steps', test_steps @@ -703,6 +729,7 @@ def test_incorrect_finalized(spec, state): # Now add the fork to the store for signed_block in signed_blocks: yield from tick_and_add_block(spec, store, signed_block, test_steps) + payload_state_transition(spec, store, signed_block.message) assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 7 assert store.justified_checkpoint.epoch == 6 assert store.finalized_checkpoint.epoch == 3 @@ -729,7 +756,7 @@ def test_incorrect_finalized(spec, state): store.finalized_checkpoint.epoch ) assert spec.get_head(store) != last_fork_block_root - assert spec.get_head(store) == head_root + check_head_against_root(spec, store, head_root) yield 'steps', test_steps """ diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_get_proposer_head.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_get_proposer_head.py index 81e5b4f563..b24e9a65b3 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_get_proposer_head.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_get_proposer_head.py @@ -1,7 +1,7 @@ from eth_utils import encode_hex from eth2spec.test.context import ( spec_state_test, - with_altair_and_later, + with_altair_until_eip7732, ) from eth2spec.test.helpers.attestations import ( get_valid_attestations_at_slot, @@ -25,7 +25,7 @@ ) -@with_altair_and_later +@with_altair_until_eip7732 @spec_state_test def test_basic_is_head_root(spec, state): test_steps = [] @@ -65,7 +65,7 @@ def test_basic_is_head_root(spec, state): yield 'steps', test_steps -@with_altair_and_later +@with_altair_until_eip7732 @spec_state_test def test_basic_is_parent_root(spec, state): test_steps = [] diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_on_block.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_on_block.py index 3a3f60a438..42a26dcdaf 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_on_block.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_on_block.py @@ -19,10 +19,14 @@ sign_block, ) from eth2spec.test.helpers.execution_payload import ( + build_empty_execution_payload, + compute_el_block_hash, compute_el_block_hash_for_block, ) from eth2spec.test.helpers.fork_choice import ( + check_head_against_root, get_genesis_forkchoice_store_and_block, + get_store_full_state, on_tick_and_append_step, add_block, tick_and_add_block, @@ -33,14 +37,15 @@ ) from eth2spec.test.helpers.forks import ( is_post_bellatrix, + is_post_eip7732, ) from eth2spec.test.helpers.state import ( next_epoch, next_slots, + payload_state_transition, state_transition_and_sign_block, ) - rng = random.Random(2020) @@ -68,14 +73,16 @@ def test_basic(spec, state): block = build_empty_block_for_next_slot(spec, state) signed_block = state_transition_and_sign_block(spec, state, block) yield from tick_and_add_block(spec, store, signed_block, test_steps) - assert spec.get_head(store) == signed_block.message.hash_tree_root() + check_head_against_root(spec, store, signed_block.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block.message) # On receiving a block of next epoch store.time = current_time + spec.config.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH) signed_block = state_transition_and_sign_block(spec, state, block) yield from tick_and_add_block(spec, store, signed_block, test_steps) - assert spec.get_head(store) == signed_block.message.hash_tree_root() + check_head_against_root(spec, store, signed_block.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block.message) yield 'steps', test_steps @@ -102,20 +109,24 @@ def test_on_block_checkpoints(spec, state): state, store, last_signed_block = yield from apply_next_epoch_with_attestations( spec, state, store, True, False, test_steps=test_steps) last_block_root = hash_tree_root(last_signed_block.message) - assert spec.get_head(store) == last_block_root + check_head_against_root(spec, store, last_block_root) # Forward 1 epoch next_epoch(spec, state) on_tick_and_append_step(spec, store, store.genesis_time + state.slot * spec.config.SECONDS_PER_SLOT, test_steps) # Mock the finalized_checkpoint and build a block on it - fin_state = store.block_states[last_block_root].copy() - fin_state.finalized_checkpoint = store.block_states[last_block_root].current_justified_checkpoint.copy() + if is_post_eip7732(spec): + fin_state = store.execution_payload_states[last_block_root].copy() + else: + fin_state = store.block_states[last_block_root].copy() + fin_state.finalized_checkpoint = store.block_states[last_block_root].current_justified_checkpoint.copy() block = build_empty_block_for_next_slot(spec, fin_state) - signed_block = state_transition_and_sign_block(spec, fin_state.copy(), block) + signed_block = state_transition_and_sign_block(spec, fin_state, block) yield from tick_and_add_block(spec, store, signed_block, test_steps) - assert spec.get_head(store) == signed_block.message.hash_tree_root() + check_head_against_root(spec, store, signed_block.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block.message) yield 'steps', test_steps @@ -158,7 +169,11 @@ def test_on_block_bad_parent_root(spec, state): block.state_root = state.hash_tree_root() block.parent_root = b'\x45' * 32 - if is_post_bellatrix(spec): + if is_post_eip7732(spec): + payload = build_empty_execution_payload(spec, state) + block.body.signed_execution_payload_header.message.block_hash = compute_el_block_hash( + spec, payload, state) + elif is_post_bellatrix(spec): block.body.execution_payload.block_hash = compute_el_block_hash_for_block(spec, block) signed_block = sign_block(spec, state, block) @@ -243,6 +258,7 @@ def test_on_block_finalized_skip_slots(spec, state): block = build_empty_block_for_next_slot(spec, target_state) signed_block = state_transition_and_sign_block(spec, target_state, block) yield from tick_and_add_block(spec, store, signed_block, test_steps) + payload_state_transition(spec, store, signed_block.message) yield 'steps', test_steps @@ -427,7 +443,7 @@ def test_new_finalized_slot_is_justified_checkpoint_ancestor(spec, state): all_blocks = [] slot = spec.compute_start_slot_at_epoch(3) block_root = spec.get_block_root_at_slot(state, slot) - another_state = store.block_states[block_root].copy() + another_state = get_store_full_state(spec, store, block_root).copy() for _ in range(2): _, signed_blocks, another_state = next_epoch_with_attestations(spec, another_state, True, True) all_blocks += signed_blocks @@ -438,6 +454,7 @@ def test_new_finalized_slot_is_justified_checkpoint_ancestor(spec, state): pre_store_justified_checkpoint_root = store.justified_checkpoint.root for block in all_blocks: yield from tick_and_add_block(spec, store, block, test_steps) + payload_state_transition(spec, store, block.message) ancestor_at_finalized_slot = spec.get_checkpoint_block( store, @@ -476,15 +493,30 @@ def test_proposer_boost(spec, state): spec.config.SECONDS_PER_SLOT // spec.INTERVALS_PER_SLOT - 1) on_tick_and_append_step(spec, store, time, test_steps) yield from add_block(spec, store, signed_block, test_steps) + payload_state_transition(spec, store, signed_block.message) assert store.proposer_boost_root == spec.hash_tree_root(block) - assert spec.get_weight(store, spec.hash_tree_root(block)) > 0 + if is_post_eip7732(spec): + node = spec.ChildNode( + root=spec.hash_tree_root(block), + slot=block.slot, + ) + assert spec.get_weight(store, node) > 0 + else: + assert spec.get_weight(store, spec.hash_tree_root(block)) > 0 # Ensure that boost is removed after slot is over time = (store.genesis_time + block.slot * spec.config.SECONDS_PER_SLOT + spec.config.SECONDS_PER_SLOT) on_tick_and_append_step(spec, store, time, test_steps) assert store.proposer_boost_root == spec.Root() - assert spec.get_weight(store, spec.hash_tree_root(block)) == 0 + if is_post_eip7732(spec): + node = spec.ChildNode( + root=spec.hash_tree_root(block), + slot=block.slot, + ) + assert spec.get_weight(store, node) == 0 + else: + assert spec.get_weight(store, spec.hash_tree_root(block)) == 0 next_slots(spec, state, 3) block = build_empty_block_for_next_slot(spec, state) @@ -494,15 +526,30 @@ def test_proposer_boost(spec, state): time = (store.genesis_time + block.slot * spec.config.SECONDS_PER_SLOT) on_tick_and_append_step(spec, store, time, test_steps) yield from add_block(spec, store, signed_block, test_steps) + payload_state_transition(spec, store, signed_block.message) assert store.proposer_boost_root == spec.hash_tree_root(block) - assert spec.get_weight(store, spec.hash_tree_root(block)) > 0 + if is_post_eip7732(spec): + node = spec.ChildNode( + root=spec.hash_tree_root(block), + slot=block.slot, + ) + assert spec.get_weight(store, node) > 0 + else: + assert spec.get_weight(store, spec.hash_tree_root(block)) > 0 # Ensure that boost is removed after slot is over time = (store.genesis_time + block.slot * spec.config.SECONDS_PER_SLOT + spec.config.SECONDS_PER_SLOT) on_tick_and_append_step(spec, store, time, test_steps) assert store.proposer_boost_root == spec.Root() - assert spec.get_weight(store, spec.hash_tree_root(block)) == 0 + if is_post_eip7732(spec): + node = spec.ChildNode( + root=spec.hash_tree_root(block), + slot=block.slot, + ) + assert spec.get_weight(store, node) == 0 + else: + assert spec.get_weight(store, spec.hash_tree_root(block)) == 0 test_steps.append({ 'checks': { @@ -535,6 +582,7 @@ def test_proposer_boost_root_same_slot_untimely_block(spec, state): spec.config.SECONDS_PER_SLOT // spec.INTERVALS_PER_SLOT) on_tick_and_append_step(spec, store, time, test_steps) yield from add_block(spec, store, signed_block, test_steps) + payload_state_transition(spec, store, signed_block.message) assert store.proposer_boost_root == spec.Root() @@ -570,9 +618,17 @@ def test_proposer_boost_is_first_block(spec, state): spec.config.SECONDS_PER_SLOT // spec.INTERVALS_PER_SLOT - 1) on_tick_and_append_step(spec, store, time, test_steps) yield from add_block(spec, store, signed_block_a, test_steps) + payload_state_transition(spec, store, signed_block_a.message) # `proposer_boost_root` is now `block_a` assert store.proposer_boost_root == spec.hash_tree_root(block_a) - assert spec.get_weight(store, spec.hash_tree_root(block_a)) > 0 + if is_post_eip7732(spec): + node = spec.ChildNode( + root=spec.hash_tree_root(block_a), + slot=block_a.slot, + ) + assert spec.get_weight(store, node) > 0 + else: + assert spec.get_weight(store, spec.hash_tree_root(block_a)) > 0 test_steps.append({ 'checks': { 'proposer_boost_root': encode_hex(store.proposer_boost_root), @@ -585,9 +641,17 @@ def test_proposer_boost_is_first_block(spec, state): block_b.body.graffiti = b'\x34' * 32 signed_block_b = state_transition_and_sign_block(spec, state, block_b) yield from add_block(spec, store, signed_block_b, test_steps) + payload_state_transition(spec, store, signed_block_b.message) # `proposer_boost_root` is still `block_a` assert store.proposer_boost_root == spec.hash_tree_root(block_a) - assert spec.get_weight(store, spec.hash_tree_root(block_b)) == 0 + if is_post_eip7732(spec): + node = spec.ChildNode( + root=spec.hash_tree_root(block_b), + slot=block_b.slot, + ) + assert spec.get_weight(store, node) == 0 + else: + assert spec.get_weight(store, spec.hash_tree_root(block_b)) == 0 test_steps.append({ 'checks': { 'proposer_boost_root': encode_hex(store.proposer_boost_root), @@ -646,9 +710,10 @@ def test_justification_withholding(spec, state): for signed_block in honest_signed_blocks: yield from tick_and_add_block(spec, store, signed_block, test_steps) + payload_state_transition(spec, store, signed_block.message) last_honest_block = honest_signed_blocks[-1].message - honest_state = store.block_states[hash_tree_root(last_honest_block)].copy() + honest_state = get_store_full_state(spec, store, hash_tree_root(last_honest_block)).copy() assert honest_state.finalized_checkpoint.epoch == store.finalized_checkpoint.epoch == 2 assert honest_state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 @@ -662,9 +727,10 @@ def test_justification_withholding(spec, state): honest_block.body.attestations = attacker_signed_blocks[-1].message.body.attestations signed_block = state_transition_and_sign_block(spec, honest_state, honest_block) yield from tick_and_add_block(spec, store, signed_block, test_steps) + payload_state_transition(spec, store, signed_block.message) assert state.finalized_checkpoint.epoch == store.finalized_checkpoint.epoch == 2 assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 - assert spec.get_head(store) == hash_tree_root(honest_block) + check_head_against_root(spec, store, hash_tree_root(honest_block)) assert is_ready_to_justify(spec, honest_state) # ------------ @@ -672,9 +738,10 @@ def test_justification_withholding(spec, state): # When the attacker's block is received, the honest block is still the head # This relies on the honest block's LMD score increasing due to proposer boost yield from tick_and_add_block(spec, store, attacker_signed_blocks[-1], test_steps) + payload_state_transition(spec, store, attacker_signed_blocks[-1].message) assert store.finalized_checkpoint.epoch == 3 assert store.justified_checkpoint.epoch == 4 - assert spec.get_head(store) == hash_tree_root(honest_block) + check_head_against_root(spec, store, hash_tree_root(honest_block)) yield 'steps', test_steps @@ -715,12 +782,13 @@ def test_justification_withholding_reverse_order(spec, state): assert len(signed_blocks) == 1 attacker_signed_blocks += signed_blocks yield from tick_and_add_block(spec, store, signed_blocks[0], test_steps) + payload_state_transition(spec, store, signed_blocks[0].message) assert attacker_state.finalized_checkpoint.epoch == 2 assert attacker_state.current_justified_checkpoint.epoch == 3 assert spec.get_current_epoch(attacker_state) == 4 attackers_head = hash_tree_root(attacker_signed_blocks[-1].message) - assert spec.get_head(store) == attackers_head + check_head_against_root(spec, store, attackers_head) # ------------ @@ -729,7 +797,7 @@ def test_justification_withholding_reverse_order(spec, state): assert len(honest_signed_blocks) > 0 last_honest_block = honest_signed_blocks[-1].message - honest_state = store.block_states[hash_tree_root(last_honest_block)].copy() + honest_state = get_store_full_state(spec, store, hash_tree_root(last_honest_block)).copy() assert honest_state.finalized_checkpoint.epoch == store.finalized_checkpoint.epoch == 2 assert honest_state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 @@ -749,9 +817,10 @@ def test_justification_withholding_reverse_order(spec, state): # When the honest block is received, the honest block becomes the head # This relies on the honest block's LMD score increasing due to proposer boost yield from tick_and_add_block(spec, store, signed_block, test_steps) + payload_state_transition(spec, store, signed_block.message) assert store.finalized_checkpoint.epoch == 3 assert store.justified_checkpoint.epoch == 4 - assert spec.get_head(store) == hash_tree_root(honest_block) + check_head_against_root(spec, store, hash_tree_root(honest_block)) yield 'steps', test_steps @@ -799,7 +868,8 @@ def test_justification_update_beginning_of_epoch(spec, state): # Now add the blocks & check that justification update was triggered for signed_block in signed_blocks: yield from tick_and_add_block(spec, store, signed_block, test_steps) - assert spec.get_head(store) == signed_block.message.hash_tree_root() + payload_state_transition(spec, store, signed_block.message) + check_head_against_root(spec, store, signed_block.message.hash_tree_root()) assert store.justified_checkpoint.epoch == 4 yield 'steps', test_steps @@ -849,9 +919,9 @@ def test_justification_update_end_of_epoch(spec, state): # Now add the blocks & check that justification update was triggered for signed_block in signed_blocks: yield from tick_and_add_block(spec, store, signed_block, test_steps) - assert spec.get_head(store) == signed_block.message.hash_tree_root() + check_head_against_root(spec, store, signed_block.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block.message) assert store.justified_checkpoint.epoch == 4 - yield 'steps', test_steps @@ -921,6 +991,7 @@ def test_incompatible_justification_update_start_of_epoch(spec, state): # Now add the blocks & check that justification update was triggered for signed_block in signed_blocks: yield from tick_and_add_block(spec, store, signed_block, test_steps) + payload_state_transition(spec, store, signed_block.message) finalized_checkpoint_block = spec.get_checkpoint_block( store, last_block_root, @@ -1006,6 +1077,7 @@ def test_incompatible_justification_update_end_of_epoch(spec, state): # Now add the blocks & check that justification update was triggered for signed_block in signed_blocks: yield from tick_and_add_block(spec, store, signed_block, test_steps) + payload_state_transition(spec, store, signed_block.message) finalized_checkpoint_block = spec.get_checkpoint_block( store, last_block_root, @@ -1053,10 +1125,13 @@ def test_justified_update_not_realized_finality(spec, state): assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 # We'll make the current head block the finalized block - finalized_root = spec.get_head(store) + if is_post_eip7732(spec): + finalized_root = spec.get_head(store).root + else: + finalized_root = spec.get_head(store) finalized_block = store.blocks[finalized_root] assert spec.compute_epoch_at_slot(finalized_block.slot) == 4 - assert spec.get_head(store) == finalized_root + check_head_against_root(spec, store, finalized_root) # Copy the post-state to use later another_state = state.copy() @@ -1087,11 +1162,15 @@ def test_justified_update_not_realized_finality(spec, state): # Now add the blocks & check that justification update was triggered for signed_block in signed_blocks: yield from tick_and_add_block(spec, store, signed_block, test_steps) + payload_state_transition(spec, store, signed_block.message) assert store.justified_checkpoint.epoch == 6 assert store.finalized_checkpoint.epoch == 4 last_block = signed_blocks[-1] last_block_root = last_block.message.hash_tree_root() ancestor_at_finalized_slot = spec.get_ancestor(store, last_block_root, finalized_block.slot) + if is_post_eip7732(spec): + ancestor_at_finalized_slot = ancestor_at_finalized_slot.root + assert ancestor_at_finalized_slot == store.finalized_checkpoint.root yield 'steps', test_steps @@ -1129,10 +1208,13 @@ def test_justified_update_monotonic(spec, state): assert store.finalized_checkpoint.epoch == 2 # We'll eventually make the current head block the finalized block - finalized_root = spec.get_head(store) + if is_post_eip7732(spec): + finalized_root = spec.get_head(store).root + else: + finalized_root = spec.get_head(store) finalized_block = store.blocks[finalized_root] assert spec.compute_epoch_at_slot(finalized_block.slot) == 4 - assert spec.get_head(store) == finalized_root + check_head_against_root(spec, store, finalized_root) # Copy into another variable so we can use `state` later another_state = state.copy() @@ -1154,12 +1236,15 @@ def test_justified_update_monotonic(spec, state): # Now add the blocks & check that justification update was triggered for signed_block in signed_blocks: yield from tick_and_add_block(spec, store, signed_block, test_steps) + payload_state_transition(spec, store, signed_block.message) assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 7 assert store.justified_checkpoint.epoch == 6 assert store.finalized_checkpoint.epoch == 2 last_block = signed_blocks[-1] last_block_root = last_block.message.hash_tree_root() ancestor_at_finalized_slot = spec.get_ancestor(store, last_block_root, finalized_block.slot) + if is_post_eip7732(spec): + ancestor_at_finalized_slot = ancestor_at_finalized_slot.root assert ancestor_at_finalized_slot == finalized_root # Create a fork with lower justification that also finalizes our chosen block @@ -1208,10 +1293,13 @@ def test_justified_update_always_if_better(spec, state): assert store.finalized_checkpoint.epoch == 2 # We'll eventually make the current head block the finalized block - finalized_root = spec.get_head(store) + if is_post_eip7732(spec): + finalized_root = spec.get_head(store).root + else: + finalized_root = spec.get_head(store) finalized_block = store.blocks[finalized_root] assert spec.compute_epoch_at_slot(finalized_block.slot) == 4 - assert spec.get_head(store) == finalized_root + check_head_against_root(spec, store, finalized_root) # Copy into another variable to use later another_state = state.copy() @@ -1241,6 +1329,7 @@ def test_justified_update_always_if_better(spec, state): # Now add the blocks & check that justification update was triggered for signed_block in signed_blocks: yield from tick_and_add_block(spec, store, signed_block, test_steps) + payload_state_transition(spec, store, signed_block.message) assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 7 assert store.justified_checkpoint.epoch == 6 assert store.finalized_checkpoint.epoch == 4 @@ -1291,7 +1380,8 @@ def test_pull_up_past_epoch_block(spec, state): # Add the previously created chain to the store and check for updates for signed_block in signed_blocks: yield from tick_and_add_block(spec, store, signed_block, test_steps) - assert spec.get_head(store) == signed_block.message.hash_tree_root() + check_head_against_root(spec, store, signed_block.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block.message) assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 5 assert store.justified_checkpoint.epoch == 4 assert store.finalized_checkpoint.epoch == 3 @@ -1340,7 +1430,8 @@ def test_not_pull_up_current_epoch_block(spec, state): # Add the previously created chain to the store and check that store does not apply pull-up updates for signed_block in signed_blocks: yield from tick_and_add_block(spec, store, signed_block, test_steps) - assert spec.get_head(store) == signed_block.message.hash_tree_root() + check_head_against_root(spec, store, signed_block.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block.message) assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 5 assert store.justified_checkpoint.epoch == 3 assert store.finalized_checkpoint.epoch == 2 @@ -1390,7 +1481,8 @@ def test_pull_up_on_tick(spec, state): # since the previous epoch was not justified for signed_block in signed_blocks: yield from tick_and_add_block(spec, store, signed_block, test_steps) - assert spec.get_head(store) == signed_block.message.hash_tree_root() + check_head_against_root(spec, store, signed_block.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block.message) assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 5 assert store.justified_checkpoint.epoch == 3 assert store.finalized_checkpoint.epoch == 2 diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_reorg.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_reorg.py index 91388b9a75..444272e30f 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_reorg.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_reorg.py @@ -15,14 +15,19 @@ build_empty_block, build_empty_block_for_next_slot, ) +from eth2spec.test.helpers.forks import is_post_eip7732 from eth2spec.test.helpers.fork_choice import ( + check_head_against_root, get_genesis_forkchoice_store_and_block, + get_store_full_state, on_tick_and_append_step, add_attestations, tick_and_add_block, apply_next_epoch_with_attestations, find_next_justifying_slot, is_ready_to_justify, + payload_state_transition, + payload_state_transition_no_store, ) from eth2spec.test.helpers.state import ( state_transition_and_sign_block, @@ -75,8 +80,15 @@ def test_simple_attempted_reorg_without_enough_ffg_votes(spec, state): assert spec.compute_epoch_at_slot(justifying_slot) == spec.get_current_epoch(state) for signed_block in signed_blocks[:-2]: yield from tick_and_add_block(spec, store, signed_block, test_steps) - assert spec.get_head(store) == signed_block.message.hash_tree_root() - state = store.block_states[spec.get_head(store)].copy() + check_head_against_root(spec, store, signed_block.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block.message) + if is_post_eip7732(spec): + head_root = spec.get_head(store).root + state = store.execution_payload_states[head_root].copy() + else: + head_root = spec.get_head(store) + state = store.block_states[head_root].copy() + assert state.current_justified_checkpoint.epoch == 3 next_slot(spec, state) state_a = state.copy() @@ -89,6 +101,7 @@ def test_simple_attempted_reorg_without_enough_ffg_votes(spec, state): block_y = build_empty_block_for_next_slot(spec, state) signed_block_y = state_transition_and_sign_block(spec, state, block_y) signed_blocks_of_y.append(signed_block_y) + payload_state_transition_no_store(spec, state, signed_block_y.message) # chain y has some on-chain attestations, but not enough to justify c4 signed_block_y = state_transition_with_full_block(spec, state, True, True) @@ -104,6 +117,7 @@ def test_simple_attempted_reorg_without_enough_ffg_votes(spec, state): block_z.body.attestations = [attestation] signed_block_z = state_transition_and_sign_block(spec, state, block_z) signed_blocks_of_z.append(signed_block_z) + payload_state_transition_no_store(spec, state, signed_block_z.message) # add an empty block on chain z block_z = build_empty_block_for_next_slot(spec, state) @@ -117,19 +131,23 @@ def test_simple_attempted_reorg_without_enough_ffg_votes(spec, state): # (i) slot block_a.slot + 1 signed_block_y = signed_blocks_of_y.pop(0) yield from tick_and_add_block(spec, store, signed_block_y, test_steps) + payload_state_transition(spec, store, signed_block_y.message) # apply block of chain `z` signed_block_z = signed_blocks_of_z.pop(0) yield from tick_and_add_block(spec, store, signed_block_z, test_steps) + payload_state_transition(spec, store, signed_block_z.message) # (ii) slot block_a.slot + 2 # apply block of chain `z` signed_block_z = signed_blocks_of_z.pop(0) yield from tick_and_add_block(spec, store, signed_block_z, test_steps) + payload_state_transition(spec, store, signed_block_z.message) # apply block of chain `y` signed_block_y = signed_blocks_of_y.pop(0) yield from tick_and_add_block(spec, store, signed_block_y, test_steps) + payload_state_transition(spec, store, signed_block_y.message) # chain `y` remains the winner since it arrives earlier than `z` - assert spec.get_head(store) == signed_block_y.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_y.message.hash_tree_root()) assert len(signed_blocks_of_y) == len(signed_blocks_of_z) == 0 assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 4 @@ -141,14 +159,14 @@ def test_simple_attempted_reorg_without_enough_ffg_votes(spec, state): assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 4 # chain `y` reminds the winner - assert spec.get_head(store) == signed_block_y.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_y.message.hash_tree_root()) # to next block next_epoch(spec, state) current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time on_tick_and_append_step(spec, store, current_time, test_steps) assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 5 - assert spec.get_head(store) == signed_block_y.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_y.message.hash_tree_root()) assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 yield 'steps', test_steps @@ -193,8 +211,11 @@ def _run_delayed_justification(spec, state, attempted_reorg, is_justifying_previ assert spec.compute_epoch_at_slot(justifying_slot) == spec.get_current_epoch(state) for signed_block in signed_blocks: yield from tick_and_add_block(spec, store, signed_block, test_steps) - spec.get_head(store) == signed_block.message.hash_tree_root() - state = store.block_states[spec.get_head(store)].copy() + payload_state_transition(spec, store, signed_block.message) + if is_post_eip7732(spec): + state = store.execution_payload_states[spec.get_head(store).root].copy() + else: + state = store.block_states[spec.get_head(store)].copy() if is_justifying_previous_epoch: assert state.current_justified_checkpoint.epoch == 2 else: @@ -209,7 +230,8 @@ def _run_delayed_justification(spec, state, attempted_reorg, is_justifying_previ else: signed_block_y = state_transition_with_full_block(spec, state, True, True) yield from tick_and_add_block(spec, store, signed_block_y, test_steps) - assert spec.get_head(store) == signed_block_y.message.hash_tree_root() + payload_state_transition(spec, store, signed_block_y.message) + check_head_against_root(spec, store, signed_block_y.message.hash_tree_root()) if is_justifying_previous_epoch: assert store.justified_checkpoint.epoch == 2 else: @@ -222,7 +244,7 @@ def _run_delayed_justification(spec, state, attempted_reorg, is_justifying_previ current_time = temp_state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time on_tick_and_append_step(spec, store, current_time, test_steps) yield from add_attestations(spec, store, attestations_for_y, test_steps) - assert spec.get_head(store) == signed_block_y.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_y.message.hash_tree_root()) if attempted_reorg: # add chain z @@ -233,6 +255,7 @@ def _run_delayed_justification(spec, state, attempted_reorg, is_justifying_previ assert spec.compute_epoch_at_slot(block_z.slot) == 5 signed_block_z = state_transition_and_sign_block(spec, state, block_z) yield from tick_and_add_block(spec, store, signed_block_z, test_steps) + payload_state_transition(spec, store, signed_block_z.message) else: # next epoch state = state_b.copy() @@ -241,7 +264,7 @@ def _run_delayed_justification(spec, state, attempted_reorg, is_justifying_previ on_tick_and_append_step(spec, store, current_time, test_steps) # no reorg - assert spec.get_head(store) == signed_block_y.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_y.message.hash_tree_root()) if is_justifying_previous_epoch: assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 else: @@ -292,6 +315,7 @@ def _run_include_votes_of_another_empty_chain(spec, state, enough_ffg, is_justif block_a = build_empty_block_for_next_slot(spec, state) signed_block_a = state_transition_and_sign_block(spec, state, block_a) yield from tick_and_add_block(spec, store, signed_block_a, test_steps) + payload_state_transition(spec, store, signed_block_a.message) assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 3 assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 2 else: @@ -300,10 +324,12 @@ def _run_include_votes_of_another_empty_chain(spec, state, enough_ffg, is_justif spec, state, store, True, True, test_steps=test_steps) signed_block_a = state_transition_with_full_block(spec, state, True, True) yield from tick_and_add_block(spec, store, signed_block_a, test_steps) + payload_state_transition(spec, store, signed_block_a.message) assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 4 assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 - spec.get_head(store) == signed_block_a.message.hash_tree_root() - state = store.block_states[spec.get_head(store)].copy() + root_a = signed_block_a.message.hash_tree_root() + check_head_against_root(spec, store, root_a) + state = get_store_full_state(spec, store, root_a).copy() state_a = state.copy() if is_justifying_previous_epoch: @@ -331,15 +357,14 @@ def _run_include_votes_of_another_empty_chain(spec, state, enough_ffg, is_justif signed_blocks_of_y = [] # build an empty chain to the slot prior epoch boundary - signed_blocks_of_empty_chain = [] states_of_empty_chain = [] for slot in range(state.slot + 1, last_slot_of_y + 1): block = build_empty_block(spec, state, slot=slot) signed_block = state_transition_and_sign_block(spec, state, block) - signed_blocks_of_empty_chain.append(signed_block) + payload_state_transition_no_store(spec, state, signed_block.message) states_of_empty_chain.append(state.copy()) signed_blocks_of_y.append(signed_block) - signed_block_y = signed_blocks_of_empty_chain[-1] + signed_block_y = signed_blocks_of_y[-1] assert spec.compute_epoch_at_slot(signed_block_y.message.slot) == 4 # create 2/3 votes for the empty chain @@ -359,6 +384,7 @@ def _run_include_votes_of_another_empty_chain(spec, state, enough_ffg, is_justif signed_block_y = signed_blocks_of_y.pop(0) assert signed_block_y.message.slot == slot yield from tick_and_add_block(spec, store, signed_block_y, test_steps) + payload_state_transition(spec, store, signed_block_y.message) # apply chain z, a fork chain that includes these attestations_for_y block = build_empty_block(spec, state, slot=slot) @@ -370,8 +396,10 @@ def _run_include_votes_of_another_empty_chain(spec, state, enough_ffg, is_justif ): block.body.attestations = attestations_for_y.pop(0) signed_block_z = state_transition_and_sign_block(spec, state, block) + payload_state_transition_no_store(spec, state, block) if signed_block_y != signed_block_z: yield from tick_and_add_block(spec, store, signed_block_z, test_steps) + payload_state_transition(spec, store, signed_block_z.message) if is_ready_to_justify(spec, state): break @@ -389,7 +417,7 @@ def _run_include_votes_of_another_empty_chain(spec, state, enough_ffg, is_justif assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 assert y_voting_source_epoch == 3 assert y_voting_source_epoch == store.justified_checkpoint.epoch - assert spec.get_head(store) == signed_block_y.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_y.message.hash_tree_root()) if enough_ffg: assert is_ready_to_justify(spec, state) @@ -409,7 +437,7 @@ def _run_include_votes_of_another_empty_chain(spec, state, enough_ffg, is_justif assert y_voting_source_epoch == 2 assert y_voting_source_epoch != store.justified_checkpoint.epoch assert not (y_voting_source_epoch + 2 >= spec.compute_epoch_at_slot(spec.get_current_slot(store))) - assert spec.get_head(store) == signed_block_z.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_z.message.hash_tree_root()) else: if enough_ffg: # y is not filtered out & wins the LMD competition, so y should be the head @@ -417,13 +445,13 @@ def _run_include_votes_of_another_empty_chain(spec, state, enough_ffg, is_justif assert y_voting_source_epoch == 3 assert y_voting_source_epoch != store.justified_checkpoint.epoch assert y_voting_source_epoch + 2 >= spec.compute_epoch_at_slot(spec.get_current_slot(store)) - assert spec.get_head(store) == signed_block_y.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_y.message.hash_tree_root()) else: # y is not filtered out & wins the LMD competition, so y should be the head assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 assert y_voting_source_epoch == 3 assert y_voting_source_epoch == store.justified_checkpoint.epoch - assert spec.get_head(store) == signed_block_y.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_y.message.hash_tree_root()) # to next epoch next_epoch(spec, state) @@ -438,7 +466,7 @@ def _run_include_votes_of_another_empty_chain(spec, state, enough_ffg, is_justif assert y_voting_source_epoch == 2 assert y_voting_source_epoch != store.justified_checkpoint.epoch assert not (y_voting_source_epoch + 2 >= spec.compute_epoch_at_slot(spec.get_current_slot(store))) - assert spec.get_head(store) == signed_block_z.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_z.message.hash_tree_root()) else: if enough_ffg: # y is filtered out & so z should be the head @@ -446,13 +474,13 @@ def _run_include_votes_of_another_empty_chain(spec, state, enough_ffg, is_justif assert y_voting_source_epoch == 3 assert y_voting_source_epoch != store.justified_checkpoint.epoch assert not (y_voting_source_epoch + 2 >= spec.compute_epoch_at_slot(spec.get_current_slot(store))) - assert spec.get_head(store) == signed_block_z.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_z.message.hash_tree_root()) else: # y is not filtered out & wins the LMD competition, so y should be the head assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 assert y_voting_source_epoch == 3 assert y_voting_source_epoch == store.justified_checkpoint.epoch - assert spec.get_head(store) == signed_block_y.message.hash_tree_root() + check_head_against_root(spec, store, signed_block_y.message.hash_tree_root()) yield 'steps', test_steps diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_withholding.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_withholding.py index 61926875ad..2c8e71c3e6 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_withholding.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_withholding.py @@ -12,9 +12,14 @@ from eth2spec.test.helpers.block import ( build_empty_block_for_next_slot, ) +from eth2spec.test.helpers.forks import is_post_eip7732 from eth2spec.test.helpers.fork_choice import ( + check_head_against_root, get_genesis_forkchoice_store_and_block, + get_store_full_state, on_tick_and_append_step, + payload_state_transition, + payload_state_transition_no_store, tick_and_add_block, apply_next_epoch_with_attestations, find_next_justifying_slot, @@ -61,42 +66,48 @@ def test_withholding_attack(spec, state): assert len(signed_blocks) > 1 signed_attack_block = signed_blocks[-1] for signed_block in signed_blocks[:-1]: + current_root = signed_block.message.hash_tree_root() yield from tick_and_add_block(spec, store, signed_block, test_steps) - assert spec.get_head(store) == signed_block.message.hash_tree_root() - assert spec.get_head(store) == signed_blocks[-2].message.hash_tree_root() - state = store.block_states[spec.get_head(store)].copy() + payload_state_transition(spec, store, signed_block.message) + check_head_against_root(spec, store, current_root) + head_root = signed_blocks[-2].message.hash_tree_root() + check_head_against_root(spec, store, head_root) assert spec.compute_epoch_at_slot(state.slot) == 4 assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 4 assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 + state = get_store_full_state(spec, store, head_root).copy() # Create an honest chain in epoch 5 that includes the justifying attestations from the attack block next_epoch(spec, state) assert spec.compute_epoch_at_slot(state.slot) == 5 assert state.current_justified_checkpoint.epoch == 3 - # Create two block in the honest chain with full attestations, and add to the store + # Create two blocks in the honest chain with full attestations, and add to the store + honest_state = state.copy() for _ in range(2): - signed_block = state_transition_with_full_block(spec, state, True, False) + signed_block = state_transition_with_full_block(spec, honest_state, True, False) yield from tick_and_add_block(spec, store, signed_block, test_steps) + honest_state = payload_state_transition(spec, store, signed_block.message).copy() # Create final block in the honest chain that includes the justifying attestations from the attack block - honest_block = build_empty_block_for_next_slot(spec, state) + honest_block = build_empty_block_for_next_slot(spec, honest_state) honest_block.body.attestations = signed_attack_block.message.body.attestations - signed_honest_block = state_transition_and_sign_block(spec, state, honest_block) + signed_honest_block = state_transition_and_sign_block(spec, honest_state, honest_block) # Add the honest block to the store yield from tick_and_add_block(spec, store, signed_honest_block, test_steps) - assert spec.get_head(store) == signed_honest_block.message.hash_tree_root() + payload_state_transition(spec, store, signed_honest_block.message) + check_head_against_root(spec, store, signed_honest_block.message.hash_tree_root()) assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 5 assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 # Tick to the next slot so proposer boost is not a factor in choosing the head current_time = (honest_block.slot + 1) * spec.config.SECONDS_PER_SLOT + store.genesis_time on_tick_and_append_step(spec, store, current_time, test_steps) - assert spec.get_head(store) == signed_honest_block.message.hash_tree_root() + check_head_against_root(spec, store, signed_honest_block.message.hash_tree_root()) assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 5 assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 # Upon revealing the withheld attack block, the honest block should still be the head yield from tick_and_add_block(spec, store, signed_attack_block, test_steps) - assert spec.get_head(store) == signed_honest_block.message.hash_tree_root() + check_head_against_root(spec, store, signed_honest_block.message.hash_tree_root()) # As a side effect of the pull-up logic, the attack block is pulled up and store.justified_checkpoint is updated assert store.justified_checkpoint.epoch == 4 @@ -105,7 +116,7 @@ def test_withholding_attack(spec, state): current_time = slot * spec.config.SECONDS_PER_SLOT + store.genesis_time on_tick_and_append_step(spec, store, current_time, test_steps) assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 6 - assert spec.get_head(store) == signed_honest_block.message.hash_tree_root() + check_head_against_root(spec, store, signed_honest_block.message.hash_tree_root()) yield 'steps', test_steps @@ -149,9 +160,9 @@ def test_withholding_attack_unviable_honest_chain(spec, state): signed_attack_block = signed_blocks[-1] for signed_block in signed_blocks[:-1]: yield from tick_and_add_block(spec, store, signed_block, test_steps) - assert spec.get_head(store) == signed_block.message.hash_tree_root() - assert spec.get_head(store) == signed_blocks[-2].message.hash_tree_root() - state = store.block_states[spec.get_head(store)].copy() + check_head_against_root(spec, store, signed_block.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block.message) + state = get_store_full_state(spec, store, signed_block.message.hash_tree_root()).copy() assert spec.compute_epoch_at_slot(state.slot) == 5 assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 5 assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 @@ -160,11 +171,14 @@ def test_withholding_attack_unviable_honest_chain(spec, state): next_epoch(spec, state) assert spec.compute_epoch_at_slot(state.slot) == 6 assert state.current_justified_checkpoint.epoch == 3 - # Create two block in the honest chain with full attestations, and add to the store + # Create two blocks in the honest chain with full attestations, and add to the store for _ in range(2): signed_block = state_transition_with_full_block(spec, state, True, False) + payload_state_transition_no_store(spec, state, signed_block.message) assert state.current_justified_checkpoint.epoch == 3 yield from tick_and_add_block(spec, store, signed_block, test_steps) + check_head_against_root(spec, store, signed_block.message.hash_tree_root()) + payload_state_transition(spec, store, signed_block.message) # Create final block in the honest chain that includes the justifying attestations from the attack block honest_block = build_empty_block_for_next_slot(spec, state) honest_block.body.attestations = signed_attack_block.message.body.attestations @@ -173,26 +187,33 @@ def test_withholding_attack_unviable_honest_chain(spec, state): assert state.current_justified_checkpoint.epoch == 3 # Add the honest block to the store yield from tick_and_add_block(spec, store, signed_honest_block, test_steps) + payload_state_transition(spec, store, signed_honest_block.message) current_epoch = spec.compute_epoch_at_slot(spec.get_current_slot(store)) assert current_epoch == 6 # assert store.voting_source[honest_block_root].epoch == 3 - assert spec.get_head(store) == honest_block_root + check_head_against_root(spec, store, honest_block_root) assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 6 assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 # Tick to the next slot so proposer boost is not a factor in choosing the head current_time = (honest_block.slot + 1) * spec.config.SECONDS_PER_SLOT + store.genesis_time on_tick_and_append_step(spec, store, current_time, test_steps) - assert spec.get_head(store) == honest_block_root + check_head_against_root(spec, store, honest_block_root) assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 6 assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 # Upon revealing the withheld attack block, it should become the head + # Except in EIP-7732 in which it's parent becomes head because of the + # attestations during the attacker's block's committee. yield from tick_and_add_block(spec, store, signed_attack_block, test_steps) + payload_state_transition(spec, store, signed_attack_block.message) # The attack block is pulled up and store.justified_checkpoint is updated assert store.justified_checkpoint.epoch == 5 - attack_block_root = signed_attack_block.message.hash_tree_root() - assert spec.get_head(store) == attack_block_root + if is_post_eip7732(spec): + attack_block_root = signed_attack_block.message.parent_root + else: + attack_block_root = signed_attack_block.message.hash_tree_root() + check_head_against_root(spec, store, attack_block_root) # After going to the next epoch, the honest block should become the head slot = spec.get_current_slot(store) + spec.SLOTS_PER_EPOCH - (state.slot % spec.SLOTS_PER_EPOCH) @@ -200,6 +221,6 @@ def test_withholding_attack_unviable_honest_chain(spec, state): on_tick_and_append_step(spec, store, current_time, test_steps) assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 7 # assert store.voting_source[honest_block_root].epoch == 5 - assert spec.get_head(store) == honest_block_root + check_head_against_root(spec, store, honest_block_root) yield 'steps', test_steps 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 56d3721b86..724cae6eb3 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py @@ -22,6 +22,8 @@ from eth2spec.test.helpers.deposits import prepare_state_and_deposit from eth2spec.test.helpers.execution_payload import ( build_empty_execution_payload, + build_empty_signed_execution_payload_header, + compute_el_block_hash, compute_el_block_hash_for_block, ) from eth2spec.test.helpers.voluntary_exits import prepare_signed_exits @@ -39,6 +41,7 @@ is_post_bellatrix, is_post_electra, is_post_capella, + is_post_eip7732, ) from eth2spec.test.context import ( spec_test, spec_state_test, dump_skipping_message, @@ -150,7 +153,7 @@ def process_and_sign_block_without_header_validations(spec, state, block): state_root=spec.Bytes32(), body_root=block.body.hash_tree_root(), ) - if is_post_bellatrix(spec): + if is_post_bellatrix(spec) and not is_post_eip7732(spec): if spec.is_execution_enabled(state, block.body): spec.process_execution_payload(state, block.body, spec.EXECUTION_ENGINE) @@ -200,11 +203,19 @@ def test_invalid_parent_from_same_slot(spec, state): signed_parent_block = state_transition_and_sign_block(spec, state, parent_block) child_block = parent_block.copy() - if is_post_bellatrix(spec): + child_block.parent_root = state.latest_block_header.hash_tree_root() + + if is_post_eip7732(spec): + child_block.body.signed_execution_payload_header = build_empty_signed_execution_payload_header(spec, state) + elif is_post_bellatrix(spec): child_block.body.execution_payload = build_empty_execution_payload(spec, state) child_block.parent_root = state.latest_block_header.hash_tree_root() - if is_post_bellatrix(spec): + if is_post_eip7732(spec): + payload = build_empty_execution_payload(spec, state) + child_block.body.signed_execution_payload_header.message.block_hash = compute_el_block_hash( + spec, payload, state) + elif is_post_bellatrix(spec): child_block.body.execution_payload.block_hash = compute_el_block_hash_for_block(spec, child_block) # Show that normal path through transition fails diff --git a/tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_attestation.py b/tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_attestation.py index e3ec931b32..368dd9a915 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_attestation.py +++ b/tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_attestation.py @@ -2,7 +2,7 @@ from eth2spec.test.helpers.block import build_empty_block_for_next_slot from eth2spec.test.helpers.attestations import get_valid_attestation, sign_attestation from eth2spec.test.helpers.constants import ALL_PHASES -from eth2spec.test.helpers.forks import is_post_electra +from eth2spec.test.helpers.forks import is_post_electra, is_post_eip7732 from eth2spec.test.helpers.state import transition_to, state_transition_and_sign_block, next_epoch, next_slot from eth2spec.test.helpers.fork_choice import get_genesis_forkchoice_store @@ -20,7 +20,12 @@ def run_on_attestation(spec, state, store, attestation, valid=True): spec.on_attestation(store, attestation) sample_index = indexed_attestation.attesting_indices[0] - if spec.fork in ALL_PHASES: + if is_post_eip7732(spec): + latest_message = spec.LatestMessage( + slot=attestation.data.slot, + root=attestation.data.beacon_block_root, + ) + elif spec.fork in ALL_PHASES: latest_message = spec.LatestMessage( epoch=attestation.data.target.epoch, root=attestation.data.beacon_block_root,