示例#1
0
def run_slash_and_exit(spec, state, slash_index, exit_index, valid=True):
    """
    Helper function to run a test that slashes and exits two validators
    """
    # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
    state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH

    yield 'pre', state

    block = build_empty_block_for_next_slot(spec, state)

    proposer_slashing = get_valid_proposer_slashing(spec,
                                                    state,
                                                    slashed_index=slash_index,
                                                    signed_1=True,
                                                    signed_2=True)
    signed_exit = prepare_signed_exits(spec, state, [exit_index])[0]

    block.body.proposer_slashings.append(proposer_slashing)
    block.body.voluntary_exits.append(signed_exit)

    signed_block = state_transition_and_sign_block(spec,
                                                   state,
                                                   block,
                                                   expect_fail=(not valid))

    yield 'blocks', [signed_block]

    if not valid:
        yield 'post', None
        return

    yield 'post', state
示例#2
0
def test_proposer_slashing(spec, state):
    # copy for later balance lookups.
    pre_state = state.copy()
    proposer_slashing = get_valid_proposer_slashing(spec,
                                                    state,
                                                    signed_1=True,
                                                    signed_2=True)
    slashed_index = proposer_slashing.signed_header_1.message.proposer_index

    assert not state.validators[slashed_index].slashed

    yield 'pre', state

    #
    # Add to state via block transition
    #
    block = build_empty_block_for_next_slot(spec, state)
    block.body.proposer_slashings.append(proposer_slashing)

    signed_block = state_transition_and_sign_block(spec, state, block)

    yield 'blocks', [signed_block]
    yield 'post', state

    check_proposer_slashing_effect(spec, pre_state, state, slashed_index,
                                   block)
def test_multiple_different_proposer_slashings_same_block(spec, state):
    pre_state = state.copy()

    num_slashings = 3
    proposer_slashings = []
    for i in range(num_slashings):
        slashed_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[i]
        assert not state.validators[slashed_index].slashed

        proposer_slashing = get_valid_proposer_slashing(spec, state,
                                                        slashed_index=slashed_index,
                                                        signed_1=True, signed_2=True)
        proposer_slashings.append(proposer_slashing)

    yield 'pre', state

    #
    # Add to state via block transition
    #
    block = build_empty_block_for_next_slot(spec, state)
    block.body.proposer_slashings = proposer_slashings

    signed_block = state_transition_and_sign_block(spec, state, block)

    yield 'blocks', [signed_block]
    yield 'post', state

    for proposer_slashing in proposer_slashings:
        slashed_index = proposer_slashing.signed_header_1.message.proposer_index
        check_proposer_slashing_effect(spec, pre_state, state, slashed_index)
示例#4
0
def test_proposer_slashing(spec, state):
    # copy for later balance lookups.
    pre_state = deepcopy(state)
    proposer_slashing = get_valid_proposer_slashing(spec,
                                                    state,
                                                    signed_1=True,
                                                    signed_2=True)
    validator_index = proposer_slashing.proposer_index

    assert not state.validators[validator_index].slashed

    yield 'pre', state

    #
    # Add to state via block transition
    #
    block = build_empty_block_for_next_slot(spec, state)
    block.body.proposer_slashings.append(proposer_slashing)
    sign_block(spec, state, block)

    state_transition_and_sign_block(spec, state, block)

    yield 'blocks', [block]
    yield 'post', state

    # check if slashed
    slashed_validator = state.validators[validator_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, validator_index) < get_balance(
        pre_state, validator_index)
示例#5
0
def test_proposer_is_slashed(spec, state):
    proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=True)

    # set proposer to slashed
    proposer_index = proposer_slashing.signed_header_1.message.proposer_index
    state.validators[proposer_index].slashed = True

    yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False)
示例#6
0
def test_proposer_is_not_activated(spec, state):
    proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=True)

    # set proposer to be not active yet
    proposer_index = proposer_slashing.signed_header_1.message.proposer_index
    state.validators[proposer_index].activation_epoch = spec.get_current_epoch(state) + 1

    yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False)
def test_success_block_header_from_future(spec, state):
    proposer_slashing = get_valid_proposer_slashing(spec,
                                                    state,
                                                    slot=state.slot + 5,
                                                    signed_1=True,
                                                    signed_2=True)

    yield from run_proposer_slashing_processing(spec, state, proposer_slashing)
