From 14a38251eda27a176974d9efee65549e56565664 Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Tue, 14 Sep 2021 11:50:43 +0300 Subject: [PATCH 01/19] Fixed uint256 type misalign --- specs/merge/fork.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/merge/fork.md b/specs/merge/fork.md index f2547758d..be791afa9 100644 --- a/specs/merge/fork.md +++ b/specs/merge/fork.md @@ -28,7 +28,7 @@ Warning: this configuration is not definitive. | - | - | | `MERGE_FORK_VERSION` | `Version('0x02000000')` | | `MERGE_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** | -| `MIN_ANCHOR_POW_BLOCK_DIFFICULTY` | **TBD** | +| `MIN_ANCHOR_POW_BLOCK_DIFFICULTY` | `uint256(2 ** 32)` **TBD** | | `TARGET_SECONDS_TO_MERGE` | `uint64(7 * 86400)` = (604,800) | ## Fork to Merge @@ -115,7 +115,7 @@ def compute_terminal_total_difficulty(anchor_pow_block: PowBlock) -> uint256: pow_blocks_after_anchor_block = ETH1_FOLLOW_DISTANCE + pow_blocks_per_voting_period + pow_blocks_to_merge anchor_difficulty = max(MIN_ANCHOR_POW_BLOCK_DIFFICULTY, anchor_pow_block.difficulty) - return anchor_pow_block.total_difficulty + anchor_difficulty * pow_blocks_after_anchor_block + return anchor_pow_block.total_difficulty + anchor_difficulty * uint256(pow_blocks_after_anchor_block) def get_transition_store(anchor_pow_block: PowBlock) -> TransitionStore: From 9acf60fe0744d2f9330de675879c7644e565f88a Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Tue, 14 Sep 2021 11:51:23 +0300 Subject: [PATCH 02/19] Extracted `process_merge_execution_payload`, terminal block validation function --- specs/merge/fork-choice.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md index de82b17fa..2e228a2e1 100644 --- a/specs/merge/fork-choice.md +++ b/specs/merge/fork-choice.md @@ -17,6 +17,7 @@ - [`PowBlock`](#powblock) - [`get_pow_block`](#get_pow_block) - [`is_valid_terminal_pow_block`](#is_valid_terminal_pow_block) + - [`process_merge_execution_payload`](#process_merge_execution_payload) - [Updated fork-choice handlers](#updated-fork-choice-handlers) - [`on_block`](#on_block) @@ -106,6 +107,19 @@ def is_valid_terminal_pow_block(transition_store: TransitionStore, block: PowBlo return block.is_valid and is_total_difficulty_reached and is_parent_total_difficulty_valid ``` +### `process_merge_execution_payload` + +Used by fork-choice handler, `on_block` to check validity of terminal block. + +```python +def process_merge_execution_payload(transition_store: TransitionStore, execution_payload: ExecutionPayload) -> None: + # Delay consideration of block until PoW block is processed by the PoW node + pow_block = get_pow_block(execution_payload.parent_hash) + pow_parent = get_pow_block(pow_block.parent_hash) + assert pow_block.is_processed + assert is_valid_terminal_pow_block(transition_store, pow_block, pow_parent) +``` + ## Updated fork-choice handlers ### `on_block` @@ -130,11 +144,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock, transition_store: Tr # [New in Merge] if (transition_store is not None) and is_merge_block(pre_state, block.body): - # Delay consideration of block until PoW block is processed by the PoW node - pow_block = get_pow_block(block.body.execution_payload.parent_hash) - pow_parent = get_pow_block(pow_block.parent_hash) - assert pow_block.is_processed - assert is_valid_terminal_pow_block(transition_store, pow_block, pow_parent) + process_merge_execution_payload(transition_store, block.body.execution_payload) # Check the block is valid and compute the post-state state = pre_state.copy() From 7ff173bd0b6dd9ff5525a8dda7eace4ca95ba07e Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Tue, 14 Sep 2021 11:51:37 +0300 Subject: [PATCH 03/19] Added new Merge tests --- .../pyspec/eth2spec/test/helpers/block.py | 12 ++ .../test/helpers/execution_payload.py | 12 ++ .../test_process_execution_payload.py | 183 ++++++++++++++++++ .../test/merge/fork_choice/__init__.py | 0 .../test/merge/fork_choice/test_on_block.py | 174 +++++++++++++++++ .../eth2spec/test/merge/unit/__init__.py | 0 .../eth2spec/test/merge/unit/test_unit.py | 121 ++++++++++++ 7 files changed, 502 insertions(+) create mode 100644 tests/core/pyspec/eth2spec/test/merge/fork_choice/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_block.py create mode 100644 tests/core/pyspec/eth2spec/test/merge/unit/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/merge/unit/test_unit.py diff --git a/tests/core/pyspec/eth2spec/test/helpers/block.py b/tests/core/pyspec/eth2spec/test/helpers/block.py index b8f7c4bcb..7daa51970 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/block.py +++ b/tests/core/pyspec/eth2spec/test/helpers/block.py @@ -4,6 +4,7 @@ from eth2spec.test.helpers.keys import privkeys from eth2spec.utils import bls from eth2spec.utils.bls import only_with_bls from eth2spec.utils.ssz.ssz_impl import hash_tree_root +from eth2spec.utils.ssz.ssz_typing import uint256 def get_proposer_index_maybe(spec, state, slot, proposer_index=None): @@ -122,3 +123,14 @@ def get_state_and_beacon_parent_root_at_slot(spec, state, slot): previous_block_header.state_root = hash_tree_root(state) beacon_parent_root = hash_tree_root(previous_block_header) return state, beacon_parent_root + + +def prepare_empty_pow_block(spec): + return spec.PowBlock( + block_hash=spec.Hash32(), + parent_hash=spec.Hash32(), + is_processed=False, + is_valid=True, + total_difficulty=uint256(0), + difficulty=uint256(0) + ) diff --git a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py index 43be965a5..d82a6a54d 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py @@ -1,3 +1,6 @@ +from remerkleable.byte_arrays import ByteVector + + 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. @@ -64,3 +67,12 @@ def build_state_with_execution_payload_header(spec, state, execution_payload_hea pre_state.latest_execution_payload_header = execution_payload_header return pre_state + + +# damages last byte of the data by changing one bit +def screw_up_bytes(data: ByteVector): + assert len(data) > 0 + length = data.vector_length() + raw_data = data.encode_bytes() + raw_data = raw_data[0:len(raw_data) - 1] + bytes([(raw_data[len(raw_data) - 1] ^ 1)]) + return ByteVector[length](*raw_data) diff --git a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py index 4c68034d4..a2af7dc92 100644 --- a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py @@ -1,8 +1,12 @@ +from remerkleable.byte_arrays import Bytes32 +from eth2spec.utils.ssz.ssz_typing import uint64 + from eth2spec.test.helpers.execution_payload import ( build_empty_execution_payload, get_execution_payload_header, build_state_with_incomplete_transition, build_state_with_complete_transition, + screw_up_bytes ) from eth2spec.test.context import spec_state_test, expect_assertion_error, with_merge_and_later from eth2spec.test.helpers.state import next_slot @@ -199,3 +203,182 @@ def test_bad_timestamp_regular_payload(spec, state): execution_payload.timestamp = execution_payload.timestamp + 1 yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) + + +@with_merge_and_later +@spec_state_test +def test_bad_randao_first_payload(spec, state): + # pre-state + state = build_state_with_incomplete_transition(spec, state) + next_slot(spec, state) + + # execution payload + execution_payload = build_empty_execution_payload(spec, state) + good_randao: Bytes32 = execution_payload.random + bad_randao = screw_up_bytes(good_randao) + # still valid because randao is ignored on this stage + execution_payload.random = bad_randao + + yield from run_execution_payload_processing(spec, state, execution_payload) + + +@with_merge_and_later +@spec_state_test +def test_bad_randao_regular_payload(spec, state): + # pre-state + state = build_state_with_complete_transition(spec, state) + next_slot(spec, state) + + # execution payload + execution_payload = build_empty_execution_payload(spec, state) + good_randao: Bytes32 = execution_payload.random + bad_randao = screw_up_bytes(good_randao) + execution_payload.random = bad_randao + + yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) + + +@with_merge_and_later +@spec_state_test +def test_gaslimit_zero_first_payload(spec, state): + # pre-state + state = build_state_with_incomplete_transition(spec, state) + next_slot(spec, state) + + # execution payload + execution_payload = build_empty_execution_payload(spec, state) + execution_payload.gas_limit = uint64(0) + + yield from run_execution_payload_processing(spec, state, execution_payload) + + +@with_merge_and_later +@spec_state_test +def test_gaslimit_max_first_payload(spec, state): + # pre-state + state = build_state_with_incomplete_transition(spec, state) + next_slot(spec, state) + + # execution payload + execution_payload = build_empty_execution_payload(spec, state) + execution_payload.gas_limit = uint64(2**64 - 1) + + yield from run_execution_payload_processing(spec, state, execution_payload) + + +@with_merge_and_later +@spec_state_test +def test_gaslimit_upper_plus_regular_payload(spec, state): + # pre-state + state = build_state_with_complete_transition(spec, state) + next_slot(spec, state) + + # execution payload + execution_payload = build_empty_execution_payload(spec, state) + execution_payload.gas_limit = execution_payload.gas_limit + \ + execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR + + yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) + + +@with_merge_and_later +@spec_state_test +def test_gaslimit_upper_regular_payload(spec, state): + # pre-state + state = build_state_with_complete_transition(spec, state) + next_slot(spec, state) + + # execution payload + execution_payload = build_empty_execution_payload(spec, state) + execution_payload.gas_limit = execution_payload.gas_limit + \ + execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR - uint64(1) + + yield from run_execution_payload_processing(spec, state, execution_payload) + + +@with_merge_and_later +@spec_state_test +def test_gaslimit_lower_minus_regular_payload(spec, state): + # pre-state + state = build_state_with_complete_transition(spec, state) + next_slot(spec, state) + + # execution payload + execution_payload = build_empty_execution_payload(spec, state) + execution_payload.gas_limit = execution_payload.gas_limit - \ + execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR + + yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) + + +@with_merge_and_later +@spec_state_test +def test_gaslimit_lower_regular_payload(spec, state): + # pre-state + state = build_state_with_complete_transition(spec, state) + next_slot(spec, state) + + # execution payload + execution_payload = build_empty_execution_payload(spec, state) + execution_payload.gas_limit = execution_payload.gas_limit - \ + execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR + uint64(1) + + yield from run_execution_payload_processing(spec, state, execution_payload) + + +@with_merge_and_later +@spec_state_test +def test_gaslimit_minimum_regular_payload(spec, state): + # pre-state + state = build_state_with_complete_transition(spec, state) + next_slot(spec, state) + state.latest_execution_payload_header.gas_limit = spec.MIN_GAS_LIMIT + + # execution payload + execution_payload = build_empty_execution_payload(spec, state) + execution_payload.gas_limit = execution_payload.gas_limit + + yield from run_execution_payload_processing(spec, state, execution_payload) + + +@with_merge_and_later +@spec_state_test +def test_gaslimit_minimum_minus_regular_payload(spec, state): + # pre-state + state = build_state_with_complete_transition(spec, state) + next_slot(spec, state) + state.latest_execution_payload_header.gas_limit = spec.MIN_GAS_LIMIT + + # execution payload + execution_payload = build_empty_execution_payload(spec, state) + execution_payload.gas_limit = execution_payload.gas_limit - uint64(1) + + yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) + + +@with_merge_and_later +@spec_state_test +def test_gasused_gaslimit_regular_payload(spec, state): + # pre-state + state = build_state_with_complete_transition(spec, state) + next_slot(spec, state) + + # execution payload + execution_payload = build_empty_execution_payload(spec, state) + execution_payload.gas_used = execution_payload.gas_limit + + yield from run_execution_payload_processing(spec, state, execution_payload) + + +@with_merge_and_later +@spec_state_test +def test_gasused_gaslimit_plus_regular_payload(spec, state): + # pre-state + state = build_state_with_complete_transition(spec, state) + next_slot(spec, state) + + # execution payload + execution_payload = build_empty_execution_payload(spec, state) + execution_payload.gas_used = execution_payload.gas_limit + uint64(1) + + yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) diff --git a/tests/core/pyspec/eth2spec/test/merge/fork_choice/__init__.py b/tests/core/pyspec/eth2spec/test/merge/fork_choice/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_block.py b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_block.py new file mode 100644 index 000000000..f096fb138 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_block.py @@ -0,0 +1,174 @@ +from eth2spec.utils.ssz.ssz_typing import uint64, uint256 +from eth2spec.test.helpers.block import ( + prepare_empty_pow_block +) +from eth2spec.test.context import spec_state_test, expect_assertion_error, with_merge_and_later + + +def create_transition_store(spec): + anchor_block = prepare_empty_pow_block(spec) + transition_store = spec.get_transition_store(anchor_block) + return transition_store + + +class BlockNotFoundException(Exception): + pass + + +def run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload, + valid=True, block_lookup_success=True): + """ + Run ``process_merge_execution_payload``, yielding: + - transition store ('transition_store') + - current block ('block') + - parent block ('parent_block') + - execution payload ('payload') + If ``valid == False``, run expecting ``AssertionError`` + If ``block_lookup_success == False``, run expecting ``BlockNotFoundException`` + """ + + yield 'transition_store', transition_store + yield 'block', block + yield 'parent_block', parent_block + yield 'payload', payload + + def get_pow_block(hash: spec.Bytes32) -> spec.PowBlock: + if hash == block.block_hash: + return block + elif hash == parent_block.block_hash: + return parent_block + else: + raise BlockNotFoundException() + save_pow_block = spec.get_pow_block + + # Guido authorized everyone to do this + spec.get_pow_block = get_pow_block + exception_caught = False + block_not_found_exception_caught = False + try: + spec.process_merge_execution_payload(transition_store, payload) + except BlockNotFoundException: + block_not_found_exception_caught = True + except AssertionError: + exception_caught = True + except Exception as e: + spec.get_pow_block = save_pow_block + raise e + spec.get_pow_block = save_pow_block + + if block_lookup_success: + assert not block_not_found_exception_caught + else: + assert block_not_found_exception_caught + if valid: + assert not exception_caught + else: + assert exception_caught + + +@with_merge_and_later +@spec_state_test +def test_valid_terminal_pow_block_success_valid_fail_invalid(spec, state): + transition_store = create_transition_store(spec) + parent_block = prepare_empty_pow_block(spec) + parent_block.total_difficulty = transition_store.terminal_total_difficulty - uint256(1) + block = prepare_empty_pow_block(spec) + block.parent_hash = parent_block.block_hash + block.total_difficulty = transition_store.terminal_total_difficulty + + assert spec.is_valid_terminal_pow_block(transition_store, block, parent_block) + + block.is_valid = False + assert not spec.is_valid_terminal_pow_block(transition_store, block, parent_block) + + +@with_merge_and_later +@spec_state_test +def test_valid_terminal_pow_block_fail_before_terminal(spec, state): + transition_store = create_transition_store(spec) + parent_block = prepare_empty_pow_block(spec) + parent_block.total_difficulty = transition_store.terminal_total_difficulty - uint256(2) + block = prepare_empty_pow_block(spec) + block.parent_hash = parent_block.block_hash + block.total_difficulty = transition_store.terminal_total_difficulty - uint256(1) + + assert not spec.is_valid_terminal_pow_block(transition_store, block, parent_block) + + +@with_merge_and_later +@spec_state_test +def test_valid_terminal_pow_block_fail_just_after_terminal(spec, state): + transition_store = create_transition_store(spec) + parent_block = prepare_empty_pow_block(spec) + parent_block.total_difficulty = transition_store.terminal_total_difficulty + block = prepare_empty_pow_block(spec) + block.parent_hash = parent_block.block_hash + block.total_difficulty = transition_store.terminal_total_difficulty + uint256(1) + + assert not spec.is_valid_terminal_pow_block(transition_store, block, parent_block) + + +@with_merge_and_later +@spec_state_test +def test_process_merge_execution_payload_success(spec, state): + transition_store = create_transition_store(spec) + parent_block = prepare_empty_pow_block(spec) + parent_block.block_hash = spec.Hash32(spec.hash(b'01')) + parent_block.total_difficulty = transition_store.terminal_total_difficulty - uint256(1) + block = prepare_empty_pow_block(spec) + block.parent_hash = parent_block.block_hash + block.is_processed = True + block.total_difficulty = transition_store.terminal_total_difficulty + payload = spec.ExecutionPayload() + payload.parent_hash = block.block_hash + yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload) + block.is_processed = False + yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload, valid=False) + + +@with_merge_and_later +@spec_state_test +def test_process_merge_execution_payload_fail_block_lookup(spec, state): + transition_store = create_transition_store(spec) + parent_block = prepare_empty_pow_block(spec) + parent_block.block_hash = spec.Hash32(spec.hash(b'01')) + parent_block.total_difficulty = transition_store.terminal_total_difficulty - uint256(1) + block = prepare_empty_pow_block(spec) + block.parent_hash = parent_block.block_hash + block.is_processed = True + block.total_difficulty = transition_store.terminal_total_difficulty + payload = spec.ExecutionPayload() + payload.parent_hash = spec.Hash32(spec.hash(b'02')) + yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload, block_lookup_success=False) + + +@with_merge_and_later +@spec_state_test +def test_process_merge_execution_payload_fail_parent_block_lookup(spec, state): + transition_store = create_transition_store(spec) + parent_block = prepare_empty_pow_block(spec) + parent_block.block_hash = spec.Hash32(spec.hash(b'01')) + parent_block.total_difficulty = transition_store.terminal_total_difficulty - uint256(1) + block = prepare_empty_pow_block(spec) + block.parent_hash = spec.Hash32(spec.hash(b'00')) + block.is_processed = True + block.total_difficulty = transition_store.terminal_total_difficulty + payload = spec.ExecutionPayload() + payload.parent_hash = block.block_hash + yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload, block_lookup_success=False) + + +@with_merge_and_later +@spec_state_test +def test_process_merge_execution_payload_fail_after_terminal(spec, state): + transition_store = create_transition_store(spec) + parent_block = prepare_empty_pow_block(spec) + parent_block.block_hash = spec.Hash32(spec.hash(b'01')) + parent_block.total_difficulty = transition_store.terminal_total_difficulty + block = prepare_empty_pow_block(spec) + block.parent_hash = parent_block.block_hash + block.is_processed = True + block.total_difficulty = transition_store.terminal_total_difficulty + 1 + payload = spec.ExecutionPayload() + payload.parent_hash = block.block_hash + yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload, valid=False) \ No newline at end of file diff --git a/tests/core/pyspec/eth2spec/test/merge/unit/__init__.py b/tests/core/pyspec/eth2spec/test/merge/unit/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/merge/unit/test_unit.py b/tests/core/pyspec/eth2spec/test/merge/unit/test_unit.py new file mode 100644 index 000000000..24caa03b1 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/merge/unit/test_unit.py @@ -0,0 +1,121 @@ +from eth2spec.utils.ssz.ssz_typing import uint64, uint256 +from eth2spec.test.helpers.execution_payload import ( + build_empty_execution_payload, + build_state_with_incomplete_transition, + build_state_with_complete_transition, +) +from eth2spec.test.helpers.block import ( + prepare_empty_pow_block +) +from eth2spec.test.context import spec_state_test, expect_assertion_error, with_merge_and_later + + +@with_merge_and_later +@spec_state_test +def test_fail_merge_complete(spec, state): + state = build_state_with_incomplete_transition(spec, state) + assert not spec.is_merge_complete(state) + + +@with_merge_and_later +@spec_state_test +def test_success_merge_complete(spec, state): + state = build_state_with_complete_transition(spec, state) + assert spec.is_merge_complete(state) + + +@with_merge_and_later +@spec_state_test +def test_fail_merge_block_false_false(spec, state): + state = build_state_with_complete_transition(spec, state) + execution_payload = spec.ExecutionPayload() + body = spec.BeaconBlockBody() + body.execution_payload = execution_payload + assert not spec.is_merge_block(state, body) + + +@with_merge_and_later +@spec_state_test +def test_fail_merge_block_false_true(spec, state): + state = build_state_with_complete_transition(spec, state) + execution_payload = build_empty_execution_payload(spec, state) + body = spec.BeaconBlockBody() + body.execution_payload = execution_payload + assert not spec.is_merge_block(state, body) + + +@with_merge_and_later +@spec_state_test +def test_fail_merge_block_true_false(spec, state): + state = build_state_with_incomplete_transition(spec, state) + execution_payload = spec.ExecutionPayload() + body = spec.BeaconBlockBody() + body.execution_payload = execution_payload + assert not spec.is_merge_block(state, body) + + +@with_merge_and_later +@spec_state_test +def test_success_merge_block(spec, state): + state = build_state_with_incomplete_transition(spec, state) + execution_payload = build_empty_execution_payload(spec, state) + body = spec.BeaconBlockBody() + body.execution_payload = execution_payload + assert spec.is_merge_block(state, body) + + +@with_merge_and_later +@spec_state_test +def test_fail_execution_enabled_false_false(spec, state): + state = build_state_with_incomplete_transition(spec, state) + execution_payload = spec.ExecutionPayload() + body = spec.BeaconBlockBody() + body.execution_payload = execution_payload + assert not spec.is_execution_enabled(state, body) + + +@with_merge_and_later +@spec_state_test +def test_success_execution_enabled_true_false(spec, state): + state = build_state_with_incomplete_transition(spec, state) + execution_payload = build_empty_execution_payload(spec, state) + body = spec.BeaconBlockBody() + body.execution_payload = execution_payload + assert spec.is_execution_enabled(state, body) + + +@with_merge_and_later +@spec_state_test +def test_success_execution_enabled_false_true(spec, state): + state = build_state_with_complete_transition(spec, state) + execution_payload = spec.ExecutionPayload() + body = spec.BeaconBlockBody() + body.execution_payload = execution_payload + assert spec.is_execution_enabled(state, body) + + +@with_merge_and_later +@spec_state_test +def test_success_execution_enabled_true_true(spec, state): + state = build_state_with_complete_transition(spec, state) + execution_payload = build_empty_execution_payload(spec, state) + body = spec.BeaconBlockBody() + body.execution_payload = execution_payload + assert spec.is_execution_enabled(state, body) + + +def compute_terminal_total_difficulty_reference(spec) -> uint256: + seconds_per_voting_period = spec.EPOCHS_PER_ETH1_VOTING_PERIOD * spec.SLOTS_PER_EPOCH * spec.config.SECONDS_PER_SLOT + pow_blocks_per_voting_period = seconds_per_voting_period // spec.config.SECONDS_PER_ETH1_BLOCK + pow_blocks_to_merge = spec.TARGET_SECONDS_TO_MERGE // spec.config.SECONDS_PER_ETH1_BLOCK + pow_blocks_after_anchor_block = spec.config.ETH1_FOLLOW_DISTANCE + pow_blocks_per_voting_period + pow_blocks_to_merge + return spec.config.MIN_ANCHOR_POW_BLOCK_DIFFICULTY * uint256(pow_blocks_after_anchor_block) + + +@with_merge_and_later +@spec_state_test +def test_zero_difficulty(spec, state): + anchor_pow_block = prepare_empty_pow_block(spec) + reference_td = compute_terminal_total_difficulty_reference(spec) + + assert spec.compute_terminal_total_difficulty(anchor_pow_block) == reference_td From cf1bd6ccc014ab002f519eba1e58b00230919906 Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Tue, 14 Sep 2021 15:26:10 +0300 Subject: [PATCH 04/19] Fixed lint errors for merge tests --- .../test_process_execution_payload.py | 8 ++++---- .../eth2spec/test/merge/fork_choice/test_on_block.py | 12 +++++++----- .../pyspec/eth2spec/test/merge/unit/test_unit.py | 7 ++++--- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py index a2af7dc92..93cc925fb 100644 --- a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py @@ -276,7 +276,7 @@ def test_gaslimit_upper_plus_regular_payload(spec, state): # execution payload execution_payload = build_empty_execution_payload(spec, state) execution_payload.gas_limit = execution_payload.gas_limit + \ - execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR + execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) @@ -291,7 +291,7 @@ def test_gaslimit_upper_regular_payload(spec, state): # execution payload execution_payload = build_empty_execution_payload(spec, state) execution_payload.gas_limit = execution_payload.gas_limit + \ - execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR - uint64(1) + execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR - uint64(1) yield from run_execution_payload_processing(spec, state, execution_payload) @@ -306,7 +306,7 @@ def test_gaslimit_lower_minus_regular_payload(spec, state): # execution payload execution_payload = build_empty_execution_payload(spec, state) execution_payload.gas_limit = execution_payload.gas_limit - \ - execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR + execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) @@ -321,7 +321,7 @@ def test_gaslimit_lower_regular_payload(spec, state): # execution payload execution_payload = build_empty_execution_payload(spec, state) execution_payload.gas_limit = execution_payload.gas_limit - \ - execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR + uint64(1) + execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR + uint64(1) yield from run_execution_payload_processing(spec, state, execution_payload) diff --git a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_block.py b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_block.py index f096fb138..983cab89a 100644 --- a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_block.py +++ b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_block.py @@ -1,8 +1,8 @@ -from eth2spec.utils.ssz.ssz_typing import uint64, uint256 +from eth2spec.utils.ssz.ssz_typing import uint256 from eth2spec.test.helpers.block import ( prepare_empty_pow_block ) -from eth2spec.test.context import spec_state_test, expect_assertion_error, with_merge_and_later +from eth2spec.test.context import spec_state_test, with_merge_and_later def create_transition_store(spec): @@ -139,7 +139,8 @@ def test_process_merge_execution_payload_fail_block_lookup(spec, state): block.total_difficulty = transition_store.terminal_total_difficulty payload = spec.ExecutionPayload() payload.parent_hash = spec.Hash32(spec.hash(b'02')) - yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload, block_lookup_success=False) + yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload, + block_lookup_success=False) @with_merge_and_later @@ -155,7 +156,8 @@ def test_process_merge_execution_payload_fail_parent_block_lookup(spec, state): block.total_difficulty = transition_store.terminal_total_difficulty payload = spec.ExecutionPayload() payload.parent_hash = block.block_hash - yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload, block_lookup_success=False) + yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload, + block_lookup_success=False) @with_merge_and_later @@ -171,4 +173,4 @@ def test_process_merge_execution_payload_fail_after_terminal(spec, state): block.total_difficulty = transition_store.terminal_total_difficulty + 1 payload = spec.ExecutionPayload() payload.parent_hash = block.block_hash - yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload, valid=False) \ No newline at end of file + yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload, valid=False) diff --git a/tests/core/pyspec/eth2spec/test/merge/unit/test_unit.py b/tests/core/pyspec/eth2spec/test/merge/unit/test_unit.py index 24caa03b1..ff153d9ab 100644 --- a/tests/core/pyspec/eth2spec/test/merge/unit/test_unit.py +++ b/tests/core/pyspec/eth2spec/test/merge/unit/test_unit.py @@ -1,4 +1,4 @@ -from eth2spec.utils.ssz.ssz_typing import uint64, uint256 +from eth2spec.utils.ssz.ssz_typing import uint256 from eth2spec.test.helpers.execution_payload import ( build_empty_execution_payload, build_state_with_incomplete_transition, @@ -7,7 +7,7 @@ from eth2spec.test.helpers.execution_payload import ( from eth2spec.test.helpers.block import ( prepare_empty_pow_block ) -from eth2spec.test.context import spec_state_test, expect_assertion_error, with_merge_and_later +from eth2spec.test.context import spec_state_test, with_merge_and_later @with_merge_and_later @@ -108,7 +108,8 @@ def compute_terminal_total_difficulty_reference(spec) -> uint256: seconds_per_voting_period = spec.EPOCHS_PER_ETH1_VOTING_PERIOD * spec.SLOTS_PER_EPOCH * spec.config.SECONDS_PER_SLOT pow_blocks_per_voting_period = seconds_per_voting_period // spec.config.SECONDS_PER_ETH1_BLOCK pow_blocks_to_merge = spec.TARGET_SECONDS_TO_MERGE // spec.config.SECONDS_PER_ETH1_BLOCK - pow_blocks_after_anchor_block = spec.config.ETH1_FOLLOW_DISTANCE + pow_blocks_per_voting_period + pow_blocks_to_merge + pow_blocks_after_anchor_block = spec.config.ETH1_FOLLOW_DISTANCE + pow_blocks_per_voting_period +\ + pow_blocks_to_merge return spec.config.MIN_ANCHOR_POW_BLOCK_DIFFICULTY * uint256(pow_blocks_after_anchor_block) From b99720ec26c79d393ef522d8e712981cc0a86918 Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Tue, 14 Sep 2021 17:56:49 +0300 Subject: [PATCH 05/19] Renamed test_on_block -> test_terminal_validity to avoid misleading as generator test --- .../fork_choice/{test_on_block.py => test_terminal_validity.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/core/pyspec/eth2spec/test/merge/fork_choice/{test_on_block.py => test_terminal_validity.py} (100%) diff --git a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_block.py b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_terminal_validity.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_block.py rename to tests/core/pyspec/eth2spec/test/merge/fork_choice/test_terminal_validity.py From 829c6e732597fe48ded17bdb656d806d9eb2e0e5 Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Wed, 15 Sep 2021 18:27:20 +0300 Subject: [PATCH 06/19] Enable genesis client tests for MERGE --- tests/generators/genesis/main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/generators/genesis/main.py b/tests/generators/genesis/main.py index 8e0294bf0..1f36afd4b 100644 --- a/tests/generators/genesis/main.py +++ b/tests/generators/genesis/main.py @@ -1,5 +1,5 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators -from eth2spec.test.helpers.constants import PHASE0, ALTAIR +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, MERGE if __name__ == "__main__": @@ -8,9 +8,12 @@ if __name__ == "__main__": 'validity', ]} altair_mods = phase_0_mods + # we have new unconditional lines in `initialize_beacon_state_from_eth1` and we want to test it + merge_mods = altair_mods all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, + MERGE: merge_mods, } run_state_test_generators(runner_name="genesis", all_mods=all_mods) From 9bd95c4709d0260a1667e4224355ad87168a560d Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Tue, 21 Sep 2021 15:18:46 +0300 Subject: [PATCH 07/19] Changed terminal total difficulty to have some difficulty range for tests without overflow --- configs/minimal.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/minimal.yaml b/configs/minimal.yaml index 8da3260f5..d164dc2e2 100644 --- a/configs/minimal.yaml +++ b/configs/minimal.yaml @@ -5,8 +5,8 @@ PRESET_BASE: 'minimal' # Transition # --------------------------------------------------------------- -# TBD, 2**256-1 is a placeholder -TERMINAL_TOTAL_DIFFICULTY: 115792089237316195423570985008687907853269984665640564039457584007913129639935 +# TBD, 2**256-2**10 is a placeholder +TERMINAL_TOTAL_DIFFICULTY: 115792089237316195423570985008687907853269984665640564039457584007913129638912 # Genesis From cdcf3660074f519df9f25f4b3e48031f12471565 Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Tue, 21 Sep 2021 15:20:19 +0300 Subject: [PATCH 08/19] merge tests updated to be in line with transition_store removal --- .../pyspec/eth2spec/test/helpers/block.py | 2 - .../test_process_execution_payload.py | 2 +- .../fork_choice/test_terminal_validity.py | 79 ++++++++----------- .../eth2spec/test/merge/unit/test_unit.py | 22 ------ 4 files changed, 32 insertions(+), 73 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/block.py b/tests/core/pyspec/eth2spec/test/helpers/block.py index 9e704cb49..edbe43c13 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/block.py +++ b/tests/core/pyspec/eth2spec/test/helpers/block.py @@ -128,8 +128,6 @@ def prepare_empty_pow_block(spec): return spec.PowBlock( block_hash=spec.Hash32(), parent_hash=spec.Hash32(), - is_processed=False, - is_valid=True, total_difficulty=uint256(0), difficulty=uint256(0) ) diff --git a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py index 8231c0223..525e42d9a 100644 --- a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py @@ -247,7 +247,7 @@ def test_bad_randao_first_payload(spec, state): # still valid because randao is ignored on this stage execution_payload.random = bad_randao - yield from run_execution_payload_processing(spec, state, execution_payload) + yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) @with_merge_and_later diff --git a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_terminal_validity.py b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_terminal_validity.py index 983cab89a..605375880 100644 --- a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_terminal_validity.py +++ b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_terminal_validity.py @@ -5,21 +5,21 @@ from eth2spec.test.helpers.block import ( from eth2spec.test.context import spec_state_test, with_merge_and_later -def create_transition_store(spec): - anchor_block = prepare_empty_pow_block(spec) - transition_store = spec.get_transition_store(anchor_block) - return transition_store - - class BlockNotFoundException(Exception): pass -def run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload, +# Copy of conditional merge part of `on_block(store: Store, signed_block: SignedBeaconBlock)` handler +def process_merge_execution_payload(spec, execution_payload): + pow_block = spec.get_pow_block(execution_payload.parent_hash) + pow_parent = spec.get_pow_block(pow_block.parent_hash) + assert spec.is_valid_terminal_pow_block(pow_block, pow_parent) + + +def run_process_merge_execution_payload(spec, block, parent_block, payload, valid=True, block_lookup_success=True): """ Run ``process_merge_execution_payload``, yielding: - - transition store ('transition_store') - current block ('block') - parent block ('parent_block') - execution payload ('payload') @@ -27,7 +27,6 @@ def run_process_merge_execution_payload(spec, transition_store, block, parent_bl If ``block_lookup_success == False``, run expecting ``BlockNotFoundException`` """ - yield 'transition_store', transition_store yield 'block', block yield 'parent_block', parent_block yield 'payload', payload @@ -46,7 +45,7 @@ def run_process_merge_execution_payload(spec, transition_store, block, parent_bl exception_caught = False block_not_found_exception_caught = False try: - spec.process_merge_execution_payload(transition_store, payload) + process_merge_execution_payload(spec, payload) except BlockNotFoundException: block_not_found_exception_caught = True except AssertionError: @@ -68,109 +67,93 @@ def run_process_merge_execution_payload(spec, transition_store, block, parent_bl @with_merge_and_later @spec_state_test -def test_valid_terminal_pow_block_success_valid_fail_invalid(spec, state): - transition_store = create_transition_store(spec) +def test_valid_terminal_pow_block_success_valid(spec, state): parent_block = prepare_empty_pow_block(spec) - parent_block.total_difficulty = transition_store.terminal_total_difficulty - uint256(1) + parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) block = prepare_empty_pow_block(spec) block.parent_hash = parent_block.block_hash - block.total_difficulty = transition_store.terminal_total_difficulty + block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - assert spec.is_valid_terminal_pow_block(transition_store, block, parent_block) - - block.is_valid = False - assert not spec.is_valid_terminal_pow_block(transition_store, block, parent_block) + assert spec.is_valid_terminal_pow_block(block, parent_block) @with_merge_and_later @spec_state_test def test_valid_terminal_pow_block_fail_before_terminal(spec, state): - transition_store = create_transition_store(spec) parent_block = prepare_empty_pow_block(spec) - parent_block.total_difficulty = transition_store.terminal_total_difficulty - uint256(2) + parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(2) block = prepare_empty_pow_block(spec) block.parent_hash = parent_block.block_hash - block.total_difficulty = transition_store.terminal_total_difficulty - uint256(1) + block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) - assert not spec.is_valid_terminal_pow_block(transition_store, block, parent_block) + assert not spec.is_valid_terminal_pow_block(block, parent_block) @with_merge_and_later @spec_state_test def test_valid_terminal_pow_block_fail_just_after_terminal(spec, state): - transition_store = create_transition_store(spec) parent_block = prepare_empty_pow_block(spec) - parent_block.total_difficulty = transition_store.terminal_total_difficulty + parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY block = prepare_empty_pow_block(spec) block.parent_hash = parent_block.block_hash - block.total_difficulty = transition_store.terminal_total_difficulty + uint256(1) + block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + uint256(1) - assert not spec.is_valid_terminal_pow_block(transition_store, block, parent_block) + assert not spec.is_valid_terminal_pow_block(block, parent_block) @with_merge_and_later @spec_state_test def test_process_merge_execution_payload_success(spec, state): - transition_store = create_transition_store(spec) parent_block = prepare_empty_pow_block(spec) parent_block.block_hash = spec.Hash32(spec.hash(b'01')) - parent_block.total_difficulty = transition_store.terminal_total_difficulty - uint256(1) + parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) block = prepare_empty_pow_block(spec) block.parent_hash = parent_block.block_hash - block.is_processed = True - block.total_difficulty = transition_store.terminal_total_difficulty + block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY payload = spec.ExecutionPayload() payload.parent_hash = block.block_hash - yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload) - block.is_processed = False - yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload, valid=False) + yield from run_process_merge_execution_payload(spec, block, parent_block, payload) @with_merge_and_later @spec_state_test def test_process_merge_execution_payload_fail_block_lookup(spec, state): - transition_store = create_transition_store(spec) parent_block = prepare_empty_pow_block(spec) parent_block.block_hash = spec.Hash32(spec.hash(b'01')) - parent_block.total_difficulty = transition_store.terminal_total_difficulty - uint256(1) + parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) block = prepare_empty_pow_block(spec) block.parent_hash = parent_block.block_hash - block.is_processed = True - block.total_difficulty = transition_store.terminal_total_difficulty + block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY payload = spec.ExecutionPayload() payload.parent_hash = spec.Hash32(spec.hash(b'02')) - yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload, + yield from run_process_merge_execution_payload(spec, block, parent_block, payload, block_lookup_success=False) @with_merge_and_later @spec_state_test def test_process_merge_execution_payload_fail_parent_block_lookup(spec, state): - transition_store = create_transition_store(spec) parent_block = prepare_empty_pow_block(spec) parent_block.block_hash = spec.Hash32(spec.hash(b'01')) - parent_block.total_difficulty = transition_store.terminal_total_difficulty - uint256(1) + parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) block = prepare_empty_pow_block(spec) block.parent_hash = spec.Hash32(spec.hash(b'00')) - block.is_processed = True - block.total_difficulty = transition_store.terminal_total_difficulty + block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY payload = spec.ExecutionPayload() payload.parent_hash = block.block_hash - yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload, + yield from run_process_merge_execution_payload(spec, block, parent_block, payload, block_lookup_success=False) @with_merge_and_later @spec_state_test def test_process_merge_execution_payload_fail_after_terminal(spec, state): - transition_store = create_transition_store(spec) parent_block = prepare_empty_pow_block(spec) parent_block.block_hash = spec.Hash32(spec.hash(b'01')) - parent_block.total_difficulty = transition_store.terminal_total_difficulty + parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY block = prepare_empty_pow_block(spec) block.parent_hash = parent_block.block_hash - block.is_processed = True - block.total_difficulty = transition_store.terminal_total_difficulty + 1 + block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + 1 payload = spec.ExecutionPayload() payload.parent_hash = block.block_hash - yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload, valid=False) + yield from run_process_merge_execution_payload(spec, block, parent_block, payload, valid=False) diff --git a/tests/core/pyspec/eth2spec/test/merge/unit/test_unit.py b/tests/core/pyspec/eth2spec/test/merge/unit/test_unit.py index ff153d9ab..bd968474e 100644 --- a/tests/core/pyspec/eth2spec/test/merge/unit/test_unit.py +++ b/tests/core/pyspec/eth2spec/test/merge/unit/test_unit.py @@ -1,12 +1,8 @@ -from eth2spec.utils.ssz.ssz_typing import uint256 from eth2spec.test.helpers.execution_payload import ( build_empty_execution_payload, build_state_with_incomplete_transition, build_state_with_complete_transition, ) -from eth2spec.test.helpers.block import ( - prepare_empty_pow_block -) from eth2spec.test.context import spec_state_test, with_merge_and_later @@ -102,21 +98,3 @@ def test_success_execution_enabled_true_true(spec, state): body = spec.BeaconBlockBody() body.execution_payload = execution_payload assert spec.is_execution_enabled(state, body) - - -def compute_terminal_total_difficulty_reference(spec) -> uint256: - seconds_per_voting_period = spec.EPOCHS_PER_ETH1_VOTING_PERIOD * spec.SLOTS_PER_EPOCH * spec.config.SECONDS_PER_SLOT - pow_blocks_per_voting_period = seconds_per_voting_period // spec.config.SECONDS_PER_ETH1_BLOCK - pow_blocks_to_merge = spec.TARGET_SECONDS_TO_MERGE // spec.config.SECONDS_PER_ETH1_BLOCK - pow_blocks_after_anchor_block = spec.config.ETH1_FOLLOW_DISTANCE + pow_blocks_per_voting_period +\ - pow_blocks_to_merge - return spec.config.MIN_ANCHOR_POW_BLOCK_DIFFICULTY * uint256(pow_blocks_after_anchor_block) - - -@with_merge_and_later -@spec_state_test -def test_zero_difficulty(spec, state): - anchor_pow_block = prepare_empty_pow_block(spec) - reference_td = compute_terminal_total_difficulty_reference(spec) - - assert spec.compute_terminal_total_difficulty(anchor_pow_block) == reference_td From b1aa2279838219b91c9c05a26da89b59bf0f8611 Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Thu, 23 Sep 2021 22:22:34 +0300 Subject: [PATCH 09/19] Added `on_merge_block` client tests --- specs/merge/fork-choice.md | 3 +- .../merge/fork_choice/test_on_merge_block.py | 162 ++++++++++++++++++ tests/generators/fork_choice/main.py | 10 +- 3 files changed, 171 insertions(+), 4 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md index 8051ab3eb..77e279e67 100644 --- a/specs/merge/fork-choice.md +++ b/specs/merge/fork-choice.md @@ -70,8 +70,7 @@ def finalize_block(self: ExecutionEngine, block_hash: Hash32) -> bool: ### `PowBlock` ```python -@dataclass -class PowBlock(object): +class PowBlock(Container): block_hash: Hash32 parent_hash: Hash32 total_difficulty: uint256 diff --git a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py new file mode 100644 index 000000000..b13628bbd --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py @@ -0,0 +1,162 @@ +from eth2spec.utils.ssz.ssz_typing import uint256 +from eth2spec.test.context import spec_state_test, with_phases, MERGE +from eth2spec.test.helpers.block import ( + build_empty_block_for_next_slot +) +from eth2spec.test.helpers.fork_choice import ( + get_genesis_forkchoice_store_and_block, + on_tick_and_append_step, + tick_and_add_block, +) +from eth2spec.test.helpers.state import ( + state_transition_and_sign_block, +) +from eth2spec.test.helpers.block import ( + prepare_empty_pow_block +) + + +def with_pow_block_patch(spec, blocks, func): + def get_pow_block(hash: spec.Bytes32) -> spec.PowBlock: + for block in blocks: + if block.block_hash == hash: + return block + raise Exception("Block not found") + get_pow_block_backup = spec.get_pow_block + spec.get_pow_block = get_pow_block + + class AtomicBoolean(): + value = False + is_called = AtomicBoolean() + + def wrap(flag: AtomicBoolean): + func() + flag.value = True + + try: + wrap(is_called) + finally: + spec.get_pow_block = get_pow_block_backup + assert is_called.value + + +@with_phases([MERGE]) +@spec_state_test +def test_all_valid(spec, state): + test_steps = [] + # Initialization + store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) + yield 'anchor_state', state + yield 'anchor_block', anchor_block + current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time + on_tick_and_append_step(spec, store, current_time, test_steps) + assert store.time == current_time + + parent_block = prepare_empty_pow_block(spec) + parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) + block = prepare_empty_pow_block(spec) + block.parent_hash = parent_block.block_hash + block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + pow_blocks = [block, parent_block] + yield 'pow_blocks', pow_blocks + + def run_func(): + 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) + # valid + assert spec.get_head(store) == signed_block.message.hash_tree_root() + + with_pow_block_patch(spec, pow_blocks, run_func) + yield 'steps', test_steps + + +@with_phases([MERGE]) +@spec_state_test +def test_block_lookup_failed(spec, state): + test_steps = [] + # Initialization + store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) + yield 'anchor_state', state + yield 'anchor_block', anchor_block + current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time + on_tick_and_append_step(spec, store, current_time, test_steps) + assert store.time == current_time + + + parent_block = prepare_empty_pow_block(spec) + parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) + pow_blocks = [parent_block] + yield 'pow_blocks', pow_blocks + + def run_func(): + 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) + # invalid + assert spec.get_head(store) == anchor_block.state_root + + with_pow_block_patch(spec, pow_blocks, run_func) + yield 'steps', test_steps + + +@with_phases([MERGE]) +@spec_state_test +def test_too_early_for_merge(spec, state): + test_steps = [] + # Initialization + store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) + yield 'anchor_state', state + yield 'anchor_block', anchor_block + current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time + on_tick_and_append_step(spec, store, current_time, test_steps) + assert store.time == current_time + + parent_block = prepare_empty_pow_block(spec) + parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(2) + block = prepare_empty_pow_block(spec) + block.parent_hash = parent_block.block_hash + block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) + pow_blocks = [block, parent_block] + yield 'pow_blocks', pow_blocks + + def run_func(): + 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) + # invalid + assert spec.get_head(store) == anchor_block.state_root + + with_pow_block_patch(spec, pow_blocks, run_func) + yield 'steps', test_steps + + +@with_phases([MERGE]) +@spec_state_test +def test_too_late_for_merge(spec, state): + test_steps = [] + # Initialization + store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) + yield 'anchor_state', state + yield 'anchor_block', anchor_block + current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time + on_tick_and_append_step(spec, store, current_time, test_steps) + assert store.time == current_time + + parent_block = prepare_empty_pow_block(spec) + parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + block = prepare_empty_pow_block(spec) + block.parent_hash = parent_block.block_hash + block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + uint256(1) + pow_blocks = [block, parent_block] + yield 'pow_blocks', pow_blocks + + def run_func(): + 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) + # invalid + assert spec.get_head(store) == anchor_block.state_root + + with_pow_block_patch(spec, pow_blocks, run_func) + yield 'steps', test_steps diff --git a/tests/generators/fork_choice/main.py b/tests/generators/fork_choice/main.py index 148174120..94516a39c 100644 --- a/tests/generators/fork_choice/main.py +++ b/tests/generators/fork_choice/main.py @@ -9,8 +9,14 @@ if __name__ == "__main__": ]} # No additional Altair specific finality tests, yet. altair_mods = phase_0_mods - # No specific Merge tests yet. - merge_mods = altair_mods + # For merge `on_merge_block` test kind added with `pow_block_N.ssz` files with several + # PowBlock's which should be resolved by `get_pow_block(hash: Hash32) -> PowBlock` function + merge_mods = { + **{key: 'eth2spec.test.merge.fork_choice.test_' + key for key in [ + 'on_merge_block', + ]}, + **altair_mods, + } all_mods = { PHASE0: phase_0_mods, From 239653ea018366d57a4177f17912764ae4f64164 Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Thu, 23 Sep 2021 22:24:02 +0300 Subject: [PATCH 10/19] Fixed lint errors for `test_on_merge_block.py` --- .../eth2spec/test/merge/fork_choice/test_on_merge_block.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py index b13628bbd..f7bd0341c 100644 --- a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py +++ b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py @@ -83,7 +83,6 @@ def test_block_lookup_failed(spec, state): on_tick_and_append_step(spec, store, current_time, test_steps) assert store.time == current_time - parent_block = prepare_empty_pow_block(spec) parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) pow_blocks = [parent_block] From 1ecfc4016c37669e69d5ec67de814c134ac323c4 Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Thu, 23 Sep 2021 23:10:29 +0300 Subject: [PATCH 11/19] Polishing merge tests --- tests/core/pyspec/eth2spec/test/exceptions.py | 4 ++ .../pyspec/eth2spec/test/helpers/block.py | 10 ---- .../test/helpers/execution_payload.py | 12 ----- .../eth2spec/test/helpers/fork_choice.py | 10 ++++ .../test_process_execution_payload.py | 54 +++++-------------- .../merge/fork_choice/test_on_merge_block.py | 2 +- .../merge/{unit => unittests}/__init__.py | 0 .../test_terminal_validity.py | 11 ++-- .../test_transition.py} | 14 ++--- tests/formats/fork_choice/README.md | 8 +++ 10 files changed, 47 insertions(+), 78 deletions(-) rename tests/core/pyspec/eth2spec/test/merge/{unit => unittests}/__init__.py (100%) rename tests/core/pyspec/eth2spec/test/merge/{fork_choice => unittests}/test_terminal_validity.py (96%) rename tests/core/pyspec/eth2spec/test/merge/{unit/test_unit.py => unittests/test_transition.py} (88%) diff --git a/tests/core/pyspec/eth2spec/test/exceptions.py b/tests/core/pyspec/eth2spec/test/exceptions.py index c553ec374..fcabd88f3 100644 --- a/tests/core/pyspec/eth2spec/test/exceptions.py +++ b/tests/core/pyspec/eth2spec/test/exceptions.py @@ -1,2 +1,6 @@ class SkippedTest(Exception): ... + + +class BlockNotFoundException(Exception): + ... diff --git a/tests/core/pyspec/eth2spec/test/helpers/block.py b/tests/core/pyspec/eth2spec/test/helpers/block.py index edbe43c13..78b90b165 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/block.py +++ b/tests/core/pyspec/eth2spec/test/helpers/block.py @@ -4,7 +4,6 @@ from eth2spec.test.helpers.keys import privkeys from eth2spec.utils import bls from eth2spec.utils.bls import only_with_bls from eth2spec.utils.ssz.ssz_impl import hash_tree_root -from eth2spec.utils.ssz.ssz_typing import uint256 def get_proposer_index_maybe(spec, state, slot, proposer_index=None): @@ -122,12 +121,3 @@ def get_state_and_beacon_parent_root_at_slot(spec, state, slot): previous_block_header.state_root = hash_tree_root(state) beacon_parent_root = hash_tree_root(previous_block_header) return state, beacon_parent_root - - -def prepare_empty_pow_block(spec): - return spec.PowBlock( - block_hash=spec.Hash32(), - parent_hash=spec.Hash32(), - total_difficulty=uint256(0), - difficulty=uint256(0) - ) diff --git a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py index 453c49d3e..6126346a9 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py @@ -1,6 +1,3 @@ -from remerkleable.byte_arrays import ByteVector - - 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. @@ -69,12 +66,3 @@ def build_state_with_execution_payload_header(spec, state, execution_payload_hea pre_state.latest_execution_payload_header = execution_payload_header return pre_state - - -# damages last byte of the data by changing one bit -def screw_up_bytes(data: ByteVector): - assert len(data) > 0 - length = data.vector_length() - raw_data = data.encode_bytes() - raw_data = raw_data[0:len(raw_data) - 1] + bytes([(raw_data[len(raw_data) - 1] ^ 1)]) - return ByteVector[length](*raw_data) diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py index 65d6975f2..aaaf5c23b 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py @@ -1,4 +1,5 @@ from eth_utils import encode_hex +from eth2spec.utils.ssz.ssz_typing import uint256 from eth2spec.test.helpers.attestations import ( next_epoch_with_attestations, next_slots_with_attestations, @@ -227,3 +228,12 @@ def apply_next_slots_with_attestations(spec, assert store.block_states[block_root].hash_tree_root() == post_state.hash_tree_root() return post_state, store, last_signed_block + + +def prepare_empty_pow_block(spec): + return spec.PowBlock( + block_hash=spec.Hash32(), + parent_hash=spec.Hash32(), + total_difficulty=uint256(0), + difficulty=uint256(0) + ) diff --git a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py index 525e42d9a..572326711 100644 --- a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py @@ -1,12 +1,9 @@ -from remerkleable.byte_arrays import Bytes32 from eth2spec.utils.ssz.ssz_typing import uint64 - from eth2spec.test.helpers.execution_payload import ( build_empty_execution_payload, get_execution_payload_header, build_state_with_incomplete_transition, - build_state_with_complete_transition, - screw_up_bytes + build_state_with_complete_transition ) from eth2spec.test.context import spec_state_test, expect_assertion_error, with_merge_and_later from eth2spec.test.helpers.state import next_slot @@ -233,39 +230,6 @@ def test_bad_timestamp_regular_payload(spec, state): yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) -@with_merge_and_later -@spec_state_test -def test_bad_randao_first_payload(spec, state): - # pre-state - state = build_state_with_incomplete_transition(spec, state) - next_slot(spec, state) - - # execution payload - execution_payload = build_empty_execution_payload(spec, state) - good_randao: Bytes32 = execution_payload.random - bad_randao = screw_up_bytes(good_randao) - # still valid because randao is ignored on this stage - execution_payload.random = bad_randao - - yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) - - -@with_merge_and_later -@spec_state_test -def test_bad_randao_regular_payload(spec, state): - # pre-state - state = build_state_with_complete_transition(spec, state) - next_slot(spec, state) - - # execution payload - execution_payload = build_empty_execution_payload(spec, state) - good_randao: Bytes32 = execution_payload.random - bad_randao = screw_up_bytes(good_randao) - execution_payload.random = bad_randao - - yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) - - @with_merge_and_later @spec_state_test def test_gaslimit_zero_first_payload(spec, state): @@ -303,8 +267,10 @@ def test_gaslimit_upper_plus_regular_payload(spec, state): # execution payload execution_payload = build_empty_execution_payload(spec, state) - execution_payload.gas_limit = execution_payload.gas_limit + \ + execution_payload.gas_limit = ( + execution_payload.gas_limit + execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR + ) yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) @@ -318,8 +284,10 @@ def test_gaslimit_upper_regular_payload(spec, state): # execution payload execution_payload = build_empty_execution_payload(spec, state) - execution_payload.gas_limit = execution_payload.gas_limit + \ + execution_payload.gas_limit = ( + execution_payload.gas_limit + execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR - uint64(1) + ) yield from run_execution_payload_processing(spec, state, execution_payload) @@ -333,8 +301,10 @@ def test_gaslimit_lower_minus_regular_payload(spec, state): # execution payload execution_payload = build_empty_execution_payload(spec, state) - execution_payload.gas_limit = execution_payload.gas_limit - \ + execution_payload.gas_limit = ( + execution_payload.gas_limit - execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR + ) yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) @@ -348,8 +318,10 @@ def test_gaslimit_lower_regular_payload(spec, state): # execution payload execution_payload = build_empty_execution_payload(spec, state) - execution_payload.gas_limit = execution_payload.gas_limit - \ + execution_payload.gas_limit = ( + execution_payload.gas_limit - execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR + uint64(1) + ) yield from run_execution_payload_processing(spec, state, execution_payload) diff --git a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py index f7bd0341c..f3e4129b3 100644 --- a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py +++ b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py @@ -11,7 +11,7 @@ from eth2spec.test.helpers.fork_choice import ( from eth2spec.test.helpers.state import ( state_transition_and_sign_block, ) -from eth2spec.test.helpers.block import ( +from eth2spec.test.helpers.fork_choice import ( prepare_empty_pow_block ) diff --git a/tests/core/pyspec/eth2spec/test/merge/unit/__init__.py b/tests/core/pyspec/eth2spec/test/merge/unittests/__init__.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/merge/unit/__init__.py rename to tests/core/pyspec/eth2spec/test/merge/unittests/__init__.py diff --git a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_terminal_validity.py b/tests/core/pyspec/eth2spec/test/merge/unittests/test_terminal_validity.py similarity index 96% rename from tests/core/pyspec/eth2spec/test/merge/fork_choice/test_terminal_validity.py rename to tests/core/pyspec/eth2spec/test/merge/unittests/test_terminal_validity.py index 605375880..87ee190cc 100644 --- a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_terminal_validity.py +++ b/tests/core/pyspec/eth2spec/test/merge/unittests/test_terminal_validity.py @@ -1,16 +1,13 @@ +from eth2spec.test.exceptions import BlockNotFoundException from eth2spec.utils.ssz.ssz_typing import uint256 -from eth2spec.test.helpers.block import ( +from eth2spec.test.helpers.fork_choice import ( prepare_empty_pow_block ) from eth2spec.test.context import spec_state_test, with_merge_and_later -class BlockNotFoundException(Exception): - pass - - # Copy of conditional merge part of `on_block(store: Store, signed_block: SignedBeaconBlock)` handler -def process_merge_execution_payload(spec, execution_payload): +def validate_transition_execution_payload(spec, execution_payload): pow_block = spec.get_pow_block(execution_payload.parent_hash) pow_parent = spec.get_pow_block(pow_block.parent_hash) assert spec.is_valid_terminal_pow_block(pow_block, pow_parent) @@ -45,7 +42,7 @@ def run_process_merge_execution_payload(spec, block, parent_block, payload, exception_caught = False block_not_found_exception_caught = False try: - process_merge_execution_payload(spec, payload) + validate_transition_execution_payload(spec, payload) except BlockNotFoundException: block_not_found_exception_caught = True except AssertionError: diff --git a/tests/core/pyspec/eth2spec/test/merge/unit/test_unit.py b/tests/core/pyspec/eth2spec/test/merge/unittests/test_transition.py similarity index 88% rename from tests/core/pyspec/eth2spec/test/merge/unit/test_unit.py rename to tests/core/pyspec/eth2spec/test/merge/unittests/test_transition.py index bd968474e..03e6ae874 100644 --- a/tests/core/pyspec/eth2spec/test/merge/unit/test_unit.py +++ b/tests/core/pyspec/eth2spec/test/merge/unittests/test_transition.py @@ -22,7 +22,7 @@ def test_success_merge_complete(spec, state): @with_merge_and_later @spec_state_test -def test_fail_merge_block_false_false(spec, state): +def test_fail_merge_block(spec, state): state = build_state_with_complete_transition(spec, state) execution_payload = spec.ExecutionPayload() body = spec.BeaconBlockBody() @@ -32,7 +32,7 @@ def test_fail_merge_block_false_false(spec, state): @with_merge_and_later @spec_state_test -def test_fail_merge_block_false_true(spec, state): +def test_fail_merge_block_complete_transition(spec, state): state = build_state_with_complete_transition(spec, state) execution_payload = build_empty_execution_payload(spec, state) body = spec.BeaconBlockBody() @@ -42,7 +42,7 @@ def test_fail_merge_block_false_true(spec, state): @with_merge_and_later @spec_state_test -def test_fail_merge_block_true_false(spec, state): +def test_fail_merge_block_no_execution_payload(spec, state): state = build_state_with_incomplete_transition(spec, state) execution_payload = spec.ExecutionPayload() body = spec.BeaconBlockBody() @@ -62,7 +62,7 @@ def test_success_merge_block(spec, state): @with_merge_and_later @spec_state_test -def test_fail_execution_enabled_false_false(spec, state): +def test_failed_execution_enabled(spec, state): state = build_state_with_incomplete_transition(spec, state) execution_payload = spec.ExecutionPayload() body = spec.BeaconBlockBody() @@ -72,7 +72,7 @@ def test_fail_execution_enabled_false_false(spec, state): @with_merge_and_later @spec_state_test -def test_success_execution_enabled_true_false(spec, state): +def test_success_execution_enabled_before_terminal(spec, state): state = build_state_with_incomplete_transition(spec, state) execution_payload = build_empty_execution_payload(spec, state) body = spec.BeaconBlockBody() @@ -82,7 +82,7 @@ def test_success_execution_enabled_true_false(spec, state): @with_merge_and_later @spec_state_test -def test_success_execution_enabled_false_true(spec, state): +def test_success_execution_enabled_no_execution_payload(spec, state): state = build_state_with_complete_transition(spec, state) execution_payload = spec.ExecutionPayload() body = spec.BeaconBlockBody() @@ -92,7 +92,7 @@ def test_success_execution_enabled_false_true(spec, state): @with_merge_and_later @spec_state_test -def test_success_execution_enabled_true_true(spec, state): +def test_success_execution_enabled(spec, state): state = build_state_with_complete_transition(spec, state) execution_payload = build_empty_execution_payload(spec, state) body = spec.BeaconBlockBody() diff --git a/tests/formats/fork_choice/README.md b/tests/formats/fork_choice/README.md index cfc86776d..2f006b07e 100644 --- a/tests/formats/fork_choice/README.md +++ b/tests/formats/fork_choice/README.md @@ -69,6 +69,14 @@ The file is located in the same folder (see below). After this step, the `store` object may have been updated. +#### `on_merge_block` execution + +Adds `PowBlock` data which is required for executing `on_block(store, block)`. +Number of blocks is stored in `meta.yaml`, block file names are `pow_block_.ssz_snappy`. +The file is located in the same folder. +PowBlocks should be used as return values for `get_pow_block(hash: Hash32) -> PowBlock` function if hashes match. + + #### Checks step The checks to verify the current status of `store`. From f3f1c86a5740d3c591bfb891038684d5ba20b2e4 Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Thu, 23 Sep 2021 23:13:04 +0300 Subject: [PATCH 12/19] Fixed imports in merge tests --- .../merge/block_processing/test_process_execution_payload.py | 2 +- .../eth2spec/test/merge/fork_choice/test_on_merge_block.py | 2 +- .../eth2spec/test/merge/unittests/test_terminal_validity.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py index 572326711..bc833d107 100644 --- a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py @@ -3,7 +3,7 @@ from eth2spec.test.helpers.execution_payload import ( build_empty_execution_payload, get_execution_payload_header, build_state_with_incomplete_transition, - build_state_with_complete_transition + build_state_with_complete_transition, ) from eth2spec.test.context import spec_state_test, expect_assertion_error, with_merge_and_later from eth2spec.test.helpers.state import next_slot diff --git a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py index f3e4129b3..02b2a5942 100644 --- a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py +++ b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py @@ -12,7 +12,7 @@ from eth2spec.test.helpers.state import ( state_transition_and_sign_block, ) from eth2spec.test.helpers.fork_choice import ( - prepare_empty_pow_block + prepare_empty_pow_block, ) diff --git a/tests/core/pyspec/eth2spec/test/merge/unittests/test_terminal_validity.py b/tests/core/pyspec/eth2spec/test/merge/unittests/test_terminal_validity.py index 87ee190cc..6c2a623b2 100644 --- a/tests/core/pyspec/eth2spec/test/merge/unittests/test_terminal_validity.py +++ b/tests/core/pyspec/eth2spec/test/merge/unittests/test_terminal_validity.py @@ -1,7 +1,7 @@ from eth2spec.test.exceptions import BlockNotFoundException from eth2spec.utils.ssz.ssz_typing import uint256 from eth2spec.test.helpers.fork_choice import ( - prepare_empty_pow_block + prepare_empty_pow_block, ) from eth2spec.test.context import spec_state_test, with_merge_and_later From 457b0396ddee2c6bbb11bb46916fa068c81d4830 Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Fri, 24 Sep 2021 20:25:18 +0300 Subject: [PATCH 13/19] update PowBlock generation with random hash --- .../core/pyspec/eth2spec/test/helpers/fork_choice.py | 12 ++++++++---- .../test/merge/unittests/test_terminal_validity.py | 6 ------ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py index aaaf5c23b..b26417e8f 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py @@ -1,3 +1,4 @@ +from random import Random from eth_utils import encode_hex from eth2spec.utils.ssz.ssz_typing import uint256 from eth2spec.test.helpers.attestations import ( @@ -23,9 +24,12 @@ def add_block_to_store(spec, store, signed_block): spec.on_block(store, signed_block) -def tick_and_add_block(spec, store, signed_block, test_steps, valid=True, allow_invalid_attestations=False): +def tick_and_add_block(spec, store, signed_block, test_steps, valid=True, allow_invalid_attestations=False, + merge_block=False): pre_state = store.block_states[signed_block.message.parent_root] block_time = pre_state.genesis_time + signed_block.message.slot * spec.config.SECONDS_PER_SLOT + if merge_block: + assert spec.is_merge_block(pre_state, signed_block.message.body) if store.time < block_time: on_tick_and_append_step(spec, store, block_time, test_steps) @@ -230,10 +234,10 @@ def apply_next_slots_with_attestations(spec, return post_state, store, last_signed_block -def prepare_empty_pow_block(spec): +def prepare_empty_pow_block(spec, rng=Random(3131)): return spec.PowBlock( - block_hash=spec.Hash32(), - parent_hash=spec.Hash32(), + block_hash=spec.Hash32(spec.hash(rng.randbytes(32))), + parent_hash=spec.Hash32(spec.hash(rng.randbytes(32))), total_difficulty=uint256(0), difficulty=uint256(0) ) diff --git a/tests/core/pyspec/eth2spec/test/merge/unittests/test_terminal_validity.py b/tests/core/pyspec/eth2spec/test/merge/unittests/test_terminal_validity.py index 6c2a623b2..42d83b4a0 100644 --- a/tests/core/pyspec/eth2spec/test/merge/unittests/test_terminal_validity.py +++ b/tests/core/pyspec/eth2spec/test/merge/unittests/test_terminal_validity.py @@ -102,7 +102,6 @@ def test_valid_terminal_pow_block_fail_just_after_terminal(spec, state): @spec_state_test def test_process_merge_execution_payload_success(spec, state): parent_block = prepare_empty_pow_block(spec) - parent_block.block_hash = spec.Hash32(spec.hash(b'01')) parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) block = prepare_empty_pow_block(spec) block.parent_hash = parent_block.block_hash @@ -116,13 +115,11 @@ def test_process_merge_execution_payload_success(spec, state): @spec_state_test def test_process_merge_execution_payload_fail_block_lookup(spec, state): parent_block = prepare_empty_pow_block(spec) - parent_block.block_hash = spec.Hash32(spec.hash(b'01')) parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) block = prepare_empty_pow_block(spec) block.parent_hash = parent_block.block_hash block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY payload = spec.ExecutionPayload() - payload.parent_hash = spec.Hash32(spec.hash(b'02')) yield from run_process_merge_execution_payload(spec, block, parent_block, payload, block_lookup_success=False) @@ -131,10 +128,8 @@ def test_process_merge_execution_payload_fail_block_lookup(spec, state): @spec_state_test def test_process_merge_execution_payload_fail_parent_block_lookup(spec, state): parent_block = prepare_empty_pow_block(spec) - parent_block.block_hash = spec.Hash32(spec.hash(b'01')) parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) block = prepare_empty_pow_block(spec) - block.parent_hash = spec.Hash32(spec.hash(b'00')) block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY payload = spec.ExecutionPayload() payload.parent_hash = block.block_hash @@ -146,7 +141,6 @@ def test_process_merge_execution_payload_fail_parent_block_lookup(spec, state): @spec_state_test def test_process_merge_execution_payload_fail_after_terminal(spec, state): parent_block = prepare_empty_pow_block(spec) - parent_block.block_hash = spec.Hash32(spec.hash(b'01')) parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY block = prepare_empty_pow_block(spec) block.parent_hash = parent_block.block_hash From f8b3a6715238e73baef82f5897e6988e89bf14a5 Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Fri, 24 Sep 2021 21:06:02 +0300 Subject: [PATCH 14/19] Fixed test_on_merge_block tests --- .../eth2spec/test/helpers/fork_choice.py | 22 +++- .../merge/fork_choice/test_on_merge_block.py | 101 ++++++++++-------- tests/formats/fork_choice/README.md | 12 ++- 3 files changed, 83 insertions(+), 52 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py index b26417e8f..640614288 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py @@ -1,5 +1,6 @@ from random import Random from eth_utils import encode_hex +from eth2spec.test.exceptions import BlockNotFoundException from eth2spec.utils.ssz.ssz_typing import uint256 from eth2spec.test.helpers.attestations import ( next_epoch_with_attestations, @@ -25,7 +26,7 @@ def add_block_to_store(spec, store, signed_block): def tick_and_add_block(spec, store, signed_block, test_steps, valid=True, allow_invalid_attestations=False, - merge_block=False): + merge_block=False, block_not_found=False): pre_state = store.block_states[signed_block.message.parent_root] block_time = pre_state.genesis_time + signed_block.message.slot * spec.config.SECONDS_PER_SLOT if merge_block: @@ -35,7 +36,8 @@ def tick_and_add_block(spec, store, signed_block, test_steps, valid=True, allow_ on_tick_and_append_step(spec, store, block_time, test_steps) post_state = yield from add_block( - spec, store, signed_block, test_steps, valid=valid, allow_invalid_attestations=allow_invalid_attestations) + spec, store, signed_block, test_steps, valid=valid, allow_invalid_attestations=allow_invalid_attestations, + block_not_found=block_not_found) return post_state @@ -123,7 +125,8 @@ def run_on_block(spec, store, signed_block, valid=True): assert store.blocks[signed_block.message.hash_tree_root()] == signed_block.message -def add_block(spec, store, signed_block, test_steps, valid=True, allow_invalid_attestations=False): +def add_block(spec, store, signed_block, test_steps, valid=True, allow_invalid_attestations=False, + block_not_found=False): """ Run on_block and on_attestation """ @@ -132,7 +135,9 @@ def add_block(spec, store, signed_block, test_steps, valid=True, allow_invalid_a if not valid: try: run_on_block(spec, store, signed_block, valid=True) - except AssertionError: + except (AssertionError, BlockNotFoundException) as e: + if isinstance(e, BlockNotFoundException) and not block_not_found: + assert False test_steps.append({ 'block': get_block_file_name(signed_block), 'valid': False, @@ -241,3 +246,12 @@ def prepare_empty_pow_block(spec, rng=Random(3131)): total_difficulty=uint256(0), difficulty=uint256(0) ) + + +def get_pow_block_file_name(pow_block): + return f"pow_block_{encode_hex(pow_block.block_hash)}" + + +def add_pow_block(spec, store, pow_block, test_steps): + yield get_pow_block_file_name(pow_block), pow_block + test_steps.append({'pow_block': get_pow_block_file_name(pow_block)}) diff --git a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py index 02b2a5942..38af539b1 100644 --- a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py +++ b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py @@ -1,7 +1,8 @@ from eth2spec.utils.ssz.ssz_typing import uint256 -from eth2spec.test.context import spec_state_test, with_phases, MERGE +from eth2spec.test.exceptions import BlockNotFoundException +from eth2spec.test.context import spec_state_test, with_phases, with_presets, MERGE, MINIMAL from eth2spec.test.helpers.block import ( - build_empty_block_for_next_slot + build_empty_block_for_next_slot, ) from eth2spec.test.helpers.fork_choice import ( get_genesis_forkchoice_store_and_block, @@ -13,6 +14,7 @@ from eth2spec.test.helpers.state import ( ) from eth2spec.test.helpers.fork_choice import ( prepare_empty_pow_block, + add_pow_block, ) @@ -21,7 +23,7 @@ def with_pow_block_patch(spec, blocks, func): for block in blocks: if block.block_hash == hash: return block - raise Exception("Block not found") + raise BlockNotFoundException() get_pow_block_backup = spec.get_pow_block spec.get_pow_block = get_pow_block @@ -30,21 +32,23 @@ def with_pow_block_patch(spec, blocks, func): is_called = AtomicBoolean() def wrap(flag: AtomicBoolean): - func() + yield from func() flag.value = True try: - wrap(is_called) + yield from wrap(is_called) finally: spec.get_pow_block = get_pow_block_backup assert is_called.value @with_phases([MERGE]) +@with_presets([MINIMAL], reason="mainnet `TERMINAL_TOTAL_DIFFICULTY` stub would cause overflow") @spec_state_test def test_all_valid(spec, state): test_steps = [] # Initialization + state.latest_execution_payload_header = spec.ExecutionPayloadHeader() store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) yield 'anchor_state', state yield 'anchor_block', anchor_block @@ -52,30 +56,34 @@ def test_all_valid(spec, state): on_tick_and_append_step(spec, store, current_time, test_steps) assert store.time == current_time - parent_block = prepare_empty_pow_block(spec) - parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) - block = prepare_empty_pow_block(spec) - block.parent_hash = parent_block.block_hash - block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - pow_blocks = [block, parent_block] - yield 'pow_blocks', pow_blocks + pow_block_parent = prepare_empty_pow_block(spec) + pow_block_parent.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) + pow_block = prepare_empty_pow_block(spec) + pow_block.parent_hash = pow_block_parent.block_hash + pow_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + pow_blocks = [pow_block, pow_block_parent] + for pb in pow_blocks: + yield from add_pow_block(spec, store, pb, test_steps) def run_func(): block = build_empty_block_for_next_slot(spec, state) + block.body.execution_payload.parent_hash = pow_block.block_hash signed_block = state_transition_and_sign_block(spec, state, block) - yield from tick_and_add_block(spec, store, signed_block, test_steps) + yield from tick_and_add_block(spec, store, signed_block, test_steps, merge_block=True) # valid assert spec.get_head(store) == signed_block.message.hash_tree_root() - with_pow_block_patch(spec, pow_blocks, run_func) + yield from with_pow_block_patch(spec, pow_blocks, run_func) yield 'steps', test_steps @with_phases([MERGE]) +@with_presets([MINIMAL], reason="mainnet `TERMINAL_TOTAL_DIFFICULTY` stub would cause overflow") @spec_state_test def test_block_lookup_failed(spec, state): test_steps = [] # Initialization + state.latest_execution_payload_header = spec.ExecutionPayloadHeader() store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) yield 'anchor_state', state yield 'anchor_block', anchor_block @@ -83,27 +91,30 @@ def test_block_lookup_failed(spec, state): on_tick_and_append_step(spec, store, current_time, test_steps) assert store.time == current_time - parent_block = prepare_empty_pow_block(spec) - parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) - pow_blocks = [parent_block] - yield 'pow_blocks', pow_blocks + pow_block = prepare_empty_pow_block(spec) + pow_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) + pow_blocks = [pow_block] + for pb in pow_blocks: + yield from add_pow_block(spec, store, pb, test_steps) def run_func(): block = build_empty_block_for_next_slot(spec, state) + block.body.execution_payload.parent_hash = pow_block.block_hash signed_block = state_transition_and_sign_block(spec, state, block) - yield from tick_and_add_block(spec, store, signed_block, test_steps) - # invalid - assert spec.get_head(store) == anchor_block.state_root + yield from tick_and_add_block(spec, store, signed_block, test_steps, valid=False, merge_block=True, + block_not_found=True) - with_pow_block_patch(spec, pow_blocks, run_func) + yield from with_pow_block_patch(spec, pow_blocks, run_func) yield 'steps', test_steps @with_phases([MERGE]) +@with_presets([MINIMAL], reason="mainnet `TERMINAL_TOTAL_DIFFICULTY` stub would cause overflow") @spec_state_test def test_too_early_for_merge(spec, state): test_steps = [] # Initialization + state.latest_execution_payload_header = spec.ExecutionPayloadHeader() store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) yield 'anchor_state', state yield 'anchor_block', anchor_block @@ -111,30 +122,32 @@ def test_too_early_for_merge(spec, state): on_tick_and_append_step(spec, store, current_time, test_steps) assert store.time == current_time - parent_block = prepare_empty_pow_block(spec) - parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(2) - block = prepare_empty_pow_block(spec) - block.parent_hash = parent_block.block_hash - block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) - pow_blocks = [block, parent_block] - yield 'pow_blocks', pow_blocks + pow_block_parent = prepare_empty_pow_block(spec) + pow_block_parent.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(2) + pow_block = prepare_empty_pow_block(spec) + pow_block.parent_hash = pow_block_parent.block_hash + pow_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) + pow_blocks = [pow_block, pow_block_parent] + for pb in pow_blocks: + yield from add_pow_block(spec, store, pb, test_steps) def run_func(): block = build_empty_block_for_next_slot(spec, state) + block.body.execution_payload.parent_hash = pow_block.block_hash signed_block = state_transition_and_sign_block(spec, state, block) - yield from tick_and_add_block(spec, store, signed_block, test_steps) - # invalid - assert spec.get_head(store) == anchor_block.state_root + yield from tick_and_add_block(spec, store, signed_block, test_steps, valid=False, merge_block=True) - with_pow_block_patch(spec, pow_blocks, run_func) + yield from with_pow_block_patch(spec, pow_blocks, run_func) yield 'steps', test_steps @with_phases([MERGE]) +@with_presets([MINIMAL], reason="mainnet `TERMINAL_TOTAL_DIFFICULTY` stub would cause overflow") @spec_state_test def test_too_late_for_merge(spec, state): test_steps = [] # Initialization + state.latest_execution_payload_header = spec.ExecutionPayloadHeader() store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) yield 'anchor_state', state yield 'anchor_block', anchor_block @@ -142,20 +155,20 @@ def test_too_late_for_merge(spec, state): on_tick_and_append_step(spec, store, current_time, test_steps) assert store.time == current_time - parent_block = prepare_empty_pow_block(spec) - parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - block = prepare_empty_pow_block(spec) - block.parent_hash = parent_block.block_hash - block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + uint256(1) - pow_blocks = [block, parent_block] - yield 'pow_blocks', pow_blocks + pow_block_parent = prepare_empty_pow_block(spec) + pow_block_parent.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + pow_block = prepare_empty_pow_block(spec) + pow_block.parent_hash = pow_block_parent.block_hash + pow_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + uint256(1) + pow_blocks = [pow_block, pow_block_parent] + for pb in pow_blocks: + yield from add_pow_block(spec, store, pb, test_steps) def run_func(): block = build_empty_block_for_next_slot(spec, state) + block.body.execution_payload.parent_hash = pow_block.block_hash signed_block = state_transition_and_sign_block(spec, state, block) - yield from tick_and_add_block(spec, store, signed_block, test_steps) - # invalid - assert spec.get_head(store) == anchor_block.state_root + yield from tick_and_add_block(spec, store, signed_block, test_steps, valid=False, merge_block=True) - with_pow_block_patch(spec, pow_blocks, run_func) + yield from with_pow_block_patch(spec, pow_blocks, run_func) yield 'steps', test_steps diff --git a/tests/formats/fork_choice/README.md b/tests/formats/fork_choice/README.md index 2f006b07e..48dde2fb1 100644 --- a/tests/formats/fork_choice/README.md +++ b/tests/formats/fork_choice/README.md @@ -71,12 +71,16 @@ After this step, the `store` object may have been updated. #### `on_merge_block` execution -Adds `PowBlock` data which is required for executing `on_block(store, block)`. -Number of blocks is stored in `meta.yaml`, block file names are `pow_block_.ssz_snappy`. -The file is located in the same folder. +Adds `PowBlock` data which is required for executing `on_block(store, block)`. +```yaml +{ + pow_block: string -- the name of the `pow_block_<32-byte-root>.ssz_snappy` file. + To be used in `get_pow_block` lookup +} +``` +The file is located in the same folder (see below). PowBlocks should be used as return values for `get_pow_block(hash: Hash32) -> PowBlock` function if hashes match. - #### Checks step The checks to verify the current status of `store`. From deb4dbd9c889f14f5d708410b024d9d75c188f0e Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Sat, 25 Sep 2021 01:26:01 +0300 Subject: [PATCH 15/19] Make empty pow_block hash generation Python 3.8 compatible --- tests/core/pyspec/eth2spec/test/helpers/fork_choice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py index 640614288..cc0860bfd 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py @@ -241,8 +241,8 @@ def apply_next_slots_with_attestations(spec, def prepare_empty_pow_block(spec, rng=Random(3131)): return spec.PowBlock( - block_hash=spec.Hash32(spec.hash(rng.randbytes(32))), - parent_hash=spec.Hash32(spec.hash(rng.randbytes(32))), + block_hash=spec.Hash32(spec.hash(bytearray(rng.getrandbits(8) for _ in range(32)))), + parent_hash=spec.Hash32(spec.hash(bytearray(rng.getrandbits(8) for _ in range(32)))), total_difficulty=uint256(0), difficulty=uint256(0) ) From c8d05c60ecc28f85879b18d049011bc45d8fd7b7 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 27 Sep 2021 08:23:02 -0600 Subject: [PATCH 16/19] Apply suggestions from code review --- .../pyspec/eth2spec/test/helpers/fork_choice.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py index cc0860bfd..04843078f 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py @@ -36,8 +36,11 @@ def tick_and_add_block(spec, store, signed_block, test_steps, valid=True, allow_ on_tick_and_append_step(spec, store, block_time, test_steps) post_state = yield from add_block( - spec, store, signed_block, test_steps, valid=valid, allow_invalid_attestations=allow_invalid_attestations, - block_not_found=block_not_found) + spec, store, signed_block, test_steps, + valid=valid, + allow_invalid_attestations=allow_invalid_attestations, + block_not_found=block_not_found, + ) return post_state @@ -125,7 +128,12 @@ def run_on_block(spec, store, signed_block, valid=True): assert store.blocks[signed_block.message.hash_tree_root()] == signed_block.message -def add_block(spec, store, signed_block, test_steps, valid=True, allow_invalid_attestations=False, +def add_block(spec, + store, + signed_block, + test_steps, + valid=True, + allow_invalid_attestations=False, block_not_found=False): """ Run on_block and on_attestation From 2fa595f7842ccc453c800975ef141ca3c053a0f6 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 28 Sep 2021 00:17:18 +0800 Subject: [PATCH 17/19] Apply the trivial suggestions --- .../merge/unittests/test_terminal_validity.py | 16 ++++++++-------- .../test/merge/unittests/test_transition.py | 18 ++++-------------- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/merge/unittests/test_terminal_validity.py b/tests/core/pyspec/eth2spec/test/merge/unittests/test_terminal_validity.py index 42d83b4a0..2cf58f06a 100644 --- a/tests/core/pyspec/eth2spec/test/merge/unittests/test_terminal_validity.py +++ b/tests/core/pyspec/eth2spec/test/merge/unittests/test_terminal_validity.py @@ -13,10 +13,10 @@ def validate_transition_execution_payload(spec, execution_payload): assert spec.is_valid_terminal_pow_block(pow_block, pow_parent) -def run_process_merge_execution_payload(spec, block, parent_block, payload, +def run_validate_transition_execution_payload(spec, block, parent_block, payload, valid=True, block_lookup_success=True): """ - Run ``process_merge_execution_payload``, yielding: + Run ``validate_transition_execution_payload``, yielding: - current block ('block') - parent block ('parent_block') - execution payload ('payload') @@ -100,7 +100,7 @@ def test_valid_terminal_pow_block_fail_just_after_terminal(spec, state): @with_merge_and_later @spec_state_test -def test_process_merge_execution_payload_success(spec, state): +def test_validate_transition_execution_payload_success(spec, state): parent_block = prepare_empty_pow_block(spec) parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) block = prepare_empty_pow_block(spec) @@ -120,26 +120,26 @@ def test_process_merge_execution_payload_fail_block_lookup(spec, state): block.parent_hash = parent_block.block_hash block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY payload = spec.ExecutionPayload() - yield from run_process_merge_execution_payload(spec, block, parent_block, payload, + yield from run_validate_transition_execution_payload(spec, block, parent_block, payload, block_lookup_success=False) @with_merge_and_later @spec_state_test -def test_process_merge_execution_payload_fail_parent_block_lookup(spec, state): +def test_validate_transition_execution_payload_fail_parent_block_lookup(spec, state): parent_block = prepare_empty_pow_block(spec) parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) block = prepare_empty_pow_block(spec) block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY payload = spec.ExecutionPayload() payload.parent_hash = block.block_hash - yield from run_process_merge_execution_payload(spec, block, parent_block, payload, + yield from run_validate_transition_execution_payload(spec, block, parent_block, payload, block_lookup_success=False) @with_merge_and_later @spec_state_test -def test_process_merge_execution_payload_fail_after_terminal(spec, state): +def test_validate_transition_execution_payload_fail_after_terminal(spec, state): parent_block = prepare_empty_pow_block(spec) parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY block = prepare_empty_pow_block(spec) @@ -147,4 +147,4 @@ def test_process_merge_execution_payload_fail_after_terminal(spec, state): block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + 1 payload = spec.ExecutionPayload() payload.parent_hash = block.block_hash - yield from run_process_merge_execution_payload(spec, block, parent_block, payload, valid=False) + yield from run_validate_transition_execution_payload(spec, block, parent_block, payload, valid=False) diff --git a/tests/core/pyspec/eth2spec/test/merge/unittests/test_transition.py b/tests/core/pyspec/eth2spec/test/merge/unittests/test_transition.py index 03e6ae874..d0dbfae40 100644 --- a/tests/core/pyspec/eth2spec/test/merge/unittests/test_transition.py +++ b/tests/core/pyspec/eth2spec/test/merge/unittests/test_transition.py @@ -34,9 +34,8 @@ def test_fail_merge_block(spec, state): @spec_state_test def test_fail_merge_block_complete_transition(spec, state): state = build_state_with_complete_transition(spec, state) - execution_payload = build_empty_execution_payload(spec, state) body = spec.BeaconBlockBody() - body.execution_payload = execution_payload + body.execution_payload = build_empty_execution_payload(spec, state) assert not spec.is_merge_block(state, body) @@ -44,9 +43,7 @@ def test_fail_merge_block_complete_transition(spec, state): @spec_state_test def test_fail_merge_block_no_execution_payload(spec, state): state = build_state_with_incomplete_transition(spec, state) - execution_payload = spec.ExecutionPayload() body = spec.BeaconBlockBody() - body.execution_payload = execution_payload assert not spec.is_merge_block(state, body) @@ -54,9 +51,8 @@ def test_fail_merge_block_no_execution_payload(spec, state): @spec_state_test def test_success_merge_block(spec, state): state = build_state_with_incomplete_transition(spec, state) - execution_payload = build_empty_execution_payload(spec, state) body = spec.BeaconBlockBody() - body.execution_payload = execution_payload + body.execution_payload = build_empty_execution_payload(spec, state) assert spec.is_merge_block(state, body) @@ -64,9 +60,7 @@ def test_success_merge_block(spec, state): @spec_state_test def test_failed_execution_enabled(spec, state): state = build_state_with_incomplete_transition(spec, state) - execution_payload = spec.ExecutionPayload() body = spec.BeaconBlockBody() - body.execution_payload = execution_payload assert not spec.is_execution_enabled(state, body) @@ -74,9 +68,8 @@ def test_failed_execution_enabled(spec, state): @spec_state_test def test_success_execution_enabled_before_terminal(spec, state): state = build_state_with_incomplete_transition(spec, state) - execution_payload = build_empty_execution_payload(spec, state) body = spec.BeaconBlockBody() - body.execution_payload = execution_payload + body.execution_payload = build_empty_execution_payload(spec, state) assert spec.is_execution_enabled(state, body) @@ -84,9 +77,7 @@ def test_success_execution_enabled_before_terminal(spec, state): @spec_state_test def test_success_execution_enabled_no_execution_payload(spec, state): state = build_state_with_complete_transition(spec, state) - execution_payload = spec.ExecutionPayload() body = spec.BeaconBlockBody() - body.execution_payload = execution_payload assert spec.is_execution_enabled(state, body) @@ -94,7 +85,6 @@ def test_success_execution_enabled_no_execution_payload(spec, state): @spec_state_test def test_success_execution_enabled(spec, state): state = build_state_with_complete_transition(spec, state) - execution_payload = build_empty_execution_payload(spec, state) body = spec.BeaconBlockBody() - body.execution_payload = execution_payload + body.execution_payload = build_empty_execution_payload(spec, state) assert spec.is_execution_enabled(state, body) From 5ab2824427f631d99e297f5590b279752128d3eb Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 28 Sep 2021 00:59:03 +0800 Subject: [PATCH 18/19] Clean up, refactor test_transition.py --- .../merge/fork_choice/test_on_merge_block.py | 11 ++- .../merge/unittests/test_terminal_validity.py | 25 ++--- .../test/merge/unittests/test_transition.py | 91 ++++++------------- 3 files changed, 44 insertions(+), 83 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py index 38af539b1..fd141ce8d 100644 --- a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py +++ b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py @@ -16,6 +16,9 @@ from eth2spec.test.helpers.fork_choice import ( prepare_empty_pow_block, add_pow_block, ) +from eth2spec.test.helpers.execution_payload import ( + build_state_with_incomplete_transition, +) def with_pow_block_patch(spec, blocks, func): @@ -48,7 +51,7 @@ def with_pow_block_patch(spec, blocks, func): def test_all_valid(spec, state): test_steps = [] # Initialization - state.latest_execution_payload_header = spec.ExecutionPayloadHeader() + state = build_state_with_incomplete_transition(spec, state) store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) yield 'anchor_state', state yield 'anchor_block', anchor_block @@ -83,7 +86,7 @@ def test_all_valid(spec, state): def test_block_lookup_failed(spec, state): test_steps = [] # Initialization - state.latest_execution_payload_header = spec.ExecutionPayloadHeader() + state = build_state_with_incomplete_transition(spec, state) store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) yield 'anchor_state', state yield 'anchor_block', anchor_block @@ -114,7 +117,7 @@ def test_block_lookup_failed(spec, state): def test_too_early_for_merge(spec, state): test_steps = [] # Initialization - state.latest_execution_payload_header = spec.ExecutionPayloadHeader() + state = build_state_with_incomplete_transition(spec, state) store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) yield 'anchor_state', state yield 'anchor_block', anchor_block @@ -147,7 +150,7 @@ def test_too_early_for_merge(spec, state): def test_too_late_for_merge(spec, state): test_steps = [] # Initialization - state.latest_execution_payload_header = spec.ExecutionPayloadHeader() + state = build_state_with_incomplete_transition(spec, state) store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) yield 'anchor_state', state yield 'anchor_block', anchor_block diff --git a/tests/core/pyspec/eth2spec/test/merge/unittests/test_terminal_validity.py b/tests/core/pyspec/eth2spec/test/merge/unittests/test_terminal_validity.py index 2cf58f06a..cfd5ea091 100644 --- a/tests/core/pyspec/eth2spec/test/merge/unittests/test_terminal_validity.py +++ b/tests/core/pyspec/eth2spec/test/merge/unittests/test_terminal_validity.py @@ -14,20 +14,13 @@ def validate_transition_execution_payload(spec, execution_payload): def run_validate_transition_execution_payload(spec, block, parent_block, payload, - valid=True, block_lookup_success=True): + valid=True, block_lookup_success=True): """ - Run ``validate_transition_execution_payload``, yielding: - - current block ('block') - - parent block ('parent_block') - - execution payload ('payload') + Run ``validate_transition_execution_payload`` If ``valid == False``, run expecting ``AssertionError`` If ``block_lookup_success == False``, run expecting ``BlockNotFoundException`` """ - yield 'block', block - yield 'parent_block', parent_block - yield 'payload', payload - def get_pow_block(hash: spec.Bytes32) -> spec.PowBlock: if hash == block.block_hash: return block @@ -108,20 +101,20 @@ def test_validate_transition_execution_payload_success(spec, state): block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY payload = spec.ExecutionPayload() payload.parent_hash = block.block_hash - yield from run_process_merge_execution_payload(spec, block, parent_block, payload) + run_validate_transition_execution_payload(spec, block, parent_block, payload) @with_merge_and_later @spec_state_test -def test_process_merge_execution_payload_fail_block_lookup(spec, state): +def test_validate_transition_execution_payload_fail_block_lookup(spec, state): parent_block = prepare_empty_pow_block(spec) parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) block = prepare_empty_pow_block(spec) block.parent_hash = parent_block.block_hash block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY payload = spec.ExecutionPayload() - yield from run_validate_transition_execution_payload(spec, block, parent_block, payload, - block_lookup_success=False) + run_validate_transition_execution_payload(spec, block, parent_block, payload, + block_lookup_success=False) @with_merge_and_later @@ -133,8 +126,8 @@ def test_validate_transition_execution_payload_fail_parent_block_lookup(spec, st block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY payload = spec.ExecutionPayload() payload.parent_hash = block.block_hash - yield from run_validate_transition_execution_payload(spec, block, parent_block, payload, - block_lookup_success=False) + run_validate_transition_execution_payload(spec, block, parent_block, payload, + block_lookup_success=False) @with_merge_and_later @@ -147,4 +140,4 @@ def test_validate_transition_execution_payload_fail_after_terminal(spec, state): block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + 1 payload = spec.ExecutionPayload() payload.parent_hash = block.block_hash - yield from run_validate_transition_execution_payload(spec, block, parent_block, payload, valid=False) + run_validate_transition_execution_payload(spec, block, parent_block, payload, valid=False) diff --git a/tests/core/pyspec/eth2spec/test/merge/unittests/test_transition.py b/tests/core/pyspec/eth2spec/test/merge/unittests/test_transition.py index d0dbfae40..05d3888da 100644 --- a/tests/core/pyspec/eth2spec/test/merge/unittests/test_transition.py +++ b/tests/core/pyspec/eth2spec/test/merge/unittests/test_transition.py @@ -3,7 +3,10 @@ from eth2spec.test.helpers.execution_payload import ( build_state_with_incomplete_transition, build_state_with_complete_transition, ) -from eth2spec.test.context import spec_state_test, with_merge_and_later +from eth2spec.test.context import ( + spec_state_test, + with_merge_and_later +) @with_merge_and_later @@ -20,71 +23,33 @@ def test_success_merge_complete(spec, state): assert spec.is_merge_complete(state) -@with_merge_and_later -@spec_state_test -def test_fail_merge_block(spec, state): - state = build_state_with_complete_transition(spec, state) - execution_payload = spec.ExecutionPayload() - body = spec.BeaconBlockBody() - body.execution_payload = execution_payload - assert not spec.is_merge_block(state, body) +# with_complete_transition', 'with_execution_payload', 'is_merge_block', 'is_execution_enabled' +expected_results = [ + (True, True, False, True), + (True, False, False, True), + (False, True, True, True), + (False, False, False, False) +] @with_merge_and_later @spec_state_test -def test_fail_merge_block_complete_transition(spec, state): - state = build_state_with_complete_transition(spec, state) - body = spec.BeaconBlockBody() - body.execution_payload = build_empty_execution_payload(spec, state) - assert not spec.is_merge_block(state, body) +def test_is_merge_block_and_is_execution_enabled(spec, state): + for result in expected_results: + ( + with_complete_transition, + with_execution_payload, + is_merge_block, + is_execution_enabled + ) = result + if with_complete_transition: + state = build_state_with_complete_transition(spec, state) + else: + state = build_state_with_incomplete_transition(spec, state) + body = spec.BeaconBlockBody() + if with_execution_payload: + body.execution_payload = build_empty_execution_payload(spec, state) -@with_merge_and_later -@spec_state_test -def test_fail_merge_block_no_execution_payload(spec, state): - state = build_state_with_incomplete_transition(spec, state) - body = spec.BeaconBlockBody() - assert not spec.is_merge_block(state, body) - - -@with_merge_and_later -@spec_state_test -def test_success_merge_block(spec, state): - state = build_state_with_incomplete_transition(spec, state) - body = spec.BeaconBlockBody() - body.execution_payload = build_empty_execution_payload(spec, state) - assert spec.is_merge_block(state, body) - - -@with_merge_and_later -@spec_state_test -def test_failed_execution_enabled(spec, state): - state = build_state_with_incomplete_transition(spec, state) - body = spec.BeaconBlockBody() - assert not spec.is_execution_enabled(state, body) - - -@with_merge_and_later -@spec_state_test -def test_success_execution_enabled_before_terminal(spec, state): - state = build_state_with_incomplete_transition(spec, state) - body = spec.BeaconBlockBody() - body.execution_payload = build_empty_execution_payload(spec, state) - assert spec.is_execution_enabled(state, body) - - -@with_merge_and_later -@spec_state_test -def test_success_execution_enabled_no_execution_payload(spec, state): - state = build_state_with_complete_transition(spec, state) - body = spec.BeaconBlockBody() - assert spec.is_execution_enabled(state, body) - - -@with_merge_and_later -@spec_state_test -def test_success_execution_enabled(spec, state): - state = build_state_with_complete_transition(spec, state) - body = spec.BeaconBlockBody() - body.execution_payload = build_empty_execution_payload(spec, state) - assert spec.is_execution_enabled(state, body) + assert spec.is_merge_block(state, body) == is_merge_block + assert spec.is_execution_enabled(state, body) == is_execution_enabled From ef51e1cfb9a9ad3f22357ffd0ac95b03c6449581 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 28 Sep 2021 01:00:56 +0800 Subject: [PATCH 19/19] Change mainnet `TERMINAL_TOTAL_DIFFICULTY` placeholder to `2**256-2**10` to avoid overflow --- configs/mainnet.yaml | 4 ++-- .../eth2spec/test/merge/fork_choice/test_on_merge_block.py | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index 6f2f582fa..7609908b4 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -5,8 +5,8 @@ PRESET_BASE: 'mainnet' # Transition # --------------------------------------------------------------- -# TBD, 2**256-1 is a placeholder -TERMINAL_TOTAL_DIFFICULTY: 115792089237316195423570985008687907853269984665640564039457584007913129639935 +# TBD, 2**256-2**10 is a placeholder +TERMINAL_TOTAL_DIFFICULTY: 115792089237316195423570985008687907853269984665640564039457584007913129638912 # Genesis diff --git a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py index fd141ce8d..830f20f23 100644 --- a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py +++ b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py @@ -1,6 +1,6 @@ from eth2spec.utils.ssz.ssz_typing import uint256 from eth2spec.test.exceptions import BlockNotFoundException -from eth2spec.test.context import spec_state_test, with_phases, with_presets, MERGE, MINIMAL +from eth2spec.test.context import spec_state_test, with_phases, MERGE from eth2spec.test.helpers.block import ( build_empty_block_for_next_slot, ) @@ -46,7 +46,6 @@ def with_pow_block_patch(spec, blocks, func): @with_phases([MERGE]) -@with_presets([MINIMAL], reason="mainnet `TERMINAL_TOTAL_DIFFICULTY` stub would cause overflow") @spec_state_test def test_all_valid(spec, state): test_steps = [] @@ -81,7 +80,6 @@ def test_all_valid(spec, state): @with_phases([MERGE]) -@with_presets([MINIMAL], reason="mainnet `TERMINAL_TOTAL_DIFFICULTY` stub would cause overflow") @spec_state_test def test_block_lookup_failed(spec, state): test_steps = [] @@ -112,7 +110,6 @@ def test_block_lookup_failed(spec, state): @with_phases([MERGE]) -@with_presets([MINIMAL], reason="mainnet `TERMINAL_TOTAL_DIFFICULTY` stub would cause overflow") @spec_state_test def test_too_early_for_merge(spec, state): test_steps = [] @@ -145,7 +142,6 @@ def test_too_early_for_merge(spec, state): @with_phases([MERGE]) -@with_presets([MINIMAL], reason="mainnet `TERMINAL_TOTAL_DIFFICULTY` stub would cause overflow") @spec_state_test def test_too_late_for_merge(spec, state): test_steps = []