diff --git a/setup.py b/setup.py index 83a2e71f3..5dc0dd2ca 100644 --- a/setup.py +++ b/setup.py @@ -1197,6 +1197,7 @@ setup( "py_ecc==6.0.0", "milagro_bls_binding==1.9.0", "remerkleable==0.1.25", + "trie==2.0.2", RUAMEL_YAML_VERSION, "lru-dict==1.1.8", MARKO_VERSION, 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 2819b2a24..3950f1e90 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 @@ -3,6 +3,7 @@ from random import Random from eth2spec.test.helpers.execution_payload import ( build_empty_execution_payload, build_randomized_execution_payload, + compute_el_block_hash, get_execution_payload_header, build_state_with_incomplete_transition, build_state_with_complete_transition, @@ -124,6 +125,7 @@ def test_bad_parent_hash_first_payload(spec, state): execution_payload = build_empty_execution_payload(spec, state) execution_payload.parent_hash = b'\x55' * 32 + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_execution_payload_processing(spec, state, execution_payload, valid=True) @@ -136,6 +138,7 @@ def test_bad_parent_hash_regular_payload(spec, state): execution_payload = build_empty_execution_payload(spec, state) execution_payload.parent_hash = spec.Hash32() + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) @@ -145,6 +148,7 @@ def run_bad_prev_randao_test(spec, state): execution_payload = build_empty_execution_payload(spec, state) execution_payload.prev_randao = b'\x42' * 32 + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) @@ -170,6 +174,7 @@ def run_bad_everything_test(spec, state): execution_payload.parent_hash = spec.Hash32() execution_payload.prev_randao = spec.Bytes32() execution_payload.timestamp = 0 + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) @@ -198,6 +203,7 @@ def run_bad_timestamp_test(spec, state, is_future): else: timestamp = execution_payload.timestamp - 1 execution_payload.timestamp = timestamp + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) @@ -235,6 +241,7 @@ def run_non_empty_extra_data_test(spec, state): execution_payload = build_empty_execution_payload(spec, state) execution_payload.extra_data = b'\x45' * 12 + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_execution_payload_processing(spec, state, execution_payload) assert state.latest_execution_payload_header.extra_data == execution_payload.extra_data @@ -263,6 +270,7 @@ def run_non_empty_transactions_test(spec, state): spec.Transaction(b'\x99' * 128) for _ in range(num_transactions) ] + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_execution_payload_processing(spec, state, execution_payload) assert state.latest_execution_payload_header.transactions_root == execution_payload.transactions.hash_tree_root() @@ -288,6 +296,7 @@ def run_zero_length_transaction_test(spec, state): execution_payload = build_empty_execution_payload(spec, state) execution_payload.transactions = [spec.Transaction(b'')] assert len(execution_payload.transactions[0]) == 0 + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_execution_payload_processing(spec, state, execution_payload) assert state.latest_execution_payload_header.transactions_root == execution_payload.transactions.hash_tree_root() diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/fork_choice/test_on_merge_block.py b/tests/core/pyspec/eth2spec/test/bellatrix/fork_choice/test_on_merge_block.py index 3a935c061..86071c225 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/fork_choice/test_on_merge_block.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/fork_choice/test_on_merge_block.py @@ -4,6 +4,9 @@ from eth2spec.test.context import spec_state_test, with_phases, BELLATRIX from eth2spec.test.helpers.block import ( build_empty_block_for_next_slot, ) +from eth2spec.test.helpers.execution_payload import ( + compute_el_block_hash, +) from eth2spec.test.helpers.fork_choice import ( get_genesis_forkchoice_store_and_block, on_tick_and_append_step, @@ -72,6 +75,7 @@ def test_all_valid(spec, state): def run_func(): block = build_empty_block_for_next_slot(spec, state) block.body.execution_payload.parent_hash = pow_block.block_hash + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) signed_block = state_transition_and_sign_block(spec, state, block) yield from tick_and_add_block(spec, store, signed_block, test_steps, merge_block=True) # valid @@ -103,6 +107,7 @@ def test_block_lookup_failed(spec, state): def run_func(): block = build_empty_block_for_next_slot(spec, state) block.body.execution_payload.parent_hash = pow_block.block_hash + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) signed_block = state_transition_and_sign_block(spec, state, block) yield from tick_and_add_block(spec, store, signed_block, test_steps, valid=False, merge_block=True, block_not_found=True) @@ -136,6 +141,7 @@ def test_too_early_for_merge(spec, state): def run_func(): block = build_empty_block_for_next_slot(spec, state) block.body.execution_payload.parent_hash = pow_block.block_hash + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) signed_block = state_transition_and_sign_block(spec, state, block) yield from tick_and_add_block(spec, store, signed_block, test_steps, valid=False, merge_block=True) @@ -168,6 +174,7 @@ def test_too_late_for_merge(spec, state): def run_func(): block = build_empty_block_for_next_slot(spec, state) block.body.execution_payload.parent_hash = pow_block.block_hash + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) signed_block = state_transition_and_sign_block(spec, state, block) yield from tick_and_add_block(spec, store, signed_block, test_steps, valid=False, merge_block=True) 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 c849ccbd8..974719f92 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py @@ -8,6 +8,9 @@ from eth2spec.test.helpers.attestations import ( from eth2spec.test.helpers.block import ( build_empty_block_for_next_slot, ) +from eth2spec.test.helpers.execution_payload import ( + compute_el_block_hash, +) from eth2spec.test.helpers.fork_choice import ( get_genesis_forkchoice_store_and_block, on_tick_and_append_step, @@ -33,6 +36,7 @@ def test_from_syncing_to_invalid(spec, state): fc_store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) op_store = get_optimistic_store(spec, state, anchor_block) mega_store = MegaStore(spec, fc_store, op_store) + block_hashes = {} yield 'anchor_state', state yield 'anchor_block', anchor_block @@ -46,7 +50,7 @@ def test_from_syncing_to_invalid(spec, state): # Block 0 block_0 = build_empty_block_for_next_slot(spec, state) - block_0.body.execution_payload.block_hash = spec.hash(bytes('block_0', 'UTF-8')) + block_hashes['block_0'] = block_0.body.execution_payload.block_hash signed_block = state_transition_and_sign_block(spec, state, block_0) yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, status=PayloadStatusV1Status.VALID) assert spec.get_head(mega_store.fc_store) == mega_store.opt_store.head_block_root @@ -57,10 +61,11 @@ def test_from_syncing_to_invalid(spec, state): signed_blocks_a = [] for i in range(3): block = build_empty_block_for_next_slot(spec, state) - block.body.execution_payload.block_hash = spec.hash(bytes(f'chain_a_{i}', 'UTF-8')) block.body.execution_payload.parent_hash = ( - spec.hash(bytes(f'chain_a_{i - 1}', 'UTF-8')) if i != 0 else block_0.body.execution_payload.block_hash + block_hashes[f'chain_a_{i - 1}'] if i != 0 else block_hashes['block_0'] ) + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) + block_hashes[f'chain_a_{i}'] = block.body.execution_payload.block_hash signed_block = state_transition_and_sign_block(spec, state, block) yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, status=PayloadStatusV1Status.VALID) @@ -72,10 +77,12 @@ def test_from_syncing_to_invalid(spec, state): state = state_0.copy() for i in range(3): block = build_empty_block_for_next_slot(spec, state) - block.body.execution_payload.block_hash = spec.hash(bytes(f'chain_b_{i}', 'UTF-8')) block.body.execution_payload.parent_hash = ( - spec.hash(bytes(f'chain_b_{i - 1}', 'UTF-8')) if i != 0 else block_0.body.execution_payload.block_hash + block_hashes[f'chain_b_{i - 1}'] if i != 0 else block_hashes['block_0'] ) + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) + block_hashes[f'chain_b_{i}'] = block.body.execution_payload.block_hash + signed_block = state_transition_with_full_block(spec, state, True, True, block=block) signed_blocks_b.append(signed_block.copy()) yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, @@ -84,8 +91,10 @@ def test_from_syncing_to_invalid(spec, state): # Now add block 4 to chain `b` with INVALID block = build_empty_block_for_next_slot(spec, state) - block.body.execution_payload.block_hash = spec.hash(bytes('chain_b_3', 'UTF-8')) block.body.execution_payload.parent_hash = signed_blocks_b[-1].message.body.execution_payload.block_hash + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) + block_hashes['chain_b_3'] = block.body.execution_payload.block_hash + signed_block = state_transition_and_sign_block(spec, state, block) payload_status = PayloadStatusV1( status=PayloadStatusV1Status.INVALID, 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 141ad5a27..df9792a43 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 @@ -3,6 +3,9 @@ from eth2spec.utils.ssz.ssz_typing import uint256, Bytes32 from eth2spec.test.helpers.block import ( build_empty_block_for_next_slot, ) +from eth2spec.test.helpers.execution_payload import ( + compute_el_block_hash, +) from eth2spec.test.helpers.pow_block import ( prepare_random_pow_chain, ) @@ -57,6 +60,7 @@ def test_validate_merge_block_success(spec, state): 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) run_validate_merge_block(spec, pow_chain, block) @@ -77,6 +81,7 @@ def test_validate_merge_block_fail_parent_block_lookup(spec, state): 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) run_validate_merge_block(spec, pow_chain, block, valid=False) @@ -88,6 +93,7 @@ def test_validate_merge_block_fail_after_terminal(spec, state): 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) run_validate_merge_block(spec, pow_chain, block, valid=False) @@ -104,6 +110,7 @@ def test_validate_merge_block_tbh_override_success(spec, state): 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) run_validate_merge_block(spec, pow_chain, block) @@ -119,6 +126,7 @@ def test_validate_merge_block_fail_parent_hash_is_not_tbh(spec, state): 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) run_validate_merge_block(spec, pow_chain, block, valid=False) @@ -135,6 +143,7 @@ def test_validate_merge_block_terminal_block_hash_fail_activation_not_reached(sp 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) run_validate_merge_block(spec, pow_chain, block, valid=False) @@ -150,4 +159,5 @@ def test_validate_merge_block_fail_activation_not_reached_parent_hash_is_not_tbh 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) run_validate_merge_block(spec, pow_chain, block, valid=False) 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 da3ddcb4d..7eea740e7 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 @@ -9,6 +9,7 @@ from eth2spec.test.context import ( from eth2spec.test.helpers.constants import MINIMAL, CAPELLA from eth2spec.test.helpers.execution_payload import ( build_empty_execution_payload, + compute_el_block_hash, ) from eth2spec.test.helpers.random import ( randomize_state, @@ -199,6 +200,7 @@ def test_fail_non_withdrawable_non_empty_withdrawals(spec, state): amount=420, ) execution_payload.withdrawals.append(withdrawal) + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) @@ -211,6 +213,7 @@ def test_fail_one_expected_full_withdrawal_and_none_in_withdrawals(spec, state): next_slot(spec, state) execution_payload = build_empty_execution_payload(spec, state) execution_payload.withdrawals = [] + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) @@ -223,6 +226,7 @@ def test_fail_one_expected_partial_withdrawal_and_none_in_withdrawals(spec, stat next_slot(spec, state) execution_payload = build_empty_execution_payload(spec, state) execution_payload.withdrawals = [] + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) @@ -235,6 +239,7 @@ def test_fail_one_expected_full_withdrawal_and_duplicate_in_withdrawals(spec, st next_slot(spec, state) execution_payload = build_empty_execution_payload(spec, state) execution_payload.withdrawals.append(execution_payload.withdrawals[0].copy()) + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) @@ -247,6 +252,7 @@ def test_fail_two_expected_partial_withdrawal_and_duplicate_in_withdrawals(spec, next_slot(spec, state) execution_payload = build_empty_execution_payload(spec, state) execution_payload.withdrawals.append(execution_payload.withdrawals[0].copy()) + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) @@ -259,6 +265,7 @@ def test_fail_max_per_slot_full_withdrawals_and_one_less_in_withdrawals(spec, st next_slot(spec, state) execution_payload = build_empty_execution_payload(spec, state) execution_payload.withdrawals = execution_payload.withdrawals[:-1] + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) @@ -271,6 +278,7 @@ def test_fail_max_per_slot_partial_withdrawals_and_one_less_in_withdrawals(spec, next_slot(spec, state) execution_payload = build_empty_execution_payload(spec, state) execution_payload.withdrawals = execution_payload.withdrawals[:-1] + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) @@ -283,6 +291,7 @@ def test_fail_a_lot_fully_withdrawable_too_few_in_withdrawals(spec, state): next_slot(spec, state) execution_payload = build_empty_execution_payload(spec, state) execution_payload.withdrawals = execution_payload.withdrawals[:-1] + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) @@ -295,6 +304,7 @@ def test_fail_a_lot_partially_withdrawable_too_few_in_withdrawals(spec, state): next_slot(spec, state) execution_payload = build_empty_execution_payload(spec, state) execution_payload.withdrawals = execution_payload.withdrawals[:-1] + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) @@ -308,6 +318,7 @@ def test_fail_a_lot_mixed_withdrawable_in_queue_too_few_in_withdrawals(spec, sta next_slot(spec, state) execution_payload = build_empty_execution_payload(spec, state) execution_payload.withdrawals = execution_payload.withdrawals[:-1] + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) @@ -324,6 +335,7 @@ def test_fail_incorrect_withdrawal_index(spec, state): next_slot(spec, state) execution_payload = build_empty_execution_payload(spec, state) execution_payload.withdrawals[0].index += 1 + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) @@ -336,6 +348,7 @@ def test_fail_incorrect_address_full(spec, state): next_slot(spec, state) execution_payload = build_empty_execution_payload(spec, state) execution_payload.withdrawals[0].address = b'\xff' * 20 + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) @@ -348,6 +361,7 @@ def test_fail_incorrect_address_partial(spec, state): next_slot(spec, state) execution_payload = build_empty_execution_payload(spec, state) execution_payload.withdrawals[0].address = b'\xff' * 20 + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) @@ -360,6 +374,7 @@ def test_fail_incorrect_amount_full(spec, state): next_slot(spec, state) execution_payload = build_empty_execution_payload(spec, state) execution_payload.withdrawals[0].amount += 1 + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) @@ -372,6 +387,7 @@ def test_fail_incorrect_amount_partial(spec, state): next_slot(spec, state) execution_payload = build_empty_execution_payload(spec, state) execution_payload.withdrawals[0].amount += 1 + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) @@ -390,6 +406,7 @@ def test_fail_one_of_many_incorrectly_full(spec, state): withdrawal.index += 1 withdrawal.address = b'\x99' * 20 withdrawal.amount += 4000000 + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) @@ -408,6 +425,7 @@ def test_fail_one_of_many_incorrectly_partial(spec, state): withdrawal.index += 1 withdrawal.address = b'\x99' * 20 withdrawal.amount += 4000000 + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) @@ -426,6 +444,7 @@ def test_fail_many_incorrectly_full(spec, state): withdrawal.address = i.to_bytes(20, 'big') else: withdrawal.amount += 1 + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) @@ -444,6 +463,7 @@ def test_fail_many_incorrectly_partial(spec, state): withdrawal.address = i.to_bytes(20, 'big') else: withdrawal.amount += 1 + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) diff --git a/tests/core/pyspec/eth2spec/test/eip4844/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/eip4844/sanity/test_blocks.py index f08a7fda9..29a71fd6a 100644 --- a/tests/core/pyspec/eth2spec/test/eip4844/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/eip4844/sanity/test_blocks.py @@ -8,6 +8,9 @@ from eth2spec.test.context import ( spec_state_test, with_eip4844_and_later, ) +from eth2spec.test.helpers.execution_payload import ( + compute_el_block_hash, +) from eth2spec.test.helpers.sharding import ( get_sample_opaque_tx, ) @@ -22,6 +25,7 @@ def test_one_blob(spec, state): opaque_tx, _, blob_kzg_commitments = get_sample_opaque_tx(spec) 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) signed_block = state_transition_and_sign_block(spec, state, block) yield 'blocks', [signed_block] @@ -37,6 +41,7 @@ def test_multiple_blobs(spec, state): opaque_tx, _, blob_kzg_commitments = get_sample_opaque_tx(spec, blob_count=5) 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) signed_block = state_transition_and_sign_block(spec, state, block) yield 'blocks', [signed_block] diff --git a/tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/test_validator.py b/tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/test_validator.py index d9e3877c3..895d4751c 100644 --- a/tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/test_validator.py +++ b/tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/test_validator.py @@ -8,6 +8,9 @@ from eth2spec.test.context import ( spec_state_test, with_eip4844_and_later, ) +from eth2spec.test.helpers.execution_payload import ( + compute_el_block_hash, +) from eth2spec.test.helpers.sharding import ( get_sample_opaque_tx, ) @@ -18,6 +21,7 @@ def _run_validate_blobs_sidecar_test(spec, state, blob_count): opaque_tx, blobs, blob_kzg_commitments = get_sample_opaque_tx(spec, blob_count=blob_count) 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_transition_and_sign_block(spec, state, block) blobs_sidecar = spec.get_blobs_sidecar(block, blobs) diff --git a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py index 5ed1472db..1f478ccc6 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py @@ -1,68 +1,10 @@ +from eth_hash.auto import keccak +from trie import HexaryTrie +from rlp import encode +from rlp.sedes import big_endian_int, Binary, List + from eth2spec.debug.random_value import get_random_bytes_list -from eth2spec.test.helpers.forks import is_post_capella - - -def build_empty_execution_payload(spec, state, randao_mix=None): - """ - Assuming a pre-state of the same slot, build a valid ExecutionPayload without any transactions. - """ - latest = state.latest_execution_payload_header - timestamp = spec.compute_timestamp_at_slot(state, state.slot) - empty_txs = spec.List[spec.Transaction, spec.MAX_TRANSACTIONS_PER_PAYLOAD]() - - if randao_mix is None: - randao_mix = spec.get_randao_mix(state, spec.get_current_epoch(state)) - - payload = spec.ExecutionPayload( - parent_hash=latest.block_hash, - fee_recipient=spec.ExecutionAddress(), - state_root=latest.state_root, # no changes to the state - receipts_root=b"no receipts here" + b"\x00" * 16, # TODO: root of empty MPT may be better. - logs_bloom=spec.ByteVector[spec.BYTES_PER_LOGS_BLOOM](), # TODO: zeroed logs bloom for empty logs ok? - block_number=latest.block_number + 1, - prev_randao=randao_mix, - gas_limit=latest.gas_limit, # retain same limit - gas_used=0, # empty block, 0 gas - timestamp=timestamp, - extra_data=spec.ByteList[spec.MAX_EXTRA_DATA_BYTES](), - base_fee_per_gas=latest.base_fee_per_gas, # retain same base_fee - block_hash=spec.Hash32(), - transactions=empty_txs, - ) - if is_post_capella(spec): - payload.withdrawals = spec.get_expected_withdrawals(state) - - # TODO: real RLP + block hash logic would be nice, requires RLP and keccak256 dependency however. - payload.block_hash = spec.Hash32(spec.hash(payload.hash_tree_root() + b"FAKE RLP HASH")) - - return payload - - -def build_randomized_execution_payload(spec, state, rng): - execution_payload = build_empty_execution_payload(spec, state) - execution_payload.fee_recipient = spec.ExecutionAddress(get_random_bytes_list(rng, 20)) - execution_payload.state_root = spec.Bytes32(get_random_bytes_list(rng, 32)) - execution_payload.receipts_root = spec.Bytes32(get_random_bytes_list(rng, 32)) - execution_payload.logs_bloom = spec.ByteVector[spec.BYTES_PER_LOGS_BLOOM]( - get_random_bytes_list(rng, spec.BYTES_PER_LOGS_BLOOM) - ) - execution_payload.block_number = rng.randint(0, 10e10) - execution_payload.gas_limit = rng.randint(0, 10e10) - execution_payload.gas_used = rng.randint(0, 10e10) - extra_data_length = rng.randint(0, spec.MAX_EXTRA_DATA_BYTES) - execution_payload.extra_data = spec.ByteList[spec.MAX_EXTRA_DATA_BYTES]( - get_random_bytes_list(rng, extra_data_length) - ) - execution_payload.base_fee_per_gas = rng.randint(0, 2**256 - 1) - execution_payload.block_hash = spec.Hash32(get_random_bytes_list(rng, 32)) - - num_transactions = rng.randint(0, 100) - execution_payload.transactions = [ - spec.Transaction(get_random_bytes_list(rng, rng.randint(0, 1000))) - for _ in range(num_transactions) - ] - - return execution_payload +from eth2spec.test.helpers.forks import is_post_capella, is_post_eip4844 def get_execution_payload_header(spec, execution_payload): @@ -87,6 +29,174 @@ def get_execution_payload_header(spec, execution_payload): return payload_header +# https://eips.ethereum.org/EIPS/eip-2718 +def compute_trie_root_from_indexed_data(data): + """ + Computes the root hash of `patriciaTrie(rlp(Index) => Data)` for a data array. + """ + t = HexaryTrie(db={}) + for i, obj in enumerate(data): + k = encode(i, big_endian_int) + t.set(k, obj) + return t.root_hash + + +# https://eips.ethereum.org/EIPS/eip-4895 +# https://eips.ethereum.org/EIPS/eip-4844 +def compute_el_header_block_hash(spec, + payload_header, + transactions_trie_root, + withdrawals_trie_root=None): + """ + Computes the RLP execution block hash described by an `ExecutionPayloadHeader`. + """ + execution_payload_header_rlp = [ + # parent_hash + (Binary(32, 32), payload_header.parent_hash), + # ommers_hash + (Binary(32, 32), bytes.fromhex("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), + # coinbase + (Binary(20, 20), payload_header.fee_recipient), + # state_root + (Binary(32, 32), payload_header.state_root), + # txs_root + (Binary(32, 32), transactions_trie_root), + # receipts_root + (Binary(32, 32), payload_header.receipts_root), + # logs_bloom + (Binary(256, 256), payload_header.logs_bloom), + # difficulty + (big_endian_int, 0), + # number + (big_endian_int, payload_header.block_number), + # gas_limit + (big_endian_int, payload_header.gas_limit), + # gas_used + (big_endian_int, payload_header.gas_used), + # timestamp + (big_endian_int, payload_header.timestamp), + # extradata + (Binary(0, 32), payload_header.extra_data), + # prev_randao + (Binary(32, 32), payload_header.prev_randao), + # nonce + (Binary(8, 8), bytes.fromhex("0000000000000000")), + # base_fee_per_gas + (big_endian_int, payload_header.base_fee_per_gas), + ] + if is_post_capella(spec): + # withdrawals_root + execution_payload_header_rlp.append((Binary(32, 32), withdrawals_trie_root)) + if is_post_eip4844(spec): + # excess_data_gas + execution_payload_header_rlp.append((big_endian_int, payload_header.excess_data_gas)) + + sedes = List([schema for schema, _ in execution_payload_header_rlp]) + values = [value for _, value in execution_payload_header_rlp] + encoded = encode(values, sedes) + + return spec.Hash32(keccak(encoded)) + + +# https://eips.ethereum.org/EIPS/eip-4895 +def get_withdrawal_rlp(spec, withdrawal): + withdrawal_rlp = [ + # index + (big_endian_int, withdrawal.index), + # validator_index + (big_endian_int, withdrawal.validator_index), + # address + (Binary(20, 20), withdrawal.address), + # amount + (big_endian_int, spec.uint256(withdrawal.amount) * (10**9)), + ] + + sedes = List([schema for schema, _ in withdrawal_rlp]) + values = [value for _, value in withdrawal_rlp] + return encode(values, sedes) + + +def compute_el_block_hash(spec, payload): + transactions_trie_root = compute_trie_root_from_indexed_data(payload.transactions) + + if is_post_capella(spec): + withdrawals_encoded = [get_withdrawal_rlp(spec, withdrawal) for withdrawal in payload.withdrawals] + withdrawals_trie_root = compute_trie_root_from_indexed_data(withdrawals_encoded) + else: + withdrawals_trie_root = None + + payload_header = get_execution_payload_header(spec, payload) + + return compute_el_header_block_hash( + spec, + payload_header, + transactions_trie_root, + withdrawals_trie_root, + ) + + +def build_empty_execution_payload(spec, state, randao_mix=None): + """ + Assuming a pre-state of the same slot, build a valid ExecutionPayload without any transactions. + """ + latest = state.latest_execution_payload_header + timestamp = spec.compute_timestamp_at_slot(state, state.slot) + empty_txs = spec.List[spec.Transaction, spec.MAX_TRANSACTIONS_PER_PAYLOAD]() + + if randao_mix is None: + randao_mix = spec.get_randao_mix(state, spec.get_current_epoch(state)) + + payload = spec.ExecutionPayload( + parent_hash=latest.block_hash, + fee_recipient=spec.ExecutionAddress(), + state_root=latest.state_root, # no changes to the state + receipts_root=spec.Bytes32(bytes.fromhex("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), + logs_bloom=spec.ByteVector[spec.BYTES_PER_LOGS_BLOOM](), # TODO: zeroed logs bloom for empty logs ok? + block_number=latest.block_number + 1, + prev_randao=randao_mix, + gas_limit=latest.gas_limit, # retain same limit + gas_used=0, # empty block, 0 gas + timestamp=timestamp, + extra_data=spec.ByteList[spec.MAX_EXTRA_DATA_BYTES](), + base_fee_per_gas=latest.base_fee_per_gas, # retain same base_fee + transactions=empty_txs, + ) + if is_post_capella(spec): + payload.withdrawals = spec.get_expected_withdrawals(state) + + payload.block_hash = compute_el_block_hash(spec, payload) + + return payload + + +def build_randomized_execution_payload(spec, state, rng): + execution_payload = build_empty_execution_payload(spec, state) + execution_payload.fee_recipient = spec.ExecutionAddress(get_random_bytes_list(rng, 20)) + execution_payload.state_root = spec.Bytes32(get_random_bytes_list(rng, 32)) + execution_payload.receipts_root = spec.Bytes32(get_random_bytes_list(rng, 32)) + execution_payload.logs_bloom = spec.ByteVector[spec.BYTES_PER_LOGS_BLOOM]( + get_random_bytes_list(rng, spec.BYTES_PER_LOGS_BLOOM) + ) + execution_payload.block_number = rng.randint(0, 10e10) + execution_payload.gas_limit = rng.randint(0, 10e10) + execution_payload.gas_used = rng.randint(0, 10e10) + extra_data_length = rng.randint(0, spec.MAX_EXTRA_DATA_BYTES) + execution_payload.extra_data = spec.ByteList[spec.MAX_EXTRA_DATA_BYTES]( + get_random_bytes_list(rng, extra_data_length) + ) + execution_payload.base_fee_per_gas = rng.randint(0, 2**256 - 1) + + num_transactions = rng.randint(0, 100) + execution_payload.transactions = [ + spec.Transaction(get_random_bytes_list(rng, rng.randint(0, 1000))) + for _ in range(num_transactions) + ] + + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) + + return execution_payload + + def build_state_with_incomplete_transition(spec, state): state = build_state_with_execution_payload_header(spec, state, spec.ExecutionPayloadHeader()) assert not spec.is_merge_transition_complete(state) diff --git a/tests/core/pyspec/eth2spec/test/helpers/genesis.py b/tests/core/pyspec/eth2spec/test/helpers/genesis.py index d758936e9..de2dd2647 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/genesis.py +++ b/tests/core/pyspec/eth2spec/test/helpers/genesis.py @@ -1,8 +1,11 @@ from eth2spec.test.helpers.constants import ( ALTAIR, BELLATRIX, CAPELLA, EIP4844, ) +from eth2spec.test.helpers.execution_payload import ( + compute_el_header_block_hash, +) from eth2spec.test.helpers.forks import ( - is_post_altair, is_post_bellatrix, + is_post_altair, is_post_bellatrix, is_post_capella, ) from eth2spec.test.helpers.keys import pubkeys @@ -29,7 +32,7 @@ def get_sample_genesis_execution_payload_header(spec, eth1_block_hash=None): if eth1_block_hash is None: eth1_block_hash = b'\x55' * 32 - return spec.ExecutionPayloadHeader( + payload_header = spec.ExecutionPayloadHeader( parent_hash=b'\x30' * 32, fee_recipient=b'\x42' * 20, state_root=b'\x20' * 32, @@ -43,6 +46,21 @@ def get_sample_genesis_execution_payload_header(spec, transactions_root=spec.Root(b'\x56' * 32), ) + transactions_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + + if is_post_capella(spec): + withdrawals_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + else: + withdrawals_trie_root = None + + payload_header.block_hash = compute_el_header_block_hash( + spec, + payload_header, + transactions_trie_root, + withdrawals_trie_root, + ) + return payload_header + def create_genesis_state(spec, validator_balances, activation_threshold): deposit_root = b'\x42' * 32 diff --git a/tests/core/pyspec/eth2spec/test/utils/randomized_block_tests.py b/tests/core/pyspec/eth2spec/test/utils/randomized_block_tests.py index 37fbdc417..4d2ec124d 100644 --- a/tests/core/pyspec/eth2spec/test/utils/randomized_block_tests.py +++ b/tests/core/pyspec/eth2spec/test/utils/randomized_block_tests.py @@ -7,6 +7,9 @@ import warnings from random import Random from typing import Callable +from eth2spec.test.helpers.execution_payload import ( + compute_el_block_hash, +) from eth2spec.test.helpers.multi_operations import ( build_random_block_from_state_for_next_slot, get_random_bls_to_execution_changes, @@ -234,6 +237,7 @@ def random_block_eip4844(spec, state, signed_blocks, scenario_state, rng=Random( # TODO: more commitments. blob_kzg_commitments: List[KZGCommitment, MAX_BLOBS_PER_BLOCK] opaque_tx, _, blob_kzg_commitments = get_sample_opaque_tx(spec, blob_count=1) block.body.execution_payload.transactions = [opaque_tx] + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) block.body.blob_kzg_commitments = blob_kzg_commitments return block