From 14a38251eda27a176974d9efee65549e56565664 Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Tue, 14 Sep 2021 11:50:43 +0300 Subject: [PATCH 01/34] 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 f2547758da..be791afa91 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/34] 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 de82b17fa2..2e228a2e16 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/34] 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 b8f7c4bcb3..7daa519707 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.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 43be965a58..d82a6a54da 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 4c68034d4a..a2af7dc921 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 0000000000..e69de29bb2 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 0000000000..f096fb1389 --- /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 0000000000..e69de29bb2 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 0000000000..24caa03b13 --- /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/34] 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 a2af7dc921..93cc925fbb 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 f096fb1389..983cab89af 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 24caa03b13..ff153d9ab5 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.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/34] 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/34] 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 8e0294bf0c..1f36afd4b9 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 @@ '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/34] 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 8da3260f5c..d164dc2e2a 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/34] 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 9e704cb49e..edbe43c13b 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 8231c0223d..525e42d9af 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 983cab89af..6053758806 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.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 get_pow_block(hash: spec.Bytes32) -> spec.PowBlock: 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 get_pow_block(hash: spec.Bytes32) -> spec.PowBlock: @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 - - assert spec.is_valid_terminal_pow_block(transition_store, block, parent_block) + block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - 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 ff153d9ab5..bd968474e1 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 df6a834889969cdb417bb56947e9f460942f836e Mon Sep 17 00:00:00 2001 From: lsankar4033 Date: Wed, 22 Sep 2021 11:18:41 -0700 Subject: [PATCH 09/34] Add TERMINAL_BLOCK_HASH override --- configs/mainnet.yaml | 3 ++- configs/minimal.yaml | 3 ++- specs/merge/beacon-chain.md | 1 + specs/merge/client-settings.md | 6 ++++++ specs/merge/fork-choice.md | 7 +++++-- specs/merge/validator.md | 10 +++++++++- 6 files changed, 25 insertions(+), 5 deletions(-) diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index 6f2f582fac..d5ea308d06 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -7,7 +7,8 @@ PRESET_BASE: 'mainnet' # --------------------------------------------------------------- # TBD, 2**256-1 is a placeholder TERMINAL_TOTAL_DIFFICULTY: 115792089237316195423570985008687907853269984665640564039457584007913129639935 - +# By default, don't use this param +TERMINAL_BLOCK_HASH: 0x0000000000000000000000000000000000000000000000000000000000000000 # Genesis # --------------------------------------------------------------- diff --git a/configs/minimal.yaml b/configs/minimal.yaml index 8da3260f5c..8f7bd63397 100644 --- a/configs/minimal.yaml +++ b/configs/minimal.yaml @@ -7,7 +7,8 @@ PRESET_BASE: 'minimal' # --------------------------------------------------------------- # TBD, 2**256-1 is a placeholder TERMINAL_TOTAL_DIFFICULTY: 115792089237316195423570985008687907853269984665640564039457584007913129639935 - +# By default, don't use this param +TERMINAL_BLOCK_HASH: 0x0000000000000000000000000000000000000000000000000000000000000000 # Genesis # --------------------------------------------------------------- diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 39f457252b..35f1407be5 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -83,6 +83,7 @@ This patch adds transaction execution to the beacon chain as part of the Merge f | Name | Value | | - | - | | `TERMINAL_TOTAL_DIFFICULTY` | **TBD** | +| `TERMINAL_BLOCK_HASH` | `Bytes32('0x0000000000000000000000000000000000000000000000000000000000000000')` | ## Containers diff --git a/specs/merge/client-settings.md b/specs/merge/client-settings.md index 64b2b20e68..efef1344fe 100644 --- a/specs/merge/client-settings.md +++ b/specs/merge/client-settings.md @@ -19,3 +19,9 @@ To coordinate manual overrides to [`TERMINAL_TOTAL_DIFFICULTY`](./beacon-chain.m Except under exceptional scenarios, this setting is expected to not be used. Sufficient warning to the user about this exceptional configurable setting should be provided. +### Override terminal block hash + +To allow for fork coordination around a specific PoW block, clients must also provide `--terminal-block-hash-override` as a configurable setting. +The value provided by this setting takes precedence over the pre-configured `TERMINAL_BLOCK_HASH` parameter. + +Except under exceptional scenarios, this setting is expected to not be used. Sufficient warning to the user about this exceptional configurable setting should be provided. diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md index 8051ab3eb5..53266cf467 100644 --- a/specs/merge/fork-choice.md +++ b/specs/merge/fork-choice.md @@ -90,6 +90,9 @@ Used by fork-choice handler, `on_block`. ```python def is_valid_terminal_pow_block(block: PowBlock, parent: PowBlock) -> bool: + if block.block_hash == TERMINAL_BLOCK_HASH: + return True + is_total_difficulty_reached = block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY is_parent_total_difficulty_valid = parent.total_difficulty < TERMINAL_TOTAL_DIFFICULTY return is_total_difficulty_reached and is_parent_total_difficulty_valid @@ -116,7 +119,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: assert block.slot > finalized_slot # Check block is a descendant of the finalized block at the checkpoint finalized slot assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root - + # Check the block is valid and compute the post-state state = pre_state.copy() state_transition(state, signed_block, True) @@ -142,7 +145,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # Update finalized checkpoint if state.finalized_checkpoint.epoch > store.finalized_checkpoint.epoch: store.finalized_checkpoint = state.finalized_checkpoint - + # Potentially update justified if different from store if store.justified_checkpoint != state.current_justified_checkpoint: # Update justified if new justified is later than store justified diff --git a/specs/merge/validator.md b/specs/merge/validator.md index 645e1967b2..88e24a2126 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -74,6 +74,14 @@ def get_pow_block_at_total_difficulty(total_difficulty: uint256, pow_chain: Sequ return None +def get_terminal_pow_block(total_difficulty: uint256, block_hash_override: Hash32, pow_chain: Sequence[PowBlock]) -> Optional[PowBlock]: + # check block hash override prior to total difficulty + pow_block_overrides = [pow_block for pow_block in pow_chain if pow_block.block_hash == block_hash_override] + if len(pow_block_overrides) != 0: + return pow_block_overrides[0] + + return get_pow_block_at_total_difficulty(total_difficulty, pow_chain) + def produce_execution_payload(state: BeaconState, parent_hash: Hash32, @@ -87,7 +95,7 @@ def get_execution_payload(state: BeaconState, execution_engine: ExecutionEngine, pow_chain: Sequence[PowBlock]) -> ExecutionPayload: if not is_merge_complete(state): - terminal_pow_block = get_pow_block_at_total_difficulty(TERMINAL_TOTAL_DIFFICULTY, pow_chain) + terminal_pow_block = get_terminal_pow_block(TERMINAL_TOTAL_DIFFICULTY, TERMINAL_BLOCK_HASH, pow_chain) if terminal_pow_block is None: # Pre-merge, empty payload return ExecutionPayload() From 56fc984333eefdb69598854e8b0db125aa0e68e1 Mon Sep 17 00:00:00 2001 From: lsankar4033 Date: Wed, 22 Sep 2021 11:24:05 -0700 Subject: [PATCH 10/34] Re-run doctoc --- specs/merge/client-settings.md | 1 + 1 file changed, 1 insertion(+) diff --git a/specs/merge/client-settings.md b/specs/merge/client-settings.md index efef1344fe..fdf7a25d5e 100644 --- a/specs/merge/client-settings.md +++ b/specs/merge/client-settings.md @@ -4,6 +4,7 @@ - [The Merge -- Client Settings](#the-merge----client-settings) - [Override terminal total difficulty](#override-terminal-total-difficulty) + - [Override terminal block hash](#override-terminal-block-hash) From 9d1cdf5ce1d879ca8e443a6453163beae474bc10 Mon Sep 17 00:00:00 2001 From: Lakshman Sankar Date: Thu, 23 Sep 2021 09:31:43 -0700 Subject: [PATCH 11/34] Bytes32 -> Hash32 Co-authored-by: Mikhail Kalinin --- specs/merge/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 35f1407be5..3b467e78f6 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -83,7 +83,7 @@ This patch adds transaction execution to the beacon chain as part of the Merge f | Name | Value | | - | - | | `TERMINAL_TOTAL_DIFFICULTY` | **TBD** | -| `TERMINAL_BLOCK_HASH` | `Bytes32('0x0000000000000000000000000000000000000000000000000000000000000000')` | +| `TERMINAL_BLOCK_HASH` | `Hash32('0x0000000000000000000000000000000000000000000000000000000000000000')` | ## Containers From 1ccb7d0a52d633c3404e5700b8e5a8e505f88bec Mon Sep 17 00:00:00 2001 From: lsankar4033 Date: Thu, 23 Sep 2021 09:52:02 -0700 Subject: [PATCH 12/34] Inline TERMINAL_BLOCK_HASH and TERMINAL_TOTAL_DIFFICULTY usages --- specs/merge/validator.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/specs/merge/validator.md b/specs/merge/validator.md index 88e24a2126..6b95830cc5 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -65,22 +65,25 @@ All validator responsibilities remain unchanged other than those noted below. Na * Set `block.body.execution_payload = get_execution_payload(state, execution_engine, pow_chain)` where: ```python -def get_pow_block_at_total_difficulty(total_difficulty: uint256, pow_chain: Sequence[PowBlock]) -> Optional[PowBlock]: +def get_pow_block_at_terminal_total_difficulty(pow_chain: Sequence[PowBlock]) -> Optional[PowBlock]: # `pow_chain` abstractly represents all blocks in the PoW chain for block in pow_chain: parent = get_pow_block(block.parent_hash) - if block.total_difficulty >= total_difficulty and parent.total_difficulty < total_difficulty: + if block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY and parent.total_difficulty < TERMINAL_TOTAL_DIFFICULTY: return block return None -def get_terminal_pow_block(total_difficulty: uint256, block_hash_override: Hash32, pow_chain: Sequence[PowBlock]) -> Optional[PowBlock]: - # check block hash override prior to total difficulty - pow_block_overrides = [pow_block for pow_block in pow_chain if pow_block.block_hash == block_hash_override] - if len(pow_block_overrides) != 0: - return pow_block_overrides[0] +def get_terminal_pow_block(pow_chain: Sequence[PowBlock]) -> Optional[PowBlock]: + if TERMINAL_BLOCK_HASH != Hash32(): + # Check block hash override prior to total difficulty + pow_block_overrides = [pow_block for pow_block in pow_chain if pow_block.block_hash == TERMINAL_BLOCK_HASH] + if len(pow_block_overrides) != 0: + return pow_block_overrides[0] + else: + return None - return get_pow_block_at_total_difficulty(total_difficulty, pow_chain) + return get_pow_block_at_terminal_total_difficulty(pow_chain) def produce_execution_payload(state: BeaconState, @@ -95,7 +98,7 @@ def get_execution_payload(state: BeaconState, execution_engine: ExecutionEngine, pow_chain: Sequence[PowBlock]) -> ExecutionPayload: if not is_merge_complete(state): - terminal_pow_block = get_terminal_pow_block(TERMINAL_TOTAL_DIFFICULTY, TERMINAL_BLOCK_HASH, pow_chain) + terminal_pow_block = get_terminal_pow_block(pow_chain) if terminal_pow_block is None: # Pre-merge, empty payload return ExecutionPayload() From b1aa2279838219b91c9c05a26da89b59bf0f8611 Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Thu, 23 Sep 2021 22:22:34 +0300 Subject: [PATCH 13/34] 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 8051ab3eb5..77e279e679 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 0000000000..b13628bbd4 --- /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 1481741203..94516a39c3 100644 --- a/tests/generators/fork_choice/main.py +++ b/tests/generators/fork_choice/main.py @@ -9,8 +9,14 @@ ]} # 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 14/34] 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 b13628bbd4..f7bd0341c0 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 15/34] 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 c553ec3744..fcabd88f3e 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 edbe43c13b..78b90b165b 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.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 453c49d3e1..6126346a98 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 65d6975f23..aaaf5c23b8 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 525e42d9af..572326711e 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 f7bd0341c0..f3e4129b38 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.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 6053758806..87ee190cc8 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 get_pow_block(hash: spec.Bytes32) -> spec.PowBlock: 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 bd968474e1..03e6ae8746 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 cfc86776dd..2f006b07e6 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 16/34] 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 572326711e..bc833d1073 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 @@ 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 f3e4129b38..02b2a5942d 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 @@ 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 87ee190cc8..6c2a623b2a 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 c9c7ab72898b4bf483a93bde55c6187e10d826eb Mon Sep 17 00:00:00 2001 From: lsankar4033 Date: Thu, 23 Sep 2021 16:51:20 -0700 Subject: [PATCH 17/34] Add back newlines in configs --- configs/mainnet.yaml | 1 + configs/minimal.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index d5ea308d06..dc3006ea6b 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -10,6 +10,7 @@ TERMINAL_TOTAL_DIFFICULTY: 11579208923731619542357098500868790785326998466564056 # By default, don't use this param TERMINAL_BLOCK_HASH: 0x0000000000000000000000000000000000000000000000000000000000000000 + # Genesis # --------------------------------------------------------------- # `2**14` (= 16,384) diff --git a/configs/minimal.yaml b/configs/minimal.yaml index 8f7bd63397..aaa7f8504c 100644 --- a/configs/minimal.yaml +++ b/configs/minimal.yaml @@ -10,6 +10,7 @@ TERMINAL_TOTAL_DIFFICULTY: 11579208923731619542357098500868790785326998466564056 # By default, don't use this param TERMINAL_BLOCK_HASH: 0x0000000000000000000000000000000000000000000000000000000000000000 + # Genesis # --------------------------------------------------------------- # [customized] From 35a42c9d458ecd00b911f2f72a875eb3d8134645 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 24 Sep 2021 06:23:59 -0600 Subject: [PATCH 18/34] Apply suggestions from code review --- specs/merge/client-settings.md | 2 +- specs/merge/validator.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/merge/client-settings.md b/specs/merge/client-settings.md index fdf7a25d5e..287e557db8 100644 --- a/specs/merge/client-settings.md +++ b/specs/merge/client-settings.md @@ -22,7 +22,7 @@ Except under exceptional scenarios, this setting is expected to not be used. Suf ### Override terminal block hash -To allow for fork coordination around a specific PoW block, clients must also provide `--terminal-block-hash-override` as a configurable setting. +To allow for transition coordination around a specific PoW block, clients must also provide `--terminal-block-hash-override` as a configurable setting. The value provided by this setting takes precedence over the pre-configured `TERMINAL_BLOCK_HASH` parameter. Except under exceptional scenarios, this setting is expected to not be used. Sufficient warning to the user about this exceptional configurable setting should be provided. diff --git a/specs/merge/validator.md b/specs/merge/validator.md index 6b95830cc5..1ccd7fe9ea 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -76,7 +76,7 @@ def get_pow_block_at_terminal_total_difficulty(pow_chain: Sequence[PowBlock]) -> def get_terminal_pow_block(pow_chain: Sequence[PowBlock]) -> Optional[PowBlock]: if TERMINAL_BLOCK_HASH != Hash32(): - # Check block hash override prior to total difficulty + # Terminal lock hash override takes precedence over terminal total difficulty pow_block_overrides = [pow_block for pow_block in pow_chain if pow_block.block_hash == TERMINAL_BLOCK_HASH] if len(pow_block_overrides) != 0: return pow_block_overrides[0] From a48a6f46c4a4151bc75625e20bbee4abb8dd0d6d Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 24 Sep 2021 06:32:08 -0600 Subject: [PATCH 19/34] fix lint --- specs/merge/validator.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/specs/merge/validator.md b/specs/merge/validator.md index 1ccd7fe9ea..c4b9e0481a 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -69,15 +69,18 @@ def get_pow_block_at_terminal_total_difficulty(pow_chain: Sequence[PowBlock]) -> # `pow_chain` abstractly represents all blocks in the PoW chain for block in pow_chain: parent = get_pow_block(block.parent_hash) - if block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY and parent.total_difficulty < TERMINAL_TOTAL_DIFFICULTY: + block_reached_ttd = block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY + parent_reached_ttd = parent.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY + if block_reached_ttd and not parent_reached_ttd: return block return None + def get_terminal_pow_block(pow_chain: Sequence[PowBlock]) -> Optional[PowBlock]: if TERMINAL_BLOCK_HASH != Hash32(): # Terminal lock hash override takes precedence over terminal total difficulty - pow_block_overrides = [pow_block for pow_block in pow_chain if pow_block.block_hash == TERMINAL_BLOCK_HASH] + pow_block_overrides = [block for block in pow_chain if block.block_hash == TERMINAL_BLOCK_HASH] if len(pow_block_overrides) != 0: return pow_block_overrides[0] else: From 0b808b5c5a47b504399a64b80fc545dccd401712 Mon Sep 17 00:00:00 2001 From: Fredrik Svantes Date: Fri, 24 Sep 2021 16:33:14 +0200 Subject: [PATCH 20/34] pip no longer invoked by an old script wrapper. while debugging an error I encountered while running setup.py I also saw pip.main was used to installed packages. installing packages through pip.main (pip.main(["install", RUAMEL_YAML_VERSION])) is not recommended, and will be removed from future versions of pip. https://github.com/pypa/pip/issues/5599 https://pip.pypa.io/en/latest/user_guide/#using-pip-from-your-program --- setup.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index 7620020e6f..e924dfce33 100644 --- a/setup.py +++ b/setup.py @@ -10,15 +10,18 @@ from typing import Dict, NamedTuple, List, Sequence, Optional, TypeVar from abc import ABC, abstractmethod import ast - +import subprocess +import sys # NOTE: have to programmatically include third-party dependencies in `setup.py`. +def installPackage(package: str): + subprocess.check_call([sys.executable, '-m', 'pip', 'install', package]) + RUAMEL_YAML_VERSION = "ruamel.yaml==0.16.5" try: import ruamel.yaml except ImportError: - import pip - pip.main(["install", RUAMEL_YAML_VERSION]) + installPackage(RUAMEL_YAML_VERSION) from ruamel.yaml import YAML @@ -26,8 +29,7 @@ try: import marko except ImportError: - import pip - pip.main(["install", MARKO_VERSION]) + installPackage(MARKO_VERSION) from marko.block import Heading, FencedCode, LinkRefDef, BlankLine from marko.inline import CodeSpan From 457b0396ddee2c6bbb11bb46916fa068c81d4830 Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Fri, 24 Sep 2021 20:25:18 +0300 Subject: [PATCH 21/34] 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 aaaf5c23b8..b26417e8f1 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 6c2a623b2a..42d83b4a0e 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 22/34] 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 b26417e8f1..6406142886 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 02b2a5942d..38af539b16 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.fork_choice import ( prepare_empty_pow_block, + add_pow_block, ) @@ -21,7 +23,7 @@ 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") + raise BlockNotFoundException() get_pow_block_backup = spec.get_pow_block spec.get_pow_block = get_pow_block @@ -30,21 +32,23 @@ class AtomicBoolean(): 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 2f006b07e6..48dde2fb11 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 9f037180bad44c4093a922b5cc5e8486ac30779b Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 24 Sep 2021 12:14:39 -0600 Subject: [PATCH 23/34] add ALTAIR_FORK_EPOCH for mainnet configuration --- configs/mainnet.yaml | 2 +- specs/altair/fork.md | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index 6f2f582fac..90e5d8736d 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -29,7 +29,7 @@ GENESIS_DELAY: 604800 # Altair ALTAIR_FORK_VERSION: 0x01000000 -ALTAIR_FORK_EPOCH: 18446744073709551615 +ALTAIR_FORK_EPOCH: 74240 # Oct 27, 2021, 10:56:23pm UTC # Merge MERGE_FORK_VERSION: 0x02000000 MERGE_FORK_EPOCH: 18446744073709551615 diff --git a/specs/altair/fork.md b/specs/altair/fork.md index f80064a830..739da80a53 100644 --- a/specs/altair/fork.md +++ b/specs/altair/fork.md @@ -1,7 +1,5 @@ # Altair -- Fork Logic -**Notice**: This document is a work-in-progress for researchers and implementers. - ## Table of contents @@ -26,13 +24,13 @@ Warning: this configuration is not definitive. | Name | Value | | - | - | | `ALTAIR_FORK_VERSION` | `Version('0x01000000')` | -| `ALTAIR_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** | +| `ALTAIR_FORK_EPOCH` | `Epoch(74240)` (Oct 27, 2021, 10:56:23pm UTC) | ## Fork to Altair ### Fork trigger -TBD. Social consensus, along with state conditions such as epoch boundary, finality, deposits, active validator count, etc. may be part of the decision process to trigger the fork. For now we assume the condition will be triggered at epoch `ALTAIR_FORK_EPOCH`. +The fork is triggered at epoch `ALTAIR_FORK_EPOCH`. Note that for the pure Altair networks, we don't apply `upgrade_to_altair` since it starts with Altair version logic. From 7b0e7ceafae30861da7101ff8e9eaba91efd5359 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 24 Sep 2021 12:28:46 -0600 Subject: [PATCH 24/34] Update configs/mainnet.yaml Co-authored-by: terence tsao --- configs/mainnet.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index 90e5d8736d..be61d070cc 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -29,7 +29,7 @@ GENESIS_DELAY: 604800 # Altair ALTAIR_FORK_VERSION: 0x01000000 -ALTAIR_FORK_EPOCH: 74240 # Oct 27, 2021, 10:56:23pm UTC +ALTAIR_FORK_EPOCH: 74240 # Oct 27, 2021, 10:56:23am UTC # Merge MERGE_FORK_VERSION: 0x02000000 MERGE_FORK_EPOCH: 18446744073709551615 From af5eef37b200129105c8430cc1e610c80b171ed3 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 24 Sep 2021 12:29:34 -0600 Subject: [PATCH 25/34] fix am/pm issue --- specs/altair/fork.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/altair/fork.md b/specs/altair/fork.md index 739da80a53..6228022b8f 100644 --- a/specs/altair/fork.md +++ b/specs/altair/fork.md @@ -24,7 +24,7 @@ Warning: this configuration is not definitive. | Name | Value | | - | - | | `ALTAIR_FORK_VERSION` | `Version('0x01000000')` | -| `ALTAIR_FORK_EPOCH` | `Epoch(74240)` (Oct 27, 2021, 10:56:23pm UTC) | +| `ALTAIR_FORK_EPOCH` | `Epoch(74240)` (Oct 27, 2021, 10:56:23am UTC) | ## Fork to Altair From 9353c71a6d2f3fb75c82d8321ba7c67ed48dd1bc Mon Sep 17 00:00:00 2001 From: lsankar4033 Date: Fri, 24 Sep 2021 11:57:58 -0700 Subject: [PATCH 26/34] Fix spelling --- specs/merge/validator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/merge/validator.md b/specs/merge/validator.md index c4b9e0481a..9eb140ef32 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -79,7 +79,7 @@ def get_pow_block_at_terminal_total_difficulty(pow_chain: Sequence[PowBlock]) -> def get_terminal_pow_block(pow_chain: Sequence[PowBlock]) -> Optional[PowBlock]: if TERMINAL_BLOCK_HASH != Hash32(): - # Terminal lock hash override takes precedence over terminal total difficulty + # Terminal block hash override takes precedence over terminal total difficulty pow_block_overrides = [block for block in pow_chain if block.block_hash == TERMINAL_BLOCK_HASH] if len(pow_block_overrides) != 0: return pow_block_overrides[0] From deb4dbd9c889f14f5d708410b024d9d75c188f0e Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Sat, 25 Sep 2021 01:26:01 +0300 Subject: [PATCH 27/34] 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 6406142886..cc0860bfd8 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 42ef3548e14526e9ca16f3d180f7886637e4d928 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 27 Sep 2021 08:01:27 -0600 Subject: [PATCH 28/34] bump VERSION.txt to 1.1.0 --- tests/core/pyspec/eth2spec/VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/VERSION.txt b/tests/core/pyspec/eth2spec/VERSION.txt index fbbb0af4d5..1cc5f657e0 100644 --- a/tests/core/pyspec/eth2spec/VERSION.txt +++ b/tests/core/pyspec/eth2spec/VERSION.txt @@ -1 +1 @@ -1.1.0-beta.5 \ No newline at end of file +1.1.0 \ No newline at end of file From c8d05c60ecc28f85879b18d049011bc45d8fd7b7 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 27 Sep 2021 08:23:02 -0600 Subject: [PATCH 29/34] 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 cc0860bfd8..04843078f6 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 e7317e228346d2eb5f3d7414b20f9485b2fa687c Mon Sep 17 00:00:00 2001 From: Etan Kissling Date: Fri, 24 Sep 2021 11:10:57 +0200 Subject: [PATCH 30/34] merkle proof test generator Building merkle proofs is required functionality for implementing light client sync. Although the spec currently only defines a function to verify merkle proofs (`is_valid_merkle_branch`) there are still a few PySpec unit tests that produce merkle proofs. This patch adds a new generator to extract test vectors from those static unit tests, so that light client implementations can validate their merkle proof logic. --- .../test/altair/unittests/test_helpers.py | 8 +++++ tests/formats/merkle/README.md | 36 +++++++++++++++++++ tests/generators/merkle/README.md | 6 ++++ tests/generators/merkle/__init__.py | 0 tests/generators/merkle/main.py | 14 ++++++++ tests/generators/merkle/requirements.txt | 2 ++ 6 files changed, 66 insertions(+) create mode 100644 tests/formats/merkle/README.md create mode 100644 tests/generators/merkle/README.md create mode 100644 tests/generators/merkle/__init__.py create mode 100644 tests/generators/merkle/main.py create mode 100644 tests/generators/merkle/requirements.txt diff --git a/tests/core/pyspec/eth2spec/test/altair/unittests/test_helpers.py b/tests/core/pyspec/eth2spec/test/altair/unittests/test_helpers.py index 137539113a..7eefc458b6 100644 --- a/tests/core/pyspec/eth2spec/test/altair/unittests/test_helpers.py +++ b/tests/core/pyspec/eth2spec/test/altair/unittests/test_helpers.py @@ -9,7 +9,11 @@ @with_phases([ALTAIR]) @spec_state_test def test_next_sync_committee_tree(spec, state): + yield "state", state + yield "leaf", state.next_sync_committee.hash_tree_root() + yield "leaf_index", "meta", spec.NEXT_SYNC_COMMITTEE_INDEX next_sync_committee_branch = build_proof(state.get_backing(), spec.NEXT_SYNC_COMMITTEE_INDEX) + yield "proof", next_sync_committee_branch assert spec.is_valid_merkle_branch( leaf=state.next_sync_committee.hash_tree_root(), branch=next_sync_committee_branch, @@ -22,7 +26,11 @@ def test_next_sync_committee_tree(spec, state): @with_phases([ALTAIR]) @spec_state_test def test_finality_root_tree(spec, state): + yield "state", state + yield "leaf", state.finalized_checkpoint.root + yield "leaf_index", "meta", spec.FINALIZED_ROOT_INDEX finality_branch = build_proof(state.get_backing(), spec.FINALIZED_ROOT_INDEX) + yield "proof", finality_branch assert spec.is_valid_merkle_branch( leaf=state.finalized_checkpoint.root, branch=finality_branch, diff --git a/tests/formats/merkle/README.md b/tests/formats/merkle/README.md new file mode 100644 index 0000000000..a1810d6671 --- /dev/null +++ b/tests/formats/merkle/README.md @@ -0,0 +1,36 @@ +# Merkle tests + +This series of tests provides reference test vectors for validating correct +generation and verification of merkle proofs based on static data. + +## Test case format + +### `meta.yaml` + +```yaml +leaf_index: int -- Generalized leaf index, verifying against the proof. +proof_count: int -- Amount of proof elements. +``` + +### `state.ssz_snappy` + +An SSZ-snappy encoded `BeaconState` object from which other data is generated. + +### `leaf.ssz_snappy` + +An SSZ-snappy encoded `Bytes32` reflecting the merkle root of `leaf_index` at +the given `state`. + +### `proof_.ssz_snappy` + +A series of files, with `` in range `[0, proof_count)`. Each file is an +SSZ-snappy encoded `Bytes32` and represents one element of the merkle proof for +`leaf_index` at the given `state`. + +## Condition + +A test-runner can implement the following assertions: +- Check that `is_valid_merkle_branch` confirms `leaf` at `leaf_index` to verify + against `has_tree_root(state)` and `proof`. +- If the implementation supports generating merkle proofs, check that the + self-generated proof matches the `proof` provided with the test. diff --git a/tests/generators/merkle/README.md b/tests/generators/merkle/README.md new file mode 100644 index 0000000000..a19a67d9ea --- /dev/null +++ b/tests/generators/merkle/README.md @@ -0,0 +1,6 @@ +# Merkle + +The purpose of this test-generator is to provide test-vectors for validating the +correct merkleization of objects and corresponding merkle proofs. + +Test-format documentation can be found [here](../../formats/merkle/README.md). diff --git a/tests/generators/merkle/__init__.py b/tests/generators/merkle/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/generators/merkle/main.py b/tests/generators/merkle/main.py new file mode 100644 index 0000000000..53cb59de96 --- /dev/null +++ b/tests/generators/merkle/main.py @@ -0,0 +1,14 @@ +from eth2spec.test.helpers.constants import ALTAIR +from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators + + +if __name__ == "__main__": + altair_mods = {key: 'eth2spec.test.altair.unittests.test_' + key for key in [ + 'helpers', + ]} + + all_mods = { + ALTAIR: altair_mods + } + + run_state_test_generators(runner_name="merkle", all_mods=all_mods) diff --git a/tests/generators/merkle/requirements.txt b/tests/generators/merkle/requirements.txt new file mode 100644 index 0000000000..1822486863 --- /dev/null +++ b/tests/generators/merkle/requirements.txt @@ -0,0 +1,2 @@ +pytest>=4.4 +../../../[generator] From bd8c97896511680a750afb53919bb57c67e710e1 Mon Sep 17 00:00:00 2001 From: protolambda Date: Mon, 27 Sep 2021 18:07:59 +0200 Subject: [PATCH 31/34] move merkle tests, output proof.yaml, update format --- .../eth2spec/test/altair/merkle/__init__.py | 0 .../test_single_proof.py} | 21 +++++++----- tests/formats/merkle/README.md | 34 ++----------------- tests/formats/merkle/single_proof.md | 28 +++++++++++++++ tests/generators/merkle/main.py | 4 +-- 5 files changed, 46 insertions(+), 41 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/altair/merkle/__init__.py rename tests/core/pyspec/eth2spec/test/altair/{unittests/test_helpers.py => merkle/test_single_proof.py} (66%) create mode 100644 tests/formats/merkle/single_proof.md diff --git a/tests/core/pyspec/eth2spec/test/altair/merkle/__init__.py b/tests/core/pyspec/eth2spec/test/altair/merkle/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/core/pyspec/eth2spec/test/altair/unittests/test_helpers.py b/tests/core/pyspec/eth2spec/test/altair/merkle/test_single_proof.py similarity index 66% rename from tests/core/pyspec/eth2spec/test/altair/unittests/test_helpers.py rename to tests/core/pyspec/eth2spec/test/altair/merkle/test_single_proof.py index 7eefc458b6..f9aa68adda 100644 --- a/tests/core/pyspec/eth2spec/test/altair/unittests/test_helpers.py +++ b/tests/core/pyspec/eth2spec/test/altair/merkle/test_single_proof.py @@ -8,12 +8,14 @@ @with_phases([ALTAIR]) @spec_state_test -def test_next_sync_committee_tree(spec, state): +def test_next_sync_committee_merkle_proof(spec, state): yield "state", state - yield "leaf", state.next_sync_committee.hash_tree_root() - yield "leaf_index", "meta", spec.NEXT_SYNC_COMMITTEE_INDEX next_sync_committee_branch = build_proof(state.get_backing(), spec.NEXT_SYNC_COMMITTEE_INDEX) - yield "proof", next_sync_committee_branch + yield "proof", { + "leaf": "0x" + state.next_sync_committee.hash_tree_root().hex(), + "leaf_index": spec.NEXT_SYNC_COMMITTEE_INDEX, + "branch": ['0x' + root.hex() for root in next_sync_committee_branch] + } assert spec.is_valid_merkle_branch( leaf=state.next_sync_committee.hash_tree_root(), branch=next_sync_committee_branch, @@ -25,12 +27,15 @@ def test_next_sync_committee_tree(spec, state): @with_phases([ALTAIR]) @spec_state_test -def test_finality_root_tree(spec, state): +def test_finality_root_merkle_proof(spec, state): yield "state", state - yield "leaf", state.finalized_checkpoint.root - yield "leaf_index", "meta", spec.FINALIZED_ROOT_INDEX finality_branch = build_proof(state.get_backing(), spec.FINALIZED_ROOT_INDEX) - yield "proof", finality_branch + yield "proof", { + "leaf": "0x" + state.finalized_checkpoint.root.hex(), + "leaf_index": spec.FINALIZED_ROOT_INDEX, + "branch": ['0x' + root.hex() for root in finality_branch] + } + assert spec.is_valid_merkle_branch( leaf=state.finalized_checkpoint.root, branch=finality_branch, diff --git a/tests/formats/merkle/README.md b/tests/formats/merkle/README.md index a1810d6671..c0f0a205b4 100644 --- a/tests/formats/merkle/README.md +++ b/tests/formats/merkle/README.md @@ -3,34 +3,6 @@ This series of tests provides reference test vectors for validating correct generation and verification of merkle proofs based on static data. -## Test case format - -### `meta.yaml` - -```yaml -leaf_index: int -- Generalized leaf index, verifying against the proof. -proof_count: int -- Amount of proof elements. -``` - -### `state.ssz_snappy` - -An SSZ-snappy encoded `BeaconState` object from which other data is generated. - -### `leaf.ssz_snappy` - -An SSZ-snappy encoded `Bytes32` reflecting the merkle root of `leaf_index` at -the given `state`. - -### `proof_.ssz_snappy` - -A series of files, with `` in range `[0, proof_count)`. Each file is an -SSZ-snappy encoded `Bytes32` and represents one element of the merkle proof for -`leaf_index` at the given `state`. - -## Condition - -A test-runner can implement the following assertions: -- Check that `is_valid_merkle_branch` confirms `leaf` at `leaf_index` to verify - against `has_tree_root(state)` and `proof`. -- If the implementation supports generating merkle proofs, check that the - self-generated proof matches the `proof` provided with the test. +Handlers: +- `single_proof`: see [Single leaf proof test format](./single_proof.md) +- Different types of merkle proofs may be supported in the future. diff --git a/tests/formats/merkle/single_proof.md b/tests/formats/merkle/single_proof.md new file mode 100644 index 0000000000..65fe7c9887 --- /dev/null +++ b/tests/formats/merkle/single_proof.md @@ -0,0 +1,28 @@ +# Single leaf merkle proof tests + +This series of tests provides reference test vectors for validating correct +generation and verification of merkle proofs based on static data. + +## Test case format + +### `state.ssz_snappy` + +An SSZ-snappy encoded `BeaconState` object from which other data is generated. + +### `proof.yaml` + +A proof of the leaf value (a merkle root) at generalized-index `leaf_index` in the given `state`. + +```yaml +leaf: Bytes32 # string, hex encoded, with 0x prefix +leaf_index: int # integer, decimal +branch: list of Bytes32 # list, each element is a string, hex encoded, with 0x prefix +``` + +## Condition + +A test-runner can implement the following assertions: +- Check that `is_valid_merkle_branch` confirms `leaf` at `leaf_index` to verify + against `has_tree_root(state)` and `proof`. +- If the implementation supports generating merkle proofs, check that the + self-generated proof matches the `proof` provided with the test. diff --git a/tests/generators/merkle/main.py b/tests/generators/merkle/main.py index 53cb59de96..7203c5f193 100644 --- a/tests/generators/merkle/main.py +++ b/tests/generators/merkle/main.py @@ -3,8 +3,8 @@ if __name__ == "__main__": - altair_mods = {key: 'eth2spec.test.altair.unittests.test_' + key for key in [ - 'helpers', + altair_mods = {key: 'eth2spec.test.altair.merkle.test_' + key for key in [ + 'single_proof', ]} all_mods = { From 2fa595f7842ccc453c800975ef141ca3c053a0f6 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 28 Sep 2021 00:17:18 +0800 Subject: [PATCH 32/34] 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 42d83b4a0e..2cf58f06a7 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 03e6ae8746..d0dbfae40d 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 33/34] 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 38af539b16..fd141ce8d0 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 @@ 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 wrap(flag: AtomicBoolean): 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 run_func(): 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 run_func(): 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 run_func(): 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 2cf58f06a7..cfd5ea0910 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 d0dbfae40d..05d3888daa 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 @@ 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 34/34] 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 6f2f582fac..7609908b4f 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 fd141ce8d0..830f20f23d 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 wrap(flag: AtomicBoolean): @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 run_func(): @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 run_func(): @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 run_func(): @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 = []