Exemplo n.º 1
0
def process_attester_slashings(state: BeaconState, block: BaseBeaconBlock,
                               config: Eth2Config) -> BeaconState:
    if len(block.body.attester_slashings) > config.MAX_ATTESTER_SLASHINGS:
        raise ValidationError(
            f"The block ({block}) has too many attester slashings:\n"
            f"\tFound {len(block.body.attester_slashings)} attester slashings, "
            f"maximum: {config.MAX_ATTESTER_SLASHINGS}")

    current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH)

    for attester_slashing in block.body.attester_slashings:
        validate_attester_slashing(
            state,
            attester_slashing,
            config.MAX_VALIDATORS_PER_COMMITTEE,
            config.SLOTS_PER_EPOCH,
        )

        slashed_any = False
        attestation_1 = attester_slashing.attestation_1
        attestation_2 = attester_slashing.attestation_2
        sorted_attesting_indices = sorted(
            set(attestation_1.attesting_indices).intersection(
                attestation_2.attesting_indices))
        for index in sorted_attesting_indices:
            validator = state.validators[index]
            if validator.is_slashable(current_epoch):
                state = slash_validator(state, index, config)
                slashed_any = True
        validate_some_slashing(slashed_any, attester_slashing)

    return state
Exemplo n.º 2
0
def process_attester_slashings(state: BeaconState,
                               block: BaseBeaconBlock,
                               config: Eth2Config) -> BeaconState:
    if len(block.body.attester_slashings) > config.MAX_ATTESTER_SLASHINGS:
        raise ValidationError(
            f"The block ({block}) has too many attester slashings:\n"
            f"\tFound {len(block.body.attester_slashings)} attester slashings, "
            f"maximum: {config.MAX_ATTESTER_SLASHINGS}"
        )

    for attester_slashing in block.body.attester_slashings:
        validate_attester_slashing(
            state,
            attester_slashing,
            config.MAX_INDICES_PER_SLASHABLE_VOTE,
            config.SLOTS_PER_EPOCH,
        )

        slashable_indices = _get_slashable_indices(state, config, attester_slashing)

        validate_slashable_indices(slashable_indices)
        for index in slashable_indices:
            state = slash_validator(
                state=state,
                index=index,
                latest_slashed_exit_length=config.LATEST_SLASHED_EXIT_LENGTH,
                whistleblower_reward_quotient=config.WHISTLEBLOWER_REWARD_QUOTIENT,
                max_deposit_amount=config.MAX_DEPOSIT_AMOUNT,
                committee_config=CommitteeConfig(config),
            )

    return state
Exemplo n.º 3
0
def test_slash_validator(monkeypatch, num_validators, committee,
                         n_validators_state, genesis_epoch, slots_per_epoch,
                         latest_slashed_exit_length,
                         whistleblower_reward_quotient, activation_exit_delay,
                         max_deposit_amount, target_committee_size,
                         shard_count, committee_config):
    from eth2.beacon import committee_helpers

    def mock_get_crosslink_committees_at_slot(state,
                                              slot,
                                              committee_config,
                                              registry_change=False):
        return ((
            committee,
            1,
        ), )

    monkeypatch.setattr(committee_helpers, 'get_crosslink_committees_at_slot',
                        mock_get_crosslink_committees_at_slot)

    state = n_validators_state
    index = 1

    result_state = slash_validator(
        state=state,
        index=index,
        latest_slashed_exit_length=latest_slashed_exit_length,
        whistleblower_reward_quotient=whistleblower_reward_quotient,
        max_deposit_amount=max_deposit_amount,
        committee_config=committee_config,
    )

    # Just check if `prepare_validator_for_withdrawal` applied these two functions
    expected_state = exit_validator(state, index, slots_per_epoch,
                                    activation_exit_delay)
    expected_state = _settle_penality_to_validator_and_whistleblower(
        state=expected_state,
        validator_index=index,
        latest_slashed_exit_length=latest_slashed_exit_length,
        whistleblower_reward_quotient=whistleblower_reward_quotient,
        max_deposit_amount=max_deposit_amount,
        committee_config=committee_config,
    )
    current_epoch = state.current_epoch(slots_per_epoch)
    validator = state.validator_registry[index].copy(
        slashed=False,
        withdrawable_epoch=current_epoch + latest_slashed_exit_length,
    )
    expected_state.update_validator_registry(index, validator)

    assert result_state == expected_state
Exemplo n.º 4
0
def process_proposer_slashings(
    state: BeaconState, block: BaseBeaconBlock, config: Eth2Config
) -> BeaconState:
    if len(block.body.proposer_slashings) > config.MAX_PROPOSER_SLASHINGS:
        raise ValidationError(
            f"The block ({block}) has too many proposer slashings:\n"
            f"\tFound {len(block.body.proposer_slashings)} proposer slashings, "
            f"maximum: {config.MAX_PROPOSER_SLASHINGS}"
        )

    for proposer_slashing in block.body.proposer_slashings:
        validate_proposer_slashing(state, proposer_slashing, config.SLOTS_PER_EPOCH)

        state = slash_validator(state, proposer_slashing.proposer_index, config)

    return state
