예제 #1
0
def test_proposer_slashing(state):
    # copy for later balance lookups.
    pre_state = deepcopy(state)
    proposer_slashing = get_valid_proposer_slashing(state, signed_1=True, signed_2=True)
    validator_index = proposer_slashing.proposer_index

    assert not state.validator_registry[validator_index].slashed

    yield 'pre', state

    #
    # Add to state via block transition
    #
    block = build_empty_block_for_next_slot(state)
    block.body.proposer_slashings.append(proposer_slashing)
    sign_block(state, block)
    yield 'blocks', [block], [spec.BeaconBlock]

    state_transition(state, block)
    yield 'post', state

    # check if slashed
    slashed_validator = state.validator_registry[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)
예제 #2
0
def test_transfer(state):
    # overwrite default 0 to test
    spec.MAX_TRANSFERS = 1

    sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
    amount = get_balance(state, sender_index)

    transfer = get_valid_transfer(state, state.slot + 1, sender_index, amount, signed=True)
    recipient_index = transfer.recipient
    pre_transfer_recipient_balance = get_balance(state, recipient_index)

    # un-activate so validator can transfer
    state.validator_registry[sender_index].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH

    yield 'pre', state

    # Add to state via block transition
    block = build_empty_block_for_next_slot(state)
    block.body.transfers.append(transfer)
    sign_block(state, block)

    yield 'blocks', [block], [spec.BeaconBlock]

    state_transition(state, block)
    yield 'post', state

    sender_balance = get_balance(state, sender_index)
    recipient_balance = get_balance(state, recipient_index)
    assert sender_balance == 0
    assert recipient_balance == pre_transfer_recipient_balance + amount
예제 #3
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``
    """

    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

    proposer_index = proposer_slashing.signed_header_1.message.proposer_index
    pre_proposer_balance = get_balance(state, proposer_index)

    spec.process_proposer_slashing(state, proposer_slashing)
    yield 'post', state

    # check if slashed
    slashed_validator = state.validators[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_index) < pre_proposer_balance
예제 #4
0
def test_attester_slashing(spec, state):
    # copy for later balance lookups.
    pre_state = deepcopy(state)

    attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True)
    validator_index = get_indexed_attestation_participants(spec, attester_slashing.attestation_1)[0]

    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.attester_slashings.append(attester_slashing)

    signed_block = state_transition_and_sign_block(spec, state, block)

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

    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)

    proposer_index = spec.get_beacon_proposer_index(state)
    # gained whistleblower reward
    assert (
        get_balance(state, proposer_index) >
        get_balance(pre_state, proposer_index)
    )
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
예제 #6
0
def run_custody_response_processing(spec, state, custody_response, valid=True):
    """
    Run ``process_bit_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

    # TODO: Add capability to also process chunk challenges, not only bit challenges
    challenge = state.custody_bit_challenge_records[custody_response.challenge_index]
    pre_slashed_balance = get_balance(state, challenge.challenger_index)

    spec.process_custody_response(state, custody_response)

    slashed_validator = state.validators[challenge.challenger_index]

    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, challenge.challenger_index) < pre_slashed_balance
    yield 'post', state
def run_custody_slashing_processing(spec, state, custody_slashing, valid=True, correct=True):
    """
    Run ``process_bit_challenge``, yielding:
      - pre-state ('pre')
      - CustodySlashing ('custody_slashing')
      - post-state ('post').
    If ``valid == False``, run expecting ``AssertionError``
    """
    yield 'pre', state
    yield 'custody_slashing', custody_slashing

    if not valid:
        expect_assertion_error(lambda: spec.process_custody_slashing(state, custody_slashing))
        yield 'post', None
        return

    if correct:
        pre_slashed_balance = get_balance(state, custody_slashing.message.malefactor_index)
    else:
        pre_slashed_balance = get_balance(state, custody_slashing.message.whistleblower_index)

    spec.process_custody_slashing(state, custody_slashing)

    if correct:
        slashed_validator = state.validators[custody_slashing.message.malefactor_index]
        assert get_balance(state, custody_slashing.message.malefactor_index) < pre_slashed_balance
    else:
        slashed_validator = state.validators[custody_slashing.message.whistleblower_index]
        assert get_balance(state, custody_slashing.message.whistleblower_index) < pre_slashed_balance

    assert slashed_validator.slashed
    assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
    assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH

    yield 'post', state
