Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
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
Ejemplo n.º 7
0
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
Ejemplo n.º 10
0
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)
Ejemplo n.º 11
0
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)
Ejemplo n.º 14
0
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