diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py index b26417e8f..640614288 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py @@ -1,5 +1,6 @@ from random import Random from eth_utils import encode_hex +from eth2spec.test.exceptions import BlockNotFoundException from eth2spec.utils.ssz.ssz_typing import uint256 from eth2spec.test.helpers.attestations import ( next_epoch_with_attestations, @@ -25,7 +26,7 @@ def add_block_to_store(spec, store, signed_block): def tick_and_add_block(spec, store, signed_block, test_steps, valid=True, allow_invalid_attestations=False, - merge_block=False): + merge_block=False, block_not_found=False): pre_state = store.block_states[signed_block.message.parent_root] block_time = pre_state.genesis_time + signed_block.message.slot * spec.config.SECONDS_PER_SLOT if merge_block: @@ -35,7 +36,8 @@ def tick_and_add_block(spec, store, signed_block, test_steps, valid=True, allow_ on_tick_and_append_step(spec, store, block_time, test_steps) post_state = yield from add_block( - spec, store, signed_block, test_steps, valid=valid, allow_invalid_attestations=allow_invalid_attestations) + spec, store, signed_block, test_steps, valid=valid, allow_invalid_attestations=allow_invalid_attestations, + block_not_found=block_not_found) return post_state @@ -123,7 +125,8 @@ def run_on_block(spec, store, signed_block, valid=True): assert store.blocks[signed_block.message.hash_tree_root()] == signed_block.message -def add_block(spec, store, signed_block, test_steps, valid=True, allow_invalid_attestations=False): +def add_block(spec, store, signed_block, test_steps, valid=True, allow_invalid_attestations=False, + block_not_found=False): """ Run on_block and on_attestation """ @@ -132,7 +135,9 @@ def add_block(spec, store, signed_block, test_steps, valid=True, allow_invalid_a if not valid: try: run_on_block(spec, store, signed_block, valid=True) - except AssertionError: + except (AssertionError, BlockNotFoundException) as e: + if isinstance(e, BlockNotFoundException) and not block_not_found: + assert False test_steps.append({ 'block': get_block_file_name(signed_block), 'valid': False, @@ -241,3 +246,12 @@ def prepare_empty_pow_block(spec, rng=Random(3131)): total_difficulty=uint256(0), difficulty=uint256(0) ) + + +def get_pow_block_file_name(pow_block): + return f"pow_block_{encode_hex(pow_block.block_hash)}" + + +def add_pow_block(spec, store, pow_block, test_steps): + yield get_pow_block_file_name(pow_block), pow_block + test_steps.append({'pow_block': get_pow_block_file_name(pow_block)}) diff --git a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py index 02b2a5942..38af539b1 100644 --- a/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py +++ b/tests/core/pyspec/eth2spec/test/merge/fork_choice/test_on_merge_block.py @@ -1,7 +1,8 @@ from eth2spec.utils.ssz.ssz_typing import uint256 -from eth2spec.test.context import spec_state_test, with_phases, MERGE +from eth2spec.test.exceptions import BlockNotFoundException +from eth2spec.test.context import spec_state_test, with_phases, with_presets, MERGE, MINIMAL from eth2spec.test.helpers.block import ( - build_empty_block_for_next_slot + build_empty_block_for_next_slot, ) from eth2spec.test.helpers.fork_choice import ( get_genesis_forkchoice_store_and_block, @@ -13,6 +14,7 @@ from eth2spec.test.helpers.state import ( ) from eth2spec.test.helpers.fork_choice import ( prepare_empty_pow_block, + add_pow_block, ) @@ -21,7 +23,7 @@ def with_pow_block_patch(spec, blocks, func): for block in blocks: if block.block_hash == hash: return block - raise Exception("Block not found") + raise BlockNotFoundException() get_pow_block_backup = spec.get_pow_block spec.get_pow_block = get_pow_block @@ -30,21 +32,23 @@ def with_pow_block_patch(spec, blocks, func): is_called = AtomicBoolean() def wrap(flag: AtomicBoolean): - func() + yield from func() flag.value = True try: - wrap(is_called) + yield from wrap(is_called) finally: spec.get_pow_block = get_pow_block_backup assert is_called.value @with_phases([MERGE]) +@with_presets([MINIMAL], reason="mainnet `TERMINAL_TOTAL_DIFFICULTY` stub would cause overflow") @spec_state_test def test_all_valid(spec, state): test_steps = [] # Initialization + state.latest_execution_payload_header = spec.ExecutionPayloadHeader() store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) yield 'anchor_state', state yield 'anchor_block', anchor_block @@ -52,30 +56,34 @@ def test_all_valid(spec, state): on_tick_and_append_step(spec, store, current_time, test_steps) assert store.time == current_time - parent_block = prepare_empty_pow_block(spec) - parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) - block = prepare_empty_pow_block(spec) - block.parent_hash = parent_block.block_hash - block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - pow_blocks = [block, parent_block] - yield 'pow_blocks', pow_blocks + pow_block_parent = prepare_empty_pow_block(spec) + pow_block_parent.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) + pow_block = prepare_empty_pow_block(spec) + pow_block.parent_hash = pow_block_parent.block_hash + pow_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + pow_blocks = [pow_block, pow_block_parent] + for pb in pow_blocks: + yield from add_pow_block(spec, store, pb, test_steps) def run_func(): block = build_empty_block_for_next_slot(spec, state) + block.body.execution_payload.parent_hash = pow_block.block_hash signed_block = state_transition_and_sign_block(spec, state, block) - yield from tick_and_add_block(spec, store, signed_block, test_steps) + yield from tick_and_add_block(spec, store, signed_block, test_steps, merge_block=True) # valid assert spec.get_head(store) == signed_block.message.hash_tree_root() - with_pow_block_patch(spec, pow_blocks, run_func) + yield from with_pow_block_patch(spec, pow_blocks, run_func) yield 'steps', test_steps @with_phases([MERGE]) +@with_presets([MINIMAL], reason="mainnet `TERMINAL_TOTAL_DIFFICULTY` stub would cause overflow") @spec_state_test def test_block_lookup_failed(spec, state): test_steps = [] # Initialization + state.latest_execution_payload_header = spec.ExecutionPayloadHeader() store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) yield 'anchor_state', state yield 'anchor_block', anchor_block @@ -83,27 +91,30 @@ def test_block_lookup_failed(spec, state): on_tick_and_append_step(spec, store, current_time, test_steps) assert store.time == current_time - parent_block = prepare_empty_pow_block(spec) - parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) - pow_blocks = [parent_block] - yield 'pow_blocks', pow_blocks + pow_block = prepare_empty_pow_block(spec) + pow_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) + pow_blocks = [pow_block] + for pb in pow_blocks: + yield from add_pow_block(spec, store, pb, test_steps) def run_func(): block = build_empty_block_for_next_slot(spec, state) + block.body.execution_payload.parent_hash = pow_block.block_hash signed_block = state_transition_and_sign_block(spec, state, block) - yield from tick_and_add_block(spec, store, signed_block, test_steps) - # invalid - assert spec.get_head(store) == anchor_block.state_root + yield from tick_and_add_block(spec, store, signed_block, test_steps, valid=False, merge_block=True, + block_not_found=True) - with_pow_block_patch(spec, pow_blocks, run_func) + yield from with_pow_block_patch(spec, pow_blocks, run_func) yield 'steps', test_steps @with_phases([MERGE]) +@with_presets([MINIMAL], reason="mainnet `TERMINAL_TOTAL_DIFFICULTY` stub would cause overflow") @spec_state_test def test_too_early_for_merge(spec, state): test_steps = [] # Initialization + state.latest_execution_payload_header = spec.ExecutionPayloadHeader() store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) yield 'anchor_state', state yield 'anchor_block', anchor_block @@ -111,30 +122,32 @@ def test_too_early_for_merge(spec, state): on_tick_and_append_step(spec, store, current_time, test_steps) assert store.time == current_time - parent_block = prepare_empty_pow_block(spec) - parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(2) - block = prepare_empty_pow_block(spec) - block.parent_hash = parent_block.block_hash - block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) - pow_blocks = [block, parent_block] - yield 'pow_blocks', pow_blocks + pow_block_parent = prepare_empty_pow_block(spec) + pow_block_parent.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(2) + pow_block = prepare_empty_pow_block(spec) + pow_block.parent_hash = pow_block_parent.block_hash + pow_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1) + pow_blocks = [pow_block, pow_block_parent] + for pb in pow_blocks: + yield from add_pow_block(spec, store, pb, test_steps) def run_func(): block = build_empty_block_for_next_slot(spec, state) + block.body.execution_payload.parent_hash = pow_block.block_hash signed_block = state_transition_and_sign_block(spec, state, block) - yield from tick_and_add_block(spec, store, signed_block, test_steps) - # invalid - assert spec.get_head(store) == anchor_block.state_root + yield from tick_and_add_block(spec, store, signed_block, test_steps, valid=False, merge_block=True) - with_pow_block_patch(spec, pow_blocks, run_func) + yield from with_pow_block_patch(spec, pow_blocks, run_func) yield 'steps', test_steps @with_phases([MERGE]) +@with_presets([MINIMAL], reason="mainnet `TERMINAL_TOTAL_DIFFICULTY` stub would cause overflow") @spec_state_test def test_too_late_for_merge(spec, state): test_steps = [] # Initialization + state.latest_execution_payload_header = spec.ExecutionPayloadHeader() store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) yield 'anchor_state', state yield 'anchor_block', anchor_block @@ -142,20 +155,20 @@ def test_too_late_for_merge(spec, state): on_tick_and_append_step(spec, store, current_time, test_steps) assert store.time == current_time - parent_block = prepare_empty_pow_block(spec) - parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - block = prepare_empty_pow_block(spec) - block.parent_hash = parent_block.block_hash - block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + uint256(1) - pow_blocks = [block, parent_block] - yield 'pow_blocks', pow_blocks + pow_block_parent = prepare_empty_pow_block(spec) + pow_block_parent.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + pow_block = prepare_empty_pow_block(spec) + pow_block.parent_hash = pow_block_parent.block_hash + pow_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + uint256(1) + pow_blocks = [pow_block, pow_block_parent] + for pb in pow_blocks: + yield from add_pow_block(spec, store, pb, test_steps) def run_func(): block = build_empty_block_for_next_slot(spec, state) + block.body.execution_payload.parent_hash = pow_block.block_hash signed_block = state_transition_and_sign_block(spec, state, block) - yield from tick_and_add_block(spec, store, signed_block, test_steps) - # invalid - assert spec.get_head(store) == anchor_block.state_root + yield from tick_and_add_block(spec, store, signed_block, test_steps, valid=False, merge_block=True) - with_pow_block_patch(spec, pow_blocks, run_func) + yield from with_pow_block_patch(spec, pow_blocks, run_func) yield 'steps', test_steps diff --git a/tests/formats/fork_choice/README.md b/tests/formats/fork_choice/README.md index 2f006b07e..48dde2fb1 100644 --- a/tests/formats/fork_choice/README.md +++ b/tests/formats/fork_choice/README.md @@ -71,12 +71,16 @@ After this step, the `store` object may have been updated. #### `on_merge_block` execution -Adds `PowBlock` data which is required for executing `on_block(store, block)`. -Number of blocks is stored in `meta.yaml`, block file names are `pow_block_.ssz_snappy`. -The file is located in the same folder. +Adds `PowBlock` data which is required for executing `on_block(store, block)`. +```yaml +{ + pow_block: string -- the name of the `pow_block_<32-byte-root>.ssz_snappy` file. + To be used in `get_pow_block` lookup +} +``` +The file is located in the same folder (see below). PowBlocks should be used as return values for `get_pow_block(hash: Hash32) -> PowBlock` function if hashes match. - #### Checks step The checks to verify the current status of `store`.