예제 #8
0
def run_deposit_processing(spec,
                           state,
                           deposit,
                           validator_index,
                           valid=True,
                           effective=True):
    """
    Run ``process_deposit``, yielding:
      - pre-state ('pre')
      - deposit ('deposit')
      - post-state ('post').
    If ``valid == False``, run expecting ``AssertionError``
    """
    pre_validator_count = len(state.validators)
    pre_balance = 0
    if validator_index < pre_validator_count:
        pre_balance = get_balance(state, validator_index)

    yield 'pre', state
    yield 'deposit', deposit

    if not valid:
        expect_assertion_error(lambda: spec.process_deposit(state, deposit))
        yield 'post', None
        return

    spec.process_deposit(state, deposit)

    yield 'post', state

    if not effective:
        assert len(state.validators) == pre_validator_count
        assert len(state.balances) == pre_validator_count
        if validator_index < pre_validator_count:
            assert get_balance(state, validator_index) == pre_balance
    else:
        if validator_index < pre_validator_count:
            # top-up
            assert len(state.validators) == pre_validator_count
            assert len(state.balances) == pre_validator_count
        else:
            # new validator
            assert len(state.validators) == pre_validator_count + 1
            assert len(state.balances) == pre_validator_count + 1
        assert get_balance(
            state, validator_index) == pre_balance + deposit.data.amount

        effective = min(spec.MAX_EFFECTIVE_BALANCE,
                        pre_balance + deposit.data.amount)
        effective -= effective % spec.EFFECTIVE_BALANCE_INCREMENT
        assert state.validators[validator_index].effective_balance == effective

    assert state.eth1_deposit_index == state.eth1_data.deposit_count
예제 #9
0
def check_attester_slashing_effect(spec, pre_state, state, slashed_indices):
    for slashed_index in slashed_indices:
        slashed_validator = state.validators[slashed_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, slashed_index) < get_balance(pre_state, slashed_index)

    proposer_index = spec.get_beacon_proposer_index(state)
    # gained whistleblower reward
    assert get_balance(state, proposer_index) > get_balance(pre_state, proposer_index)
def run_deposit_processing(state,
                           deposit,
                           validator_index,
                           valid=True,
                           effective=True):
    """
    Run ``process_deposit``, yielding:
      - pre-state ('pre')
      - deposit ('deposit')
      - post-state ('post').
    If ``valid == False``, run expecting ``AssertionError``
    """
    pre_validator_count = len(state.validator_registry)
    pre_balance = 0
    if validator_index < pre_validator_count:
        pre_balance = get_balance(state, validator_index)
    else:
        # if it is a new validator, it should be right at the end of the current registry.
        assert validator_index == pre_validator_count

    yield 'pre', state
    yield 'deposit', deposit

    if not valid:
        expect_assertion_error(lambda: process_deposit(state, deposit))
        yield 'post', None
        return

    process_deposit(state, deposit)

    yield 'post', state

    if not effective:
        assert len(state.validator_registry) == pre_validator_count
        assert len(state.balances) == pre_validator_count
        if validator_index < pre_validator_count:
            assert get_balance(state, validator_index) == pre_balance
    else:
        if validator_index < pre_validator_count:
            # top-up
            assert len(state.validator_registry) == pre_validator_count
            assert len(state.balances) == pre_validator_count
        else:
            # new validator
            assert len(state.validator_registry) == pre_validator_count + 1
            assert len(state.balances) == pre_validator_count + 1
        assert get_balance(
            state, validator_index) == pre_balance + deposit.data.amount

    assert state.deposit_index == state.latest_eth1_data.deposit_count
