From 68442c2eefbd245a717b1710e8bb155c1c22678f Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 13 May 2020 16:46:28 +0200 Subject: [PATCH 1/4] Update testing and restrict to incremental block transitions --- specs/phase0/beacon-chain.md | 2 +- .../pyspec/eth2spec/test/helpers/attestations.py | 3 ++- tests/core/pyspec/eth2spec/test/helpers/block.py | 15 ++++++++++----- tests/core/pyspec/eth2spec/test/helpers/state.py | 6 ++++-- .../epoch_processing/run_epoch_process_base.py | 3 ++- .../pyspec/eth2spec/test/sanity/test_slots.py | 3 ++- 6 files changed, 21 insertions(+), 11 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index cdf38dc1e..1fd18336d 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -1219,7 +1219,7 @@ def verify_block_signature(state: BeaconState, signed_block: SignedBeaconBlock) ```python def process_slots(state: BeaconState, slot: Slot) -> None: - assert state.slot <= slot + assert state.slot < slot while state.slot < slot: process_slot(state) # Process epoch on the start slot of the next epoch diff --git a/tests/core/pyspec/eth2spec/test/helpers/attestations.py b/tests/core/pyspec/eth2spec/test/helpers/attestations.py index 8215f5c5b..76e90e89f 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/attestations.py +++ b/tests/core/pyspec/eth2spec/test/helpers/attestations.py @@ -243,7 +243,8 @@ def fill_aggregate_attestation(spec, state, attestation, signed=False): def add_attestations_to_state(spec, state, attestations, slot): - spec.process_slots(state, slot) + if state.slot < slot: + spec.process_slots(state, slot) for attestation in attestations: spec.process_attestation(state, attestation) diff --git a/tests/core/pyspec/eth2spec/test/helpers/block.py b/tests/core/pyspec/eth2spec/test/helpers/block.py index 96cc30e35..b6167c569 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/block.py +++ b/tests/core/pyspec/eth2spec/test/helpers/block.py @@ -15,7 +15,8 @@ def get_proposer_index_maybe(spec, state, slot, proposer_index=None): " Signing block is slow due to transition for proposer index calculation.") # use stub state to get proposer index of future slot stub_state = state.copy() - spec.process_slots(stub_state, slot) + if stub_state.slot < slot: + spec.process_slots(stub_state, slot) proposer_index = spec.get_beacon_proposer_index(stub_state) return proposer_index @@ -52,7 +53,10 @@ def sign_block(spec, state, block, proposer_index=None): def transition_unsigned_block(spec, state, block): - spec.process_slots(state, block.slot) + if state.slot < block.slot: + spec.process_slots(state, block.slot) + assert state.latest_block_header.slot < block.slot # There may not already be a block in this slot or past it. + assert state.slot == block.slot # The block must be for this slot spec.process_block(state, block) @@ -74,9 +78,10 @@ def build_empty_block(spec, state, slot=None): if slot < state.slot: raise Exception("build_empty_block cannot build blocks for past slots") if slot > state.slot: - # transition forward in copied state to grab relevant data from state - state = state.copy() - spec.process_slots(state, slot) + if state.slot < slot: + # transition forward in copied state to grab relevant data from state + state = state.copy() + spec.process_slots(state, slot) empty_block = spec.BeaconBlock() empty_block.slot = slot diff --git a/tests/core/pyspec/eth2spec/test/helpers/state.py b/tests/core/pyspec/eth2spec/test/helpers/state.py index 46a7ce2b5..3860a11ce 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/state.py +++ b/tests/core/pyspec/eth2spec/test/helpers/state.py @@ -17,7 +17,8 @@ def next_slots(spec, state, slots): """ Transition given slots forward. """ - spec.process_slots(state, state.slot + slots) + if slots > 0: + spec.process_slots(state, state.slot + slots) def transition_to(spec, state, slot): @@ -35,7 +36,8 @@ def next_epoch(spec, state): Transition to the start slot of the next epoch """ slot = state.slot + spec.SLOTS_PER_EPOCH - (state.slot % spec.SLOTS_PER_EPOCH) - spec.process_slots(state, slot) + if slot > state.slot: + spec.process_slots(state, slot) def get_state_root(spec, state, slot) -> bytes: diff --git a/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py b/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py index af4587a2a..b8692227f 100644 --- a/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py +++ b/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py @@ -18,7 +18,8 @@ def run_epoch_processing_to(spec, state, process_name: str): slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) # transition state to slot before epoch state transition - spec.process_slots(state, slot - 1) + if state.slot < slot - 1: + spec.process_slots(state, slot - 1) # start transitioning, do one slot update before the epoch itself. spec.process_slot(state) diff --git a/tests/core/pyspec/eth2spec/test/sanity/test_slots.py b/tests/core/pyspec/eth2spec/test/sanity/test_slots.py index 6ef6be4d3..dd4f302ae 100644 --- a/tests/core/pyspec/eth2spec/test/sanity/test_slots.py +++ b/tests/core/pyspec/eth2spec/test/sanity/test_slots.py @@ -51,7 +51,8 @@ def test_double_empty_epoch(spec, state): @with_all_phases @spec_state_test def test_over_epoch_boundary(spec, state): - spec.process_slots(state, state.slot + (spec.SLOTS_PER_EPOCH // 2)) + if spec.SLOTS_PER_EPOCH > 1: + spec.process_slots(state, state.slot + (spec.SLOTS_PER_EPOCH // 2)) yield 'pre', state slots = spec.SLOTS_PER_EPOCH yield 'slots', slots From 1d147037eb6b7dd86b7b785a88c860b4cd875030 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 13 May 2020 11:48:30 -0600 Subject: [PATCH 2/4] write block tests for double slot proposal and genesis proposal --- .../pyspec/eth2spec/test/helpers/block.py | 9 ++- .../eth2spec/test/sanity/test_blocks.py | 58 ++++++++++++++++++- 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/block.py b/tests/core/pyspec/eth2spec/test/helpers/block.py index b6167c569..cc8b6f5e0 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/block.py +++ b/tests/core/pyspec/eth2spec/test/helpers/block.py @@ -77,11 +77,10 @@ def build_empty_block(spec, state, slot=None): slot = state.slot if slot < state.slot: raise Exception("build_empty_block cannot build blocks for past slots") - if slot > state.slot: - if state.slot < slot: - # transition forward in copied state to grab relevant data from state - state = state.copy() - spec.process_slots(state, slot) + if state.slot < slot: + # transition forward in copied state to grab relevant data from state + state = state.copy() + spec.process_slots(state, slot) empty_block = spec.BeaconBlock() empty_block.slot = slot diff --git a/tests/core/pyspec/eth2spec/test/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/sanity/test_blocks.py index b6b671872..60bcaddfc 100644 --- a/tests/core/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/sanity/test_blocks.py @@ -3,8 +3,11 @@ from copy import deepcopy from eth2spec.utils import bls from eth2spec.test.helpers.state import get_balance, state_transition_and_sign_block, next_slot, next_epoch -from eth2spec.test.helpers.block import build_empty_block_for_next_slot, build_empty_block, sign_block, \ - transition_unsigned_block +from eth2spec.test.helpers.block import ( + build_empty_block_for_next_slot, build_empty_block, + sign_block, + transition_unsigned_block, +) from eth2spec.test.helpers.keys import privkeys, pubkeys from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing, get_indexed_attestation_participants from eth2spec.test.helpers.proposer_slashings import get_valid_proposer_slashing @@ -71,6 +74,57 @@ def test_empty_block_transition(spec, state): assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != spec.Bytes32() +@with_phases(['phase0']) +@spec_state_test +def test_proposal_for_genesis_slot(spec, state): + assert state.slot == spec.GENESIS_SLOT + + yield 'pre', state + + block = build_empty_block(spec, state, spec.GENESIS_SLOT) + block.parent_root = state.latest_block_header.hash_tree_root() + + # Show that normal path through transition fails + failed_state = state.copy() + expect_assertion_error( + lambda: spec.state_transition(failed_state, spec.SignedBeaconBlock(message=block), validate_result=False) + ) + + # Artifically bypass the restriction in the state transition to transition and sign block for test vectors + spec.process_block(state, block) + block.state_root = state.hash_tree_root() + signed_block = sign_block(spec, state, block) + + yield 'blocks', [signed_block] + yield 'post', None + + +@with_all_phases +@spec_state_test +def test_parent_from_same_slot(spec, state): + yield 'pre', state + + parent_block = build_empty_block_for_next_slot(spec, state) + signed_parent_block = state_transition_and_sign_block(spec, state, parent_block) + + child_block = parent_block.copy() + child_block.parent_root = state.latest_block_header.hash_tree_root() + + # Show that normal path through transition fails + failed_state = state.copy() + expect_assertion_error( + lambda: spec.state_transition(failed_state, spec.SignedBeaconBlock(message=child_block), validate_result=False) + ) + + # Artifically bypass the restriction in the state transition to transition and sign block for test vectors + spec.process_block(state, child_block) + child_block.state_root = state.hash_tree_root() + signed_child_block = sign_block(spec, state, child_block) + + yield 'blocks', [signed_parent_block, signed_child_block] + yield 'post', None + + @with_all_phases @spec_state_test def test_invalid_state_root(spec, state): From e7ae4be8a97284d9aec62eb6db0b99ddd56ed0d5 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 13 May 2020 17:25:39 -0600 Subject: [PATCH 3/4] ensure that pre-state slot is less than block for all block tests --- .../pyspec/eth2spec/test/helpers/block.py | 5 ++-- .../pyspec/eth2spec/test/helpers/state.py | 18 ++++++++++++- .../test_process_attestation.py | 16 +++++------ .../test_process_attester_slashing.py | 6 ++--- ...est_process_early_derived_secret_reveal.py | 6 ++--- .../eth2spec/test/sanity/test_blocks.py | 26 +++++++++--------- .../pyspec/eth2spec/test/test_finality.py | 27 +++++++------------ 7 files changed, 53 insertions(+), 51 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/block.py b/tests/core/pyspec/eth2spec/test/helpers/block.py index cc8b6f5e0..4ed84b307 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/block.py +++ b/tests/core/pyspec/eth2spec/test/helpers/block.py @@ -53,6 +53,7 @@ def sign_block(spec, state, block, proposer_index=None): def transition_unsigned_block(spec, state, block): + assert state.slot < block.slot # Preserve assertion from state transition to avoid strange pre-states from testing if state.slot < block.slot: spec.process_slots(state, block.slot) assert state.latest_block_header.slot < block.slot # There may not already be a block in this slot or past it. @@ -60,11 +61,11 @@ def transition_unsigned_block(spec, state, block): spec.process_block(state, block) -def apply_empty_block(spec, state): +def apply_empty_block(spec, state, slot=None): """ Transition via an empty block (on current slot, assuming no block has been applied yet). """ - block = build_empty_block(spec, state) + block = build_empty_block(spec, state, slot) transition_unsigned_block(spec, state, block) diff --git a/tests/core/pyspec/eth2spec/test/helpers/state.py b/tests/core/pyspec/eth2spec/test/helpers/state.py index 3860a11ce..7e16e13d0 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/state.py +++ b/tests/core/pyspec/eth2spec/test/helpers/state.py @@ -1,5 +1,5 @@ from eth2spec.test.context import expect_assertion_error -from eth2spec.test.helpers.block import sign_block, transition_unsigned_block +from eth2spec.test.helpers.block import apply_empty_block, sign_block, transition_unsigned_block def get_balance(state, index): @@ -31,6 +31,15 @@ def transition_to(spec, state, slot): assert state.slot == slot +def transition_to_slot_via_block(spec, state, slot): + """ + Transition to ``slot`` via an empty block transition + """ + assert state.slot < slot + apply_empty_block(spec, state, slot) + assert state.slot == slot + + def next_epoch(spec, state): """ Transition to the start slot of the next epoch @@ -40,6 +49,13 @@ def next_epoch(spec, state): spec.process_slots(state, slot) +def next_epoch_via_block(spec, state): + """ + Transition to the start slot of the next epoch via a full block transition + """ + apply_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) + + def get_state_root(spec, state, slot) -> bytes: """ Return the state root at a recent ``slot``. diff --git a/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py b/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py index 8663391aa..8752d3747 100644 --- a/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py +++ b/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py @@ -15,10 +15,9 @@ from eth2spec.test.helpers.attestations import ( from eth2spec.test.helpers.state import ( next_slot, next_slots, - next_epoch, - transition_to, + next_epoch_via_block, + transition_to_slot_via_block, ) -from eth2spec.test.helpers.block import apply_empty_block from eth2spec.utils.ssz.ssz_typing import Bitlist @@ -47,9 +46,7 @@ def test_success_multi_proposer_index_iterations(spec, state): @spec_state_test def test_success_previous_epoch(spec, state): attestation = get_valid_attestation(spec, state, signed=True, on_time=False) - transition_to(spec, state, spec.SLOTS_PER_EPOCH - 1) - next_epoch(spec, state) - apply_empty_block(spec, state) + next_epoch_via_block(spec, state) yield from run_attestation_processing(spec, state, attestation) @@ -79,8 +76,7 @@ def test_after_epoch_slots(spec, state): attestation = get_valid_attestation(spec, state, signed=True, on_time=False) # increment past latest inclusion slot - transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH + 1) - apply_empty_block(spec, state) + transition_to_slot_via_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH + 1) yield from run_attestation_processing(spec, state, attestation, False) @@ -151,8 +147,8 @@ def test_invalid_index(spec, state): @with_all_phases @spec_state_test def test_mismatched_target_and_slot(spec, state): - next_epoch(spec, state) - next_epoch(spec, state) + next_epoch_via_block(spec, state) + next_epoch_via_block(spec, state) attestation = get_valid_attestation(spec, state, on_time=False) attestation.data.slot = attestation.data.slot - spec.SLOTS_PER_EPOCH diff --git a/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py b/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py index 48dc75fd9..11ead6033 100644 --- a/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py +++ b/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py @@ -5,10 +5,9 @@ from eth2spec.test.context import ( from eth2spec.test.helpers.attestations import sign_indexed_attestation from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing, \ get_indexed_attestation_participants, get_attestation_2_data, get_attestation_1_data -from eth2spec.test.helpers.block import apply_empty_block from eth2spec.test.helpers.state import ( get_balance, - next_epoch, + next_epoch_via_block, ) @@ -91,8 +90,7 @@ def test_success_double(spec, state): @with_all_phases @spec_state_test def test_success_surround(spec, state): - next_epoch(spec, state) - apply_empty_block(spec, state) + next_epoch_via_block(spec, state) state.current_justified_checkpoint.epoch += 1 attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True) diff --git a/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_early_derived_secret_reveal.py b/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_early_derived_secret_reveal.py index 83b0fe325..668561261 100644 --- a/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_early_derived_secret_reveal.py +++ b/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_early_derived_secret_reveal.py @@ -1,6 +1,5 @@ from eth2spec.test.helpers.custody import get_valid_early_derived_secret_reveal -from eth2spec.test.helpers.block import apply_empty_block -from eth2spec.test.helpers.state import next_epoch, get_balance +from eth2spec.test.helpers.state import next_epoch_via_block, get_balance from eth2spec.test.context import ( PHASE0, with_all_phases_except, @@ -64,8 +63,7 @@ def test_reveal_from_current_epoch(spec, state): @spec_state_test @never_bls def test_reveal_from_past_epoch(spec, state): - next_epoch(spec, state) - apply_empty_block(spec, state) + next_epoch_via_block(spec, state) randao_key_reveal = get_valid_early_derived_secret_reveal(spec, state, spec.get_current_epoch(state) - 1) yield from run_early_derived_secret_reveal_processing(spec, state, randao_key_reveal, False) diff --git a/tests/core/pyspec/eth2spec/test/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/sanity/test_blocks.py index 60bcaddfc..a0678dbb3 100644 --- a/tests/core/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/sanity/test_blocks.py @@ -2,7 +2,10 @@ from copy import deepcopy from eth2spec.utils import bls -from eth2spec.test.helpers.state import get_balance, state_transition_and_sign_block, next_slot, next_epoch +from eth2spec.test.helpers.state import ( + get_balance, state_transition_and_sign_block, + next_slot, next_epoch, next_epoch_via_block, +) from eth2spec.test.helpers.block import ( build_empty_block_for_next_slot, build_empty_block, sign_block, @@ -48,10 +51,12 @@ def test_same_slot_block_transition(spec, state): yield 'pre', state - signed_block = state_transition_and_sign_block(spec, state, block) + assert state.slot == block.slot + + signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True) yield 'blocks', [signed_block] - yield 'post', state + yield 'post', None @with_all_phases @@ -357,22 +362,19 @@ def test_proposer_after_inactive_index(spec, state): state.validators[inactive_index].exit_epoch = spec.get_current_epoch(state) # skip forward, get brand new proposers - next_epoch(spec, state) - next_epoch(spec, state) - block = build_empty_block_for_next_slot(spec, state) - state_transition_and_sign_block(spec, state, block) - + next_epoch_via_block(spec, state) + next_epoch_via_block(spec, state) while True: - next_slot(spec, state) proposer_index = spec.get_beacon_proposer_index(state) if proposer_index > inactive_index: # found a proposer that has a higher index than a disabled validator yield 'pre', state # test if the proposer can be recognized correctly after the inactive validator - signed_block = state_transition_and_sign_block(spec, state, build_empty_block(spec, state)) + signed_block = state_transition_and_sign_block(spec, state, build_empty_block_for_next_slot(spec, state)) yield 'blocks', [signed_block] yield 'post', state break + next_slot(spec, state) @with_all_phases @@ -390,16 +392,16 @@ def test_high_proposer_index(spec, state): active_count = len(spec.get_active_validator_indices(state, current_epoch)) while True: - next_slot(spec, state) proposer_index = spec.get_beacon_proposer_index(state) if proposer_index >= active_count: # found a proposer that has a higher index than the active validator count yield 'pre', state # test if the proposer can be recognized correctly, even while it has a high index. - signed_block = state_transition_and_sign_block(spec, state, build_empty_block(spec, state)) + signed_block = state_transition_and_sign_block(spec, state, build_empty_block_for_next_slot(spec, state)) yield 'blocks', [signed_block] yield 'post', state break + next_slot(spec, state) @with_all_phases diff --git a/tests/core/pyspec/eth2spec/test/test_finality.py b/tests/core/pyspec/eth2spec/test/test_finality.py index 0b99cc3f9..55ccde2d9 100644 --- a/tests/core/pyspec/eth2spec/test/test_finality.py +++ b/tests/core/pyspec/eth2spec/test/test_finality.py @@ -1,7 +1,6 @@ from eth2spec.test.context import spec_state_test, never_bls, with_all_phases, with_phases -from eth2spec.test.helpers.state import next_epoch +from eth2spec.test.helpers.state import next_epoch_via_block from eth2spec.test.helpers.attestations import next_epoch_with_attestations -from eth2spec.test.helpers.block import apply_empty_block def check_finality(spec, @@ -58,10 +57,8 @@ def test_finality_no_updates_at_genesis(spec, state): @never_bls def test_finality_rule_4(spec, state): # get past first two epochs that finality does not run on - next_epoch(spec, state) - apply_empty_block(spec, state) - next_epoch(spec, state) - apply_empty_block(spec, state) + next_epoch_via_block(spec, state) + next_epoch_via_block(spec, state) yield 'pre', state @@ -86,10 +83,8 @@ def test_finality_rule_4(spec, state): @never_bls def test_finality_rule_1(spec, state): # get past first two epochs that finality does not run on - next_epoch(spec, state) - apply_empty_block(spec, state) - next_epoch(spec, state) - apply_empty_block(spec, state) + next_epoch_via_block(spec, state) + next_epoch_via_block(spec, state) yield 'pre', state @@ -116,10 +111,8 @@ def test_finality_rule_1(spec, state): @never_bls def test_finality_rule_2(spec, state): # get past first two epochs that finality does not run on - next_epoch(spec, state) - apply_empty_block(spec, state) - next_epoch(spec, state) - apply_empty_block(spec, state) + next_epoch_via_block(spec, state) + next_epoch_via_block(spec, state) yield 'pre', state @@ -152,10 +145,8 @@ def test_finality_rule_3(spec, state): https://github.com/ethereum/eth2.0-specs/issues/611#issuecomment-463612892 """ # get past first two epochs that finality does not run on - next_epoch(spec, state) - apply_empty_block(spec, state) - next_epoch(spec, state) - apply_empty_block(spec, state) + next_epoch_via_block(spec, state) + next_epoch_via_block(spec, state) yield 'pre', state From 74c900f814b75d026699d92c51b8f198979a9a93 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 15 May 2020 16:39:26 -0600 Subject: [PATCH 4/4] add conditoin that block must be later than latest_block_header --- specs/phase0/beacon-chain.md | 2 + .../test_process_block_header.py | 21 +++++++++- .../eth2spec/test/sanity/test_blocks.py | 40 +++++++++++++++---- 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index 1fd18336d..4a48e968f 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -1494,6 +1494,8 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: def process_block_header(state: BeaconState, block: BeaconBlock) -> None: # Verify that the slots match assert block.slot == state.slot + # Verify that the block is newer than latest block header + assert block.slot > state.latest_block_header.slot # Verify that proposer index is the correct index assert block.proposer_index == get_beacon_proposer_index(state) # Verify that the parent matches diff --git a/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_block_header.py b/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_block_header.py index a2eb744b9..b57090568 100644 --- a/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_block_header.py +++ b/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_block_header.py @@ -9,7 +9,7 @@ def prepare_state_for_header_processing(spec, state): spec.process_slots(state, state.slot + 1) -def run_block_header_processing(spec, state, block, valid=True): +def run_block_header_processing(spec, state, block, prepare_state=True, valid=True): """ Run ``process_block_header``, yielding: - pre-state ('pre') @@ -17,7 +17,8 @@ def run_block_header_processing(spec, state, block, valid=True): - post-state ('post'). If ``valid == False``, run expecting ``AssertionError`` """ - prepare_state_for_header_processing(spec, state) + if prepare_state: + prepare_state_for_header_processing(spec, state) yield 'pre', state yield 'block', block @@ -68,6 +69,22 @@ def test_invalid_parent_root(spec, state): yield from run_block_header_processing(spec, state, block, valid=False) +@with_all_phases +@spec_state_test +def test_invalid_multiple_blocks_single_slot(spec, state): + block = build_empty_block_for_next_slot(spec, state) + + prepare_state_for_header_processing(spec, state) + spec.process_block_header(state, block) + + assert state.latest_block_header.slot == state.slot + + child_block = block.copy() + child_block.parent_root = block.hash_tree_root() + + yield from run_block_header_processing(spec, state, child_block, prepare_state=False, valid=False) + + @with_all_phases @spec_state_test def test_proposer_slashed(spec, state): diff --git a/tests/core/pyspec/eth2spec/test/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/sanity/test_blocks.py index a0678dbb3..71ee5141d 100644 --- a/tests/core/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/sanity/test_blocks.py @@ -79,6 +79,34 @@ def test_empty_block_transition(spec, state): assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != spec.Bytes32() +def process_and_sign_block_without_header_validations(spec, state, block): + """ + Artificially bypass the restrictions in the state transition to transition and sign block + + WARNING UNSAFE: Only use when generating valid-looking invalid blocks for test vectors + """ + + # Perform single mutation in `process_block_header` + state.latest_block_header = spec.BeaconBlockHeader( + slot=block.slot, + proposer_index=block.proposer_index, + parent_root=block.parent_root, + state_root=spec.Bytes32(), + body_root=block.body.hash_tree_root(), + ) + + # Perform rest of process_block transitions + spec.process_randao(state, block.body) + spec.process_eth1_data(state, block.body) + spec.process_operations(state, block.body) + + # Insert post-state rot + block.state_root = state.hash_tree_root() + + # Sign block + return sign_block(spec, state, block) + + @with_phases(['phase0']) @spec_state_test def test_proposal_for_genesis_slot(spec, state): @@ -95,10 +123,8 @@ def test_proposal_for_genesis_slot(spec, state): lambda: spec.state_transition(failed_state, spec.SignedBeaconBlock(message=block), validate_result=False) ) - # Artifically bypass the restriction in the state transition to transition and sign block for test vectors - spec.process_block(state, block) - block.state_root = state.hash_tree_root() - signed_block = sign_block(spec, state, block) + # Artificially bypass the restriction in the state transition to transition and sign block for test vectors + signed_block = process_and_sign_block_without_header_validations(spec, state, block) yield 'blocks', [signed_block] yield 'post', None @@ -121,10 +147,8 @@ def test_parent_from_same_slot(spec, state): lambda: spec.state_transition(failed_state, spec.SignedBeaconBlock(message=child_block), validate_result=False) ) - # Artifically bypass the restriction in the state transition to transition and sign block for test vectors - spec.process_block(state, child_block) - child_block.state_root = state.hash_tree_root() - signed_child_block = sign_block(spec, state, child_block) + # Artificially bypass the restriction in the state transition to transition and sign block for test vectors + signed_child_block = process_and_sign_block_without_header_validations(spec, state, child_block) yield 'blocks', [signed_parent_block, signed_child_block] yield 'post', None