def process_proposer_slashings(state: BeaconState, block: BaseBeaconBlock,
                               config: BeaconConfig) -> BeaconState:
    if len(block.body.proposer_slashings) > config.MAX_PROPOSER_SLASHINGS:
        raise ValidationError(
            f"The block ({block}) has too many proposer slashings:\n"
            f"\tFound {len(block.body.proposer_slashings)} proposer slashings, "
            f"maximum: {config.MAX_PROPOSER_SLASHINGS}")

    for proposer_slashing in block.body.proposer_slashings:
        validate_proposer_slashing(state, proposer_slashing,
                                   config.SLOTS_PER_EPOCH)

        state = slash_validator(
            state=state,
            index=proposer_slashing.proposer_index,
            latest_slashed_exit_length=config.LATEST_SLASHED_EXIT_LENGTH,
            whistleblower_reward_quotient=config.WHISTLEBLOWER_REWARD_QUOTIENT,
            max_deposit_amount=config.MAX_DEPOSIT_AMOUNT,
            committee_config=CommitteeConfig(config),
        )

    return state
Exemplo n.º 6
0
def test_slash_validator(genesis_state, config):
    some_epoch = (config.GENESIS_EPOCH + random.randrange(1, 2**32) +
                  config.EPOCHS_PER_SLASHINGS_VECTOR)
    earliest_slashable_epoch = some_epoch - config.EPOCHS_PER_SLASHINGS_VECTOR
    slashable_range = range(earliest_slashable_epoch, some_epoch)
    sampling_quotient = 4

    state = genesis_state.copy(slot=compute_start_slot_at_epoch(
        earliest_slashable_epoch, config.SLOTS_PER_EPOCH))
    validator_count_to_slash = len(state.validators) // sampling_quotient
    assert validator_count_to_slash > 1
    validator_indices_to_slash = random.sample(range(len(state.validators)),
                                               validator_count_to_slash)
    # ensure case w/ one slashing in an epoch
    # by ignoring the first
    set_of_colluding_validators = validator_indices_to_slash[1:]
    # simulate multiple slashings in an epoch
    validators_grouped_by_coalition = groupby(
        lambda index: index % sampling_quotient, set_of_colluding_validators)
    coalition_count = len(validators_grouped_by_coalition)
    slashings = {
        epoch: coalition
        for coalition, epoch in zip(
            validators_grouped_by_coalition.values(),
            random.sample(slashable_range, coalition_count),
        )
    }
    another_slashing_epoch = first(random.sample(slashable_range, 1))
    while another_slashing_epoch in slashings:
        another_slashing_epoch += 1
    slashings[another_slashing_epoch] = (validator_indices_to_slash[0], )

    expected_slashings = {}
    expected_individual_penalties = {}
    for epoch, coalition in slashings.items():
        for index in coalition:
            validator = state.validators[index]
            assert validator.is_active(earliest_slashable_epoch)
            assert validator.exit_epoch == FAR_FUTURE_EPOCH
            assert validator.withdrawable_epoch == FAR_FUTURE_EPOCH

            expected_slashings = update_in(
                expected_slashings,
                [epoch],
                lambda balance: balance + state.validators[index].
                effective_balance,
                default=0,
            )
            expected_individual_penalties = update_in(
                expected_individual_penalties,
                [index],
                lambda penalty:
                (penalty + (state.validators[index].effective_balance // config
                            .MIN_SLASHING_PENALTY_QUOTIENT)),
                default=0,
            )

    # emulate slashings across the current slashable range
    expected_proposer_rewards = {}
    for epoch, coalition in slashings.items():
        state = state.copy(
            slot=compute_start_slot_at_epoch(epoch, config.SLOTS_PER_EPOCH))

        expected_total_slashed_balance = expected_slashings[epoch]
        proposer_index = get_beacon_proposer_index(state,
                                                   CommitteeConfig(config))

        expected_proposer_rewards = update_in(
            expected_proposer_rewards,
            [proposer_index],
            lambda reward: reward + (expected_total_slashed_balance // config.
                                     WHISTLEBLOWER_REWARD_QUOTIENT),
            default=0,
        )
        for index in coalition:
            state = slash_validator(state, index, config)

    state = state.copy(
        slot=compute_start_slot_at_epoch(some_epoch, config.SLOTS_PER_EPOCH))
    # verify result
    for epoch, coalition in slashings.items():
        for index in coalition:
            validator = state.validators[index]
            assert validator.exit_epoch != FAR_FUTURE_EPOCH
            assert validator.slashed
            assert validator.withdrawable_epoch == max(
                validator.exit_epoch +
                config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY,
                epoch + config.EPOCHS_PER_SLASHINGS_VECTOR,
            )

            slashed_epoch_index = epoch % config.EPOCHS_PER_SLASHINGS_VECTOR
            slashed_balance = state.slashings[slashed_epoch_index]
            assert slashed_balance == expected_slashings[epoch]
            assert state.balances[index] == (
                config.MAX_EFFECTIVE_BALANCE -
                expected_individual_penalties[index] +
                expected_proposer_rewards.get(index, 0))

    for proposer_index, total_reward in expected_proposer_rewards.items():
        assert state.balances[proposer_index] == (
            total_reward + config.MAX_EFFECTIVE_BALANCE -
            expected_individual_penalties.get(proposer_index, 0))