예제 #11
0
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_index = attester_slashing.attestation_1.custody_bit_0_indices[0]
    pre_slashed_balance = get_balance(state, slashed_index)

    proposer_index = spec.get_beacon_proposer_index(state)
    pre_proposer_balance = get_balance(state, proposer_index)

    # Process slashing
    spec.process_attester_slashing(state, attester_slashing)

    slashed_validator = state.validator_registry[slashed_index]

    # Check slashing
    assert slashed_validator.slashed
    assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
    assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH

    if slashed_index != proposer_index:
        # lost whistleblower reward
        assert get_balance(state, slashed_index) < pre_slashed_balance
        # gained whistleblower reward
        assert get_balance(state, proposer_index) > pre_proposer_balance
    else:
        # gained rewards for all slashings, which may include others. And only lost that of themselves.
        # Netto at least 0, if more people where slashed, a balance increase.
        assert get_balance(state, slashed_index) >= pre_slashed_balance

    yield 'post', state
예제 #12
0
def test_deposit_in_block(spec, state):
    initial_registry_len = len(state.validators)
    initial_balances_len = len(state.balances)

    validator_index = len(state.validators)
    amount = spec.MAX_EFFECTIVE_BALANCE
    deposit = prepare_state_and_deposit(spec,
                                        state,
                                        validator_index,
                                        amount,
                                        signed=True)

    yield 'pre', state

    block = build_empty_block_for_next_slot(spec, state)
    block.body.deposits.append(deposit)
    signed_block = state_transition_and_sign_block(spec, state, block)

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

    assert len(state.validators) == initial_registry_len + 1
    assert len(state.balances) == initial_balances_len + 1
    assert get_balance(state, validator_index) == spec.MAX_EFFECTIVE_BALANCE
    assert state.validators[validator_index].pubkey == pubkeys[validator_index]
예제 #13
0
def test_deposit_top_up(spec, state):
    validator_index = 0
    amount = spec.MAX_EFFECTIVE_BALANCE // 4
    deposit = prepare_state_and_deposit(spec, state, validator_index, amount)

    initial_registry_len = len(state.validators)
    initial_balances_len = len(state.balances)
    validator_pre_balance = get_balance(state, validator_index)

    pre_state = state.copy()
    yield 'pre', pre_state

    block = build_empty_block_for_next_slot(spec, state)
    block.body.deposits.append(deposit)

    signed_block = state_transition_and_sign_block(spec, state, block)

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

    assert len(state.validators) == initial_registry_len
    assert len(state.balances) == initial_balances_len

    # Altair introduces sync committee (sm) reward and penalty
    sync_committee_reward = sync_committee_penalty = 0
    if is_post_altair(spec):
        committee_indices = compute_committee_indices(
            spec, state, state.current_sync_committee)
        committee_bits = block.body.sync_aggregate.sync_committee_bits
        sync_committee_reward, sync_committee_penalty = compute_sync_committee_participant_reward_and_penalty(
            spec,
            pre_state,
            validator_index,
            committee_indices,
            committee_bits,
        )

    assert get_balance(state,
                       validator_index) == (validator_pre_balance + amount +
                                            sync_committee_reward -
                                            sync_committee_penalty)
예제 #14
0
def get_valid_transfer(spec,
                       state,
                       slot=None,
                       sender_index=None,
                       recipient_index=None,
                       amount=None,
                       fee=None,
                       signed=False):
    if slot is None:
        slot = state.slot
    current_epoch = spec.get_current_epoch(state)
    if sender_index is None:
        sender_index = spec.get_active_validator_indices(state,
                                                         current_epoch)[-1]
    if recipient_index is None:
        recipient_index = spec.get_active_validator_indices(
            state, current_epoch)[0]
    transfer_pubkey = pubkeys[-1]
    transfer_privkey = privkeys[-1]

    if fee is None:
        fee = get_balance(state, sender_index) // 32
    if amount is None:
        amount = get_balance(state, sender_index) - fee

    transfer = spec.Transfer(
        sender=sender_index,
        recipient=recipient_index,
        amount=amount,
        fee=fee,
        slot=slot,
        pubkey=transfer_pubkey,
    )
    if signed:
        sign_transfer(spec, state, transfer, transfer_privkey)

    # ensure withdrawal_credentials reproducible
    state.validators[transfer.sender].withdrawal_credentials = (
        spec.BLS_WITHDRAWAL_PREFIX + spec.hash(transfer.pubkey)[1:])

    return transfer