示例#8
0
def test_invalid_sig_1_and_2_swap(spec, state):
    # Get valid signatures for the slashings
    proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=True)

    # But swap them
    signature_1 = proposer_slashing.signed_header_1.signature
    proposer_slashing.signed_header_1.signature = proposer_slashing.signed_header_2.signature
    proposer_slashing.signed_header_2.signature = signature_1
    yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False)
def test_invalid_proposer_index(state):
    proposer_slashing = get_valid_proposer_slashing(state,
                                                    signed_1=True,
                                                    signed_2=True)
    # Index just too high (by 1)
    proposer_slashing.proposer_index = len(state.validator_registry)

    yield from run_proposer_slashing_processing(state, proposer_slashing,
                                                False)
def test_proposer_is_slashed(state):
    proposer_slashing = get_valid_proposer_slashing(state,
                                                    signed_1=True,
                                                    signed_2=True)

    # set proposer to slashed
    state.validator_registry[proposer_slashing.proposer_index].slashed = True

    yield from run_proposer_slashing_processing(state, proposer_slashing,
                                                False)
def test_headers_are_same(state):
    proposer_slashing = get_valid_proposer_slashing(state,
                                                    signed_1=True,
                                                    signed_2=False)

    # set headers to be the same
    proposer_slashing.header_2 = proposer_slashing.header_1

    yield from run_proposer_slashing_processing(state, proposer_slashing,
                                                False)
示例#12
0
def test_epochs_are_different(spec, state):
    proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=False)

    # set slots to be in different epochs
    header_2 = proposer_slashing.signed_header_2.message
    proposer_index = header_2.proposer_index
    header_2.slot += spec.SLOTS_PER_EPOCH
    proposer_slashing.signed_header_2 = sign_block_header(spec, state, header_2, privkeys[proposer_index])

    yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False)
示例#13
0
def test_success(spec, state):
    # Get proposer for next slot
    block = build_empty_block_for_next_slot(spec, state)
    proposer_index = block.proposer_index

    # Create slashing for same proposer
    proposer_slashing = get_valid_proposer_slashing(spec, state,
                                                    slashed_index=proposer_index,
                                                    signed_1=True, signed_2=True)

    yield from run_proposer_slashing_processing(spec, state, proposer_slashing)
示例#14
0
def test_double_similar_proposer_slashings_same_block(spec, state):
    slashed_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]

    # Same validator, but different slashable offences in the same block
    proposer_slashing_1 = get_valid_proposer_slashing(spec, state, random_root=b'\xaa' * 32,
                                                      slashed_index=slashed_index,
                                                      signed_1=True, signed_2=True)
    proposer_slashing_2 = get_valid_proposer_slashing(spec, state, random_root=b'\xbb' * 32,
                                                      slashed_index=slashed_index,
                                                      signed_1=True, signed_2=True)
    assert not state.validators[slashed_index].slashed

    yield 'pre', state

    block = build_empty_block_for_next_slot(spec, state)
    block.body.proposer_slashings = [proposer_slashing_1, proposer_slashing_2]
    signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True)

    yield 'blocks', [signed_block]
    yield 'post', None
示例#15
0
def test_headers_are_same_sigs_are_different(spec, state):
    proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=False)

    # set headers to be the same
    proposer_slashing.signed_header_2 = proposer_slashing.signed_header_1.copy()
    # but signatures to be different
    proposer_slashing.signed_header_2.signature = proposer_slashing.signed_header_2.signature[:-1] + b'\x00'

    assert proposer_slashing.signed_header_1.signature != proposer_slashing.signed_header_2.signature

    yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False)
示例#16
0
def test_proposer_is_withdrawn(spec, state):
    proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=True)

    # move 1 epoch into future, to allow for past withdrawable epoch
    next_epoch(spec, state)
    # set proposer withdrawable_epoch in past
    current_epoch = spec.get_current_epoch(state)
    proposer_index = proposer_slashing.signed_header_1.message.proposer_index
    state.validators[proposer_index].withdrawable_epoch = current_epoch - 1

    yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False)
