def test_invalid_proposer_index_sig_from_expected_proposer(spec, state): yield 'pre', state block = build_empty_block_for_next_slot(spec, state) expect_proposer_index = block.proposer_index # Set invalid proposer index but correct signature wrt expected proposer active_indices = spec.get_active_validator_indices( state, spec.get_current_epoch(state)) active_indices = [i for i in active_indices if i != block.proposer_index] block.proposer_index = active_indices[0] # invalid proposer index invalid_signed_block = sign_block(spec, state, block, expect_proposer_index) expect_assertion_error( lambda: spec.state_transition(state, invalid_signed_block)) yield 'blocks', [invalid_signed_block] yield 'post', None
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) ) # 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
def test_prev_slot_block_transition(spec, state): beacon_state, shard_state = configure_shard_state(spec, state) # Go to clean slot spec.process_shard_slots(shard_state, shard_state.slot + 1) # Make a block for it block = build_empty_shard_block(spec, beacon_state, shard_state, slot=shard_state.slot, signed=True) # Transition to next slot, above block will not be invalid on top of new state. spec.process_shard_slots(shard_state, shard_state.slot + 1) yield 'pre', shard_state yield 'beacon_state', beacon_state expect_assertion_error( lambda: spec.shard_state_transition(beacon_state, shard_state, block)) yield 'blocks', [block] yield 'post', None
def run_attestation_processing(spec, state, attestation, valid=True): """ Run ``process_attestation``, yielding: - pre-state ('pre') - attestation ('attestation') - post-state ('post'). If ``valid == False``, run expecting ``AssertionError`` """ # yield pre-state yield 'pre', state yield 'attestation', attestation # If the attestation is invalid, processing is aborted, and there is no post-state. if not valid: expect_assertion_error( lambda: spec.process_attestation(state, attestation)) yield 'post', None return if not is_post_altair(spec): current_epoch_count = len(state.current_epoch_attestations) previous_epoch_count = len(state.previous_epoch_attestations) # process attestation spec.process_attestation(state, attestation) # Make sure the attestation has been processed if not is_post_altair(spec): if attestation.data.target.epoch == spec.get_current_epoch(state): assert len( state.current_epoch_attestations) == current_epoch_count + 1 else: assert len( state.previous_epoch_attestations) == previous_epoch_count + 1 else: # After accounting reform, there are cases when processing an attestation does not result in any flag updates pass # yield post-state yield 'post', state
def test_prev_slot_block_transition(spec, state): # Go to clean slot spec.process_slots(state, state.slot + 1) # Make a block for it block = build_empty_block(spec, state, slot=state.slot) proposer_index = spec.get_beacon_proposer_index(state) # Transition to next slot, above block will not be invalid on top of new state. spec.process_slots(state, state.slot + 1) yield 'pre', state # State is beyond block slot, but the block can still be realistic when invalid. # Try the transition, and update the state root to where it is halted. Then sign with the supposed proposer. expect_assertion_error( lambda: transition_unsigned_block(spec, state, block)) block.state_root = state.hash_tree_root() signed_block = sign_block(spec, state, block, proposer_index=proposer_index) yield 'blocks', [signed_block] yield 'post', None
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)) # 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
def run_custody_chunk_response_processing(spec, state, custody_response, valid=True): """ Run ``process_chunk_challenge_response``, yielding: - pre-state ('pre') - CustodyResponse ('custody_response') - post-state ('post'). If ``valid == False``, run expecting ``AssertionError`` """ yield 'pre', state yield 'custody_response', custody_response if not valid: expect_assertion_error(lambda: spec.process_custody_response(state, custody_response)) yield 'post', None return spec.process_chunk_challenge_response(state, custody_response) assert state.custody_chunk_challenge_records[custody_response.challenge_index] == spec.CustodyChunkChallengeRecord() yield 'post', state
def run_block_header_processing(spec, state, block, prepare_state=True, valid=True): """ Run ``process_block_header``, yielding: - pre-state ('pre') - block ('block') - post-state ('post'). If ``valid == False``, run expecting ``AssertionError`` """ if prepare_state: prepare_state_for_header_processing(spec, state) yield 'pre', state yield 'block', block if not valid: expect_assertion_error(lambda: spec.process_block_header(state, block)) yield 'post', None return spec.process_block_header(state, block) yield 'post', state
def run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=True): """ Run ``process_bls_to_execution_change``, yielding: - pre-state ('pre') - address-change ('address_change') - post-state ('post'). If ``valid == False``, run expecting ``AssertionError`` """ # yield pre-state yield 'pre', state yield 'address_change', signed_address_change # If the address_change is invalid, processing is aborted, and there is no post-state. if not valid: expect_assertion_error(lambda: spec.process_bls_to_execution_change( state, signed_address_change)) yield 'post', None return # process address change spec.process_bls_to_execution_change(state, signed_address_change) # Make sure the address change has been processed validator_index = signed_address_change.message.validator_index validator = state.validators[validator_index] assert validator.withdrawal_credentials[: 1] == spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX assert validator.withdrawal_credentials[1:12] == b'\x00' * 11 assert validator.withdrawal_credentials[ 12:] == signed_address_change.message.to_execution_address # yield post-state yield 'post', state
def run_proposer_slashing_processing(spec, state, proposer_slashing, valid=True): """ Run ``process_proposer_slashing``, yielding: - pre-state ('pre') - proposer_slashing ('proposer_slashing') - post-state ('post'). If ``valid == False``, run expecting ``AssertionError`` """ pre_state = state.copy() yield 'pre', state yield 'proposer_slashing', proposer_slashing if not valid: expect_assertion_error(lambda: spec.process_proposer_slashing(state, proposer_slashing)) yield 'post', None return spec.process_proposer_slashing(state, proposer_slashing) yield 'post', state slashed_proposer_index = proposer_slashing.signed_header_1.message.proposer_index check_proposer_slashing_effect(spec, pre_state, state, slashed_proposer_index)
def run_chunk_challenge_processing(spec, state, custody_chunk_challenge, valid=True): """ Run ``process_chunk_challenge``, yielding: - pre-state ('pre') - CustodyBitChallenge ('custody_chunk_challenge') - post-state ('post'). If ``valid == False``, run expecting ``AssertionError`` """ yield 'pre', state yield 'custody_chunk_challenge', custody_chunk_challenge if not valid: expect_assertion_error(lambda: spec.process_chunk_challenge(state, custody_chunk_challenge)) yield 'post', None return spec.process_chunk_challenge(state, custody_chunk_challenge) assert state.custody_chunk_challenge_records[state.custody_chunk_challenge_index - 1].responder_index == \ custody_chunk_challenge.responder_index assert state.custody_chunk_challenge_records[state.custody_chunk_challenge_index - 1].chunk_index == \ custody_chunk_challenge.chunk_index yield 'post', state
def run_early_derived_secret_reveal_processing(spec, state, randao_key_reveal, valid=True): """ Run ``process_randao_key_reveal``, yielding: - pre-state ('pre') - randao_key_reveal ('randao_key_reveal') - post-state ('post'). If ``valid == False``, run expecting ``AssertionError`` """ yield 'pre', state yield 'randao_key_reveal', randao_key_reveal if not valid: expect_assertion_error( lambda: spec.process_early_derived_secret_reveal( state, randao_key_reveal)) yield 'post', None return pre_slashed_balance = get_balance(state, randao_key_reveal.revealed_index) spec.process_early_derived_secret_reveal(state, randao_key_reveal) slashed_validator = state.validators[randao_key_reveal.revealed_index] if randao_key_reveal.epoch >= spec.get_current_epoch( state) + spec.CUSTODY_PERIOD_TO_RANDAO_PADDING: assert slashed_validator.slashed assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH assert get_balance(state, randao_key_reveal.revealed_index) < pre_slashed_balance yield 'post', state
def run_proposer_slashing_processing(spec, state, proposer_slashing, valid=True): """ Run ``process_proposer_slashing``, yielding: - pre-state ('pre') - proposer_slashing ('proposer_slashing') - post-state ('post'). If ``valid == False``, run expecting ``AssertionError`` """ yield 'pre', state yield 'proposer_slashing', proposer_slashing if not valid: expect_assertion_error( lambda: spec.process_proposer_slashing(state, proposer_slashing)) yield 'post', None return pre_proposer_balance = get_balance(state, proposer_slashing.proposer_index) spec.process_proposer_slashing(state, proposer_slashing) yield 'post', state # check if slashed slashed_validator = state.validator_registry[ proposer_slashing.proposer_index] assert slashed_validator.slashed assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH # lost whistleblower reward assert (get_balance(state, proposer_slashing.proposer_index) < pre_proposer_balance)
def run_execution_payload_processing(spec, state, execution_payload, valid=True, execution_valid=True): """ Run ``process_execution_payload``, yielding: - pre-state ('pre') - execution payload ('execution_payload') - execution details, to mock EVM execution ('execution.yml', a dict with 'execution_valid' key and boolean value) - post-state ('post'). If ``valid == False``, run expecting ``AssertionError`` """ yield 'pre', state yield 'execution', {'execution_valid': execution_valid} yield 'execution_payload', execution_payload called_new_block = False class TestEngine(spec.NoopExecutionEngine): def execute_payload(self, payload) -> bool: nonlocal called_new_block, execution_valid called_new_block = True assert payload == execution_payload return execution_valid if not valid: expect_assertion_error(lambda: spec.process_execution_payload(state, execution_payload, TestEngine())) yield 'post', None return spec.process_execution_payload(state, execution_payload, TestEngine()) # Make sure we called the engine assert called_new_block yield 'post', state assert state.latest_execution_payload_header == get_execution_payload_header(spec, execution_payload)
def test_invalid_signature_past_block(spec, state): committee = spec.get_sync_committee_indices(state, spec.get_current_epoch(state)) yield 'pre', state blocks = [] for _ in range(2): # NOTE: need to transition twice to move beyond the degenerate case at genesis block = build_empty_block_for_next_slot(spec, state) # Valid sync committee signature here... block.body.sync_committee_bits = [True] * len(committee) block.body.sync_committee_signature = compute_aggregate_sync_committee_signature( spec, state, block.slot - 1, committee, ) signed_block = state_transition_and_sign_block(spec, state, block) blocks.append(signed_block) invalid_block = build_empty_block_for_next_slot(spec, state) # Invalid signature from a slot other than the previous invalid_block.body.sync_committee_bits = [True] * len(committee) invalid_block.body.sync_committee_signature = compute_aggregate_sync_committee_signature( spec, state, invalid_block.slot - 2, committee, ) blocks.append(invalid_block) expect_assertion_error(lambda: transition_unsigned_block(spec, state, invalid_block)) yield 'blocks', blocks yield 'post', None
def run_attester_slashing_processing(spec, state, attester_slashing, valid=True): """ Run ``process_attester_slashing``, yielding: - pre-state ('pre') - attester_slashing ('attester_slashing') - post-state ('post'). If ``valid == False``, run expecting ``AssertionError`` """ yield 'pre', state yield 'attester_slashing', attester_slashing if not valid: expect_assertion_error( lambda: spec.process_attester_slashing(state, attester_slashing)) yield 'post', None return slashed_indices = attester_slashing.attestation_1.attesting_indices proposer_index = spec.get_beacon_proposer_index(state) pre_proposer_balance = get_balance(state, proposer_index) pre_slashings = { slashed_index: get_balance(state, slashed_index) for slashed_index in slashed_indices } pre_withdrawalable_epochs = { slashed_index: state.validators[slashed_index].withdrawable_epoch for slashed_index in slashed_indices } total_proposer_rewards = sum(balance // spec.WHISTLEBLOWER_REWARD_QUOTIENT for balance in pre_slashings.values()) # Process slashing spec.process_attester_slashing(state, attester_slashing) for slashed_index in slashed_indices: pre_withdrawalable_epoch = pre_withdrawalable_epochs[slashed_index] slashed_validator = state.validators[slashed_index] # Check slashing assert slashed_validator.slashed assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH if pre_withdrawalable_epoch < spec.FAR_FUTURE_EPOCH: expected_withdrawable_epoch = max( pre_withdrawalable_epoch, spec.get_current_epoch(state) + spec.EPOCHS_PER_SLASHINGS_VECTOR) assert slashed_validator.withdrawable_epoch == expected_withdrawable_epoch else: assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH assert get_balance(state, slashed_index) < pre_slashings[slashed_index] if proposer_index not in slashed_indices: # gained whistleblower reward assert get_balance( state, proposer_index) == pre_proposer_balance + total_proposer_rewards else: # gained rewards for all slashings, which may include others. And only lost that of themselves. expected_balance = (pre_proposer_balance + total_proposer_rewards - pre_slashings[proposer_index] // spec.MIN_SLASHING_PENALTY_QUOTIENT) assert get_balance(state, proposer_index) == expected_balance yield 'post', state