예제 #15
0
def check_proposer_slashing_effect(spec, pre_state, state, slashed_index):
    slashed_validator = state.validators[slashed_index]
    assert slashed_validator.slashed
    assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
    assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH

    proposer_index = spec.get_beacon_proposer_index(state)
    slash_penalty = state.validators[
        slashed_index].effective_balance // get_min_slashing_penalty_quotient(
            spec)
    whistleblower_reward = state.validators[
        slashed_index].effective_balance // spec.WHISTLEBLOWER_REWARD_QUOTIENT
    if proposer_index != slashed_index:
        # slashed validator lost initial slash penalty
        assert (get_balance(
            state, slashed_index) == get_balance(pre_state, slashed_index) -
                slash_penalty)
        # block proposer gained whistleblower reward
        # >= because proposer could have reported multiple
        assert (get_balance(state, proposer_index) >=
                get_balance(pre_state, proposer_index) + whistleblower_reward)
    else:
        # proposer reported themself so get penalty and reward
        # >= because proposer could have reported multiple
        assert (get_balance(
            state, slashed_index) >= get_balance(pre_state, slashed_index) -
                slash_penalty + whistleblower_reward)
예제 #16
0
def test_deposit_top_up(spec, state):
    validator_index = 0
    amount = spec.MAX_EFFECTIVE_BALANCE // 4
    deposit = prepare_state_and_deposit(spec, state, validator_index, amount)

    initial_registry_len = len(state.validators)
    initial_balances_len = len(state.balances)
    validator_pre_balance = get_balance(state, validator_index)

    yield 'pre', state

    block = build_empty_block_for_next_slot(spec, state)
    block.body.deposits.append(deposit)

    signed_block = state_transition_and_sign_block(spec, state, block)

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

    assert len(state.validators) == initial_registry_len
    assert len(state.balances) == initial_balances_len
    assert get_balance(state, validator_index) == validator_pre_balance + amount
예제 #17
0
def check_proposer_slashing_effect(spec,
                                   pre_state,
                                   state,
                                   slashed_index,
                                   block=None):
    slashed_validator = state.validators[slashed_index]
    assert slashed_validator.slashed
    assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
    assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH

    proposer_index = spec.get_beacon_proposer_index(state)
    slash_penalty = state.validators[
        slashed_index].effective_balance // get_min_slashing_penalty_quotient(
            spec)
    whistleblower_reward = state.validators[
        slashed_index].effective_balance // spec.WHISTLEBLOWER_REWARD_QUOTIENT

    # Altair introduces sync committee (SC) reward and penalty
    sc_reward_for_slashed = sc_penalty_for_slashed = sc_reward_for_proposer = sc_penalty_for_proposer = 0
    if is_post_altair(spec) and block is not None:
        committee_indices = compute_committee_indices(
            spec, state, state.current_sync_committee)
        committee_bits = block.body.sync_aggregate.sync_committee_bits
        sc_reward_for_slashed, sc_penalty_for_slashed = compute_sync_committee_participant_reward_and_penalty(
            spec,
            pre_state,
            slashed_index,
            committee_indices,
            committee_bits,
        )
        sc_reward_for_proposer, sc_penalty_for_proposer = compute_sync_committee_participant_reward_and_penalty(
            spec,
            pre_state,
            proposer_index,
            committee_indices,
            committee_bits,
        )

    if proposer_index != slashed_index:
        # slashed validator lost initial slash penalty
        assert (get_balance(
            state, slashed_index) == get_balance(pre_state, slashed_index) -
                slash_penalty + sc_reward_for_slashed - sc_penalty_for_slashed)
        # block proposer gained whistleblower reward
        # >= because proposer could have reported multiple
        assert (
            get_balance(state, proposer_index) >=
            (get_balance(pre_state, proposer_index) + whistleblower_reward +
             sc_reward_for_proposer - sc_penalty_for_proposer))
    else:
        # proposer reported themself so get penalty and reward
        # >= because proposer could have reported multiple
        assert (get_balance(state, slashed_index) >=
                (get_balance(pre_state, slashed_index) - slash_penalty +
                 whistleblower_reward + sc_reward_for_slashed -
                 sc_penalty_for_slashed))
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