示例#17
0
def test_invalid_different_proposer_indices(spec, state):
    proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=True)
    # set different index and sign
    header_1 = proposer_slashing.signed_header_1.message
    header_2 = proposer_slashing.signed_header_2.message
    active_indices = spec.get_active_validator_indices(state, spec.get_current_epoch(state))
    active_indices = [i for i in active_indices if i != header_1.proposer_index]

    header_2.proposer_index = active_indices[0]
    proposer_slashing.signed_header_2 = sign_block_header(spec, state, header_2, privkeys[header_2.proposer_index])

    yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False)
def test_proposer_is_not_activated(state):
    proposer_slashing = get_valid_proposer_slashing(state,
                                                    signed_1=True,
                                                    signed_2=True)

    # set proposer to be not active yet
    state.validator_registry[
        proposer_slashing.
        proposer_index].activation_epoch = get_current_epoch(state) + 1

    yield from run_proposer_slashing_processing(state, proposer_slashing,
                                                False)
def test_invalid_proposer_index(spec, state):
    proposer_slashing = get_valid_proposer_slashing(spec,
                                                    state,
                                                    signed_1=True,
                                                    signed_2=True)
    # Index just too high (by 1)
    proposer_slashing.signed_header_1.message.proposer_index = len(
        state.validators)
    proposer_slashing.signed_header_2.message.proposer_index = len(
        state.validators)

    yield from run_proposer_slashing_processing(spec, state, proposer_slashing,
                                                False)
示例#20
0
def test_double_same_proposer_slashings_same_block(spec, state):
    proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=True)
    slashed_index = proposer_slashing.signed_header_1.message.proposer_index
    assert not state.validators[slashed_index].slashed

    yield 'pre', state

    block = build_empty_block_for_next_slot(spec, state)
    block.body.proposer_slashings = [proposer_slashing, proposer_slashing]
    signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True)

    yield 'blocks', [signed_block]
    yield 'post', None
def get_random_proposer_slashings(spec, state, rng):
    num_slashings = rng.randrange(spec.MAX_PROPOSER_SLASHINGS)
    indices = spec.get_active_validator_indices(
        state, spec.get_current_epoch(state)).copy()
    slashings = [
        get_valid_proposer_slashing(
            spec,
            state,
            slashed_index=indices.pop(rng.randrange(len(indices))),
            signed_1=True,
            signed_2=True,
        ) for _ in range(num_slashings)
    ]
    return slashings
def test_proposer_is_withdrawn(state):
    proposer_slashing = get_valid_proposer_slashing(state,
                                                    signed_1=True,
                                                    signed_2=True)

    # move 1 epoch into future, to allow for past withdrawable epoch
    state.slot += spec.SLOTS_PER_EPOCH
    # set proposer withdrawable_epoch in past
    current_epoch = get_current_epoch(state)
    proposer_index = proposer_slashing.proposer_index
    state.validator_registry[
        proposer_index].withdrawable_epoch = current_epoch - 1

    yield from run_proposer_slashing_processing(state, proposer_slashing,
                                                False)
示例#23
0
def test_proposer_self_slashing(spec, state):
    yield 'pre', state

    block = build_empty_block_for_next_slot(spec, state)
    assert not state.validators[block.proposer_index].slashed

    proposer_slashing = get_valid_proposer_slashing(
        spec, state, slashed_index=block.proposer_index, signed_1=True, signed_2=True)
    block.body.proposer_slashings.append(proposer_slashing)

    # The header is processed *before* the block body:
    # the proposer was not slashed before the body, thus the block is valid.
    signed_block = state_transition_and_sign_block(spec, state, block)
    # The proposer slashed themselves.
    assert state.validators[block.proposer_index].slashed

    yield 'blocks', [signed_block]
    yield 'post', state
示例#24
0
def run_transition_with_operation(state, fork_epoch, spec, post_spec, pre_tag,
                                  post_tag, operation_type, operation_at_slot):
    """
    Generate `operation_type` operation with the spec before fork.
    The operation would be included into the block at `operation_at_slot`.
    """
    is_at_fork = operation_at_slot == fork_epoch * spec.SLOTS_PER_EPOCH
    is_right_before_fork = operation_at_slot == fork_epoch * spec.SLOTS_PER_EPOCH - 1
    assert is_at_fork or is_right_before_fork

    if is_at_fork:
        transition_until_fork(spec, state, fork_epoch)
    elif is_right_before_fork:
        _transition_until_fork_minus_one(spec, state, fork_epoch)

    is_slashing_operation = operation_type in (OperationType.PROPOSER_SLASHING,
                                               OperationType.ATTESTER_SLASHING)
    # prepare operation
    selected_validator_index = None
    if is_slashing_operation:
        # avoid slashing the next proposer
        future_state = state.copy()
        next_slot(spec, future_state)
        proposer_index = spec.get_beacon_proposer_index(future_state)
        selected_validator_index = (proposer_index + 1) % len(state.validators)
        if operation_type == OperationType.PROPOSER_SLASHING:
            proposer_slashing = get_valid_proposer_slashing(
                spec,
                state,
                slashed_index=selected_validator_index,
                signed_1=True,
                signed_2=True)
            operation_dict = {'proposer_slashings': [proposer_slashing]}
        else:
            # operation_type == OperationType.ATTESTER_SLASHING:
            attester_slashing = get_valid_attester_slashing_by_indices(
                spec,
                state,
                [selected_validator_index],
                signed_1=True,
                signed_2=True,
            )
            operation_dict = {'attester_slashings': [attester_slashing]}
    elif operation_type == OperationType.DEPOSIT:
        # create a new deposit
        selected_validator_index = len(state.validators)
        amount = spec.MAX_EFFECTIVE_BALANCE
        deposit = prepare_state_and_deposit(spec,
                                            state,
                                            selected_validator_index,
                                            amount,
                                            signed=True)
        operation_dict = {'deposits': [deposit]}
    elif operation_type == OperationType.VOLUNTARY_EXIT:
        selected_validator_index = 0
        signed_exits = prepare_signed_exits(spec, state,
                                            [selected_validator_index])
        operation_dict = {'voluntary_exits': signed_exits}

    def _check_state():
        if operation_type == OperationType.PROPOSER_SLASHING:
            slashed_proposer = state.validators[
                proposer_slashing.signed_header_1.message.proposer_index]
            assert slashed_proposer.slashed
        elif operation_type == OperationType.ATTESTER_SLASHING:
            indices = set(
                attester_slashing.attestation_1.attesting_indices
            ).intersection(attester_slashing.attestation_2.attesting_indices)
            assert selected_validator_index in indices
            assert len(indices) > 0
            for validator_index in indices:
                assert state.validators[validator_index].slashed
        elif operation_type == OperationType.DEPOSIT:
            assert not post_spec.is_active_validator(
                state.validators[selected_validator_index],
                post_spec.get_current_epoch(state))
        elif operation_type == OperationType.VOLUNTARY_EXIT:
            validator = state.validators[selected_validator_index]
            assert validator.exit_epoch < post_spec.FAR_FUTURE_EPOCH

    yield "pre", state

    blocks = []

    if is_right_before_fork:
        # add a block with operation.
        block = build_empty_block_for_next_slot(spec, state)
        _set_operations_by_dict(block, operation_dict)
        signed_block = state_transition_and_sign_block(spec, state, block)
        blocks.append(pre_tag(signed_block))

        _check_state()

    # irregular state transition to handle fork:
    _operation_at_slot = operation_dict if is_at_fork else None
    state, block = do_fork(state,
                           spec,
                           post_spec,
                           fork_epoch,
                           operation_dict=_operation_at_slot)
    blocks.append(post_tag(block))

    if is_at_fork:
        _check_state()

    # after the fork
    if operation_type == OperationType.DEPOSIT:
        state = _transition_until_active(post_spec, state, post_tag, blocks,
                                         selected_validator_index)
    else:
        # avoid using the slashed validators as block proposers
        ignoring_proposers = [selected_validator_index
                              ] if is_slashing_operation else None

        # continue regular state transition with new spec into next epoch
        transition_to_next_epoch_and_append_blocks(
            post_spec,
            state,
            post_tag,
            blocks,
            only_last_block=True,
            ignoring_proposers=ignoring_proposers,
        )

    yield "blocks", blocks
    yield "post", state
示例#25
0
def test_invalid_sig_1_and_2(spec, state):
    proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=False, signed_2=False)
    yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False)
示例#26
0
def test_success_slashed_and_proposer_index_the_same(spec, state):
    proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=True)

    yield from run_proposer_slashing_processing(spec, state, proposer_slashing)
def test_success(state):
    proposer_slashing = get_valid_proposer_slashing(state,
                                                    signed_1=True,
                                                    signed_2=True)

    yield from run_proposer_slashing_processing(state, proposer_slashing)