Beispiel #1
0
def get_attestation_deltas(state: BeaconState,
                           config: Eth2Config) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
    committee_config = CommitteeConfig(config)
    rewards = tuple(
        0 for _ in range(len(state.validators))
    )
    penalties = tuple(
        0 for _ in range(len(state.validators))
    )
    previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH)
    total_balance = get_total_active_balance(state, config)
    eligible_validator_indices = tuple(
        ValidatorIndex(index) for index, v in enumerate(state.validators)
        if v.is_active(previous_epoch) or (
            v.slashed and previous_epoch + 1 < v.withdrawable_epoch
        )
    )

    matching_source_attestations = get_matching_source_attestations(
        state,
        previous_epoch,
        config,
    )
    matching_target_attestations = get_matching_target_attestations(
        state,
        previous_epoch,
        config,
    )
    matching_head_attestations = get_matching_head_attestations(
        state,
        previous_epoch,
        config,
    )

    for attestations in (
            matching_source_attestations,
            matching_target_attestations,
            matching_head_attestations
    ):
        unslashed_attesting_indices = get_unslashed_attesting_indices(
            state,
            attestations,
            committee_config,
        )
        attesting_balance = get_total_balance(state, unslashed_attesting_indices)
        for index in eligible_validator_indices:
            if index in unslashed_attesting_indices:
                rewards = update_tuple_item_with_fn(
                    rewards,
                    index,
                    lambda balance, delta: balance + delta,
                    get_base_reward(
                        state,
                        index,
                        config,
                    ) * attesting_balance // total_balance,
                )
            else:
                penalties = update_tuple_item_with_fn(
                    penalties,
                    index,
                    lambda balance, delta: balance + delta,
                    get_base_reward(
                        state,
                        index,
                        config,
                    ),
                )

    for index in get_unslashed_attesting_indices(
            state,
            matching_source_attestations,
            committee_config,
    ):
        attestation = min(
            (
                a for a in matching_source_attestations
                if index in get_attesting_indices(
                    state,
                    a.data,
                    a.aggregation_bitfield,
                    committee_config,
                )
            ),
            key=lambda a: a.inclusion_delay,
        )
        base_reward = get_base_reward(state, index, config)
        proposer_reward = base_reward // config.PROPOSER_REWARD_QUOTIENT
        rewards = update_tuple_item_with_fn(
            rewards,
            attestation.proposer_index,
            lambda balance, delta: balance + delta,
            proposer_reward,
        )
        max_attester_reward = base_reward - proposer_reward
        rewards = update_tuple_item_with_fn(
            rewards,
            index,
            lambda balance, delta: balance + delta,
            (
                max_attester_reward *
                config.MIN_ATTESTATION_INCLUSION_DELAY //
                attestation.inclusion_delay
            )
        )

    finality_delay = previous_epoch - state.finalized_epoch
    if finality_delay > config.MIN_EPOCHS_TO_INACTIVITY_PENALTY:
        matching_target_attesting_indices = get_unslashed_attesting_indices(
            state,
            matching_target_attestations,
            committee_config,
        )
        for index in eligible_validator_indices:
            penalties = update_tuple_item_with_fn(
                penalties,
                index,
                lambda balance, delta: balance + delta,
                BASE_REWARDS_PER_EPOCH * get_base_reward(
                    state,
                    index,
                    config,
                ),
            )
            if index not in matching_target_attesting_indices:
                effective_balance = state.validators[index].effective_balance
                penalties = update_tuple_item_with_fn(
                    penalties,
                    index,
                    lambda balance, delta: balance + delta,
                    effective_balance * finality_delay // config.INACTIVITY_PENALTY_QUOTIENT,
                )
    return tuple(
        Gwei(reward) for reward in rewards
    ), tuple(
        Gwei(penalty) for penalty in penalties
    )
Beispiel #2
0
def _compute_next_start_shard(state: BeaconState, config: Eth2Config) -> Shard:
    current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH)
    return (
        state.start_shard
        + get_shard_delta(state, current_epoch, CommitteeConfig(config))
    ) % config.SHARD_COUNT
Beispiel #3
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))
Beispiel #4
0
def _get_proposer_index(state: BeaconState, config: Eth2Config) -> ValidatorIndex:
    proposer_index = get_beacon_proposer_index(state, CommitteeConfig(config))
    return proposer_index
Beispiel #5
0
    def on_attestation(self,
                       attestation: Attestation,
                       validate_signature: bool = True) -> None:
        target = attestation.data.target
        current_epoch = compute_epoch_at_slot(self.get_current_slot(),
                                              self._config.SLOTS_PER_EPOCH)
        previous_epoch = (current_epoch -
                          1 if current_epoch > self._config.GENESIS_EPOCH else
                          self._config.GENESIS_EPOCH)
        if target.epoch not in (current_epoch, previous_epoch):
            raise ValidationError(
                "Attestations must be from the current or previous epoch")

        if target.root not in self._context.blocks:
            raise ValidationError(
                "Attestation targets a block we have not seen")

        base_state = self._context.block_states[target.root]
        time_of_target_epoch = (
            base_state.genesis_time + compute_start_slot_at_epoch(
                target.epoch, self._config.SLOTS_PER_EPOCH) *
            self._config.SECONDS_PER_SLOT)
        if self._context.time < time_of_target_epoch:
            raise ValidationError("Attestation cannot be for a future epoch")

        beacon_block_root = attestation.data.beacon_block_root
        if beacon_block_root not in self._context.blocks:
            raise ValidationError("Attestations must be for a known block")

        if self._context.blocks[beacon_block_root].slot > attestation.data.slot:
            raise ValidationError(
                "Attestations must not be for a block in the future")

        if target not in self._context.checkpoint_states:
            base_state = process_slots(
                base_state,
                compute_start_slot_at_epoch(target.epoch,
                                            self._config.SLOTS_PER_EPOCH),
                self._config,
            )
            self._context.checkpoint_states[target] = base_state
        target_state = self._context.checkpoint_states[target]

        if (self._context.time <
            (attestation.data.slot + 1) * self._config.SECONDS_PER_SLOT):
            raise ValidationError(
                "Attestations can only affect the fork choice of future slots")

        # TODO: has this validation already been performed?
        indexed_attestation = get_indexed_attestation(
            target_state, attestation, CommitteeConfig(self._config))
        validate_indexed_attestation(
            target_state,
            indexed_attestation,
            self._config.MAX_VALIDATORS_PER_COMMITTEE,
            self._config.SLOTS_PER_EPOCH,
            validate_signature=validate_signature,
        )

        for i in indexed_attestation.attesting_indices:
            if (i not in self._context.latest_messages
                    or target.epoch > self._context.latest_messages[i].epoch):
                self._context.latest_messages[i] = LatestMessage(
                    epoch=target.epoch,
                    root=attestation.data.beacon_block_root)
Beispiel #6
0
    async def handle_first_tick(self, slot: Slot) -> None:
        head = self.chain.get_canonical_head()
        state_machine = self.chain.get_state_machine()
        state = self.chain.get_head_state()
        self.logger.debug(
            bold_green(
                "status at slot %s in epoch %s: state_root %s, finalized_checkpoint %s"
            ),
            state.slot,
            state.current_epoch(self.slots_per_epoch),
            humanize_hash(head.state_root),
            state.finalized_checkpoint,
        )
        self.logger.debug(
            (
                "status at slot %s in epoch %s:"
                " previous_justified_checkpoint %s, current_justified_checkpoint %s"
            ),
            state.slot,
            state.current_epoch(self.slots_per_epoch),
            state.previous_justified_checkpoint,
            state.current_justified_checkpoint,
        )
        self.logger.debug(
            (
                "status at slot %s in epoch %s:"
                " previous_epoch_attestations %s, current_epoch_attestations %s"
            ),
            state.slot,
            state.current_epoch(self.slots_per_epoch),
            state.previous_epoch_attestations,
            state.current_epoch_attestations,
        )

        # To see if a validator is assigned to propose during the slot, the beacon state must
        # be in the epoch in question. At the epoch boundaries, the validator must run an
        # epoch transition into the epoch to successfully check the proposal assignment of the
        # first slot.
        temp_state = state_machine.state_transition.apply_state_transition(
            state,
            future_slot=slot,
        )
        proposer_index = get_beacon_proposer_index(
            temp_state,
            CommitteeConfig(state_machine.config),
        )

        # `latest_proposed_epoch` is used to prevent validator from erraneously proposing twice
        # in the same epoch due to service crashing.
        epoch = compute_epoch_at_slot(slot, self.slots_per_epoch)
        if proposer_index in self.validator_privkeys:
            has_proposed = epoch <= self.latest_proposed_epoch[proposer_index]
            if not has_proposed:
                await self.propose_block(
                    proposer_index=proposer_index,
                    slot=slot,
                    state=state,
                    state_machine=state_machine,
                    head_block=head,
                )
                self.latest_proposed_epoch[proposer_index] = epoch
def test_get_attestation_deltas(
    genesis_state,
    config,
    slots_per_epoch,
    target_committee_size,
    shard_count,
    min_attestation_inclusion_delay,
    inactivity_penalty_quotient,
    finalized_epoch,
    current_slot,
    sample_pending_attestation_record_params,
    sample_attestation_data_params,
):
    state = genesis_state.copy(
        slot=current_slot,
        finalized_checkpoint=Checkpoint(epoch=finalized_epoch))
    previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH,
                                          config.GENESIS_EPOCH)
    epoch_start_shard = get_start_shard(state, previous_epoch,
                                        CommitteeConfig(config))
    shard_delta = get_shard_delta(state, previous_epoch,
                                  CommitteeConfig(config))

    a = epoch_start_shard
    b = epoch_start_shard + shard_delta
    if a > b:
        valid_shards_for_epoch = range(b, a)
    else:
        valid_shards_for_epoch = range(a, b)

    indices_to_check = set()

    prev_epoch_start_slot = compute_start_slot_of_epoch(
        previous_epoch, slots_per_epoch)
    prev_epoch_attestations = tuple()
    for slot in range(prev_epoch_start_slot,
                      prev_epoch_start_slot + slots_per_epoch):
        committee, shard = get_crosslink_committees_at_slot(
            state, slot, CommitteeConfig(config))[0]
        if not committee:
            continue
        if shard not in valid_shards_for_epoch:
            continue
        participants_bitfield = get_empty_bitfield(len(committee))
        for i, index in enumerate(committee):
            indices_to_check.add(index)
            participants_bitfield = set_voted(participants_bitfield, i)
        prev_epoch_attestations += (PendingAttestation(
            **sample_pending_attestation_record_params).copy(
                aggregation_bits=participants_bitfield,
                inclusion_delay=min_attestation_inclusion_delay,
                proposer_index=get_beacon_proposer_index(
                    state.copy(slot=slot), CommitteeConfig(config)),
                data=AttestationData(**sample_attestation_data_params).copy(
                    crosslink=Crosslink(shard=shard),
                    target=Checkpoint(
                        epoch=previous_epoch,
                        root=get_block_root(
                            state,
                            previous_epoch,
                            config.SLOTS_PER_EPOCH,
                            config.SLOTS_PER_HISTORICAL_ROOT,
                        ),
                    ),
                    beacon_block_root=get_block_root_at_slot(
                        state, slot, config.SLOTS_PER_HISTORICAL_ROOT),
                ),
            ), )
    state = state.copy(previous_epoch_attestations=prev_epoch_attestations)

    rewards_received, penalties_received = get_attestation_deltas(
        state, config)

    # everyone attested, no penalties
    assert sum(penalties_received) == 0
    the_reward = rewards_received[0]
    # everyone performed the same, equal rewards
    assert sum(rewards_received) // len(rewards_received) == the_reward
Beispiel #8
0
def test_find_winning_crosslink_and_attesting_indices_from_candidates(
        genesis_state, number_of_candidates, config):
    some_epoch = config.GENESIS_EPOCH + 20
    some_shard = 3

    state = genesis_state.copy(
        slot=get_epoch_start_slot(some_epoch, config.SLOTS_PER_EPOCH),
        start_shard=some_shard,
        current_crosslinks=tuple(
            Crosslink(
                shard=i,
                data_root=(i).to_bytes(32, "little"),
            ) for i in range(config.SHARD_COUNT)),
    )

    full_committee = get_crosslink_committee(
        state,
        some_epoch,
        some_shard,
        CommitteeConfig(config),
    )

    # break the committees up into different subsets to simulate different
    # attestations for the same crosslink
    committees = tuple(
        random_sample(
            len(full_committee) // number_of_candidates, full_committee)
        for _ in range(number_of_candidates))
    seen = set()
    filtered_committees = tuple()
    for committee in committees:
        deduplicated_committee = tuple()
        for index in committee:
            if index in seen:
                pass
            else:
                seen.add(index)
                deduplicated_committee += (index, )
        filtered_committees += (deduplicated_committee, )

    candidates = tuple(
        mk_pending_attestation_from_committee(
            state.current_crosslinks[some_shard],
            len(full_committee),
            some_shard,
            target_epoch=some_epoch,
        ) for committee in filtered_committees)

    if number_of_candidates == 0:
        expected_result = (Crosslink(), tuple())
    else:
        expected_result = (
            candidates[0].data.crosslink,
            tuple(sorted(full_committee)),
        )

    result = _find_winning_crosslink_and_attesting_indices_from_candidates(
        state,
        candidates,
        config,
    )
    assert result == expected_result
Beispiel #9
0
def test_find_candidate_attestations_for_shard(genesis_state, config):
    some_epoch = config.GENESIS_EPOCH + 20
    # start on some shard and walk a subset of them
    some_shard = 3
    shard_offset = 24

    state = genesis_state.copy(
        slot=compute_start_slot_of_epoch(some_epoch, config.SLOTS_PER_EPOCH),
        start_shard=some_shard,
        current_crosslinks=tuple(
            Crosslink(shard=i, data_root=(i).to_bytes(32, "little"))
            for i in range(config.SHARD_COUNT)),
    )

    # sample a subset of the shards to make attestations for
    some_shards_with_attestations = random.sample(
        range(some_shard, some_shard + shard_offset), shard_offset // 2)

    committee_and_shard_pairs = tuple(
        (
            get_crosslink_committee(state, some_epoch, some_shard +
                                    i, CommitteeConfig(config)),
            some_shard + i,
        ) for i in range(shard_offset)
        if some_shard + i in some_shards_with_attestations)

    pending_attestations = {
        shard:
        mk_pending_attestation_from_committee(state.current_crosslinks[shard],
                                              len(committee), shard)
        for committee, shard in committee_and_shard_pairs
    }

    # invalidate some crosslinks to test the crosslink filter
    some_crosslinks_to_mangle = random.sample(
        some_shards_with_attestations,
        len(some_shards_with_attestations) // 2)

    shards_with_valid_crosslinks = set(some_shards_with_attestations) - set(
        some_crosslinks_to_mangle)

    crosslinks = tuple()
    for shard in range(config.SHARD_COUNT):
        if shard in shards_with_valid_crosslinks:
            crosslinks += (state.current_crosslinks[shard], )
        else:
            crosslinks += (Crosslink(), )

    state = state.copy(current_crosslinks=crosslinks)

    # check around the range of shards we built up
    for shard in range(0, some_shard + shard_offset + 3):
        if shard in some_shards_with_attestations:
            attestations = _get_attestations_for_shard(
                pending_attestations.values(), shard)
            assert attestations == (pending_attestations[shard], )

            if shard in some_crosslinks_to_mangle:
                assert not _get_attestations_for_valid_crosslink(
                    pending_attestations.values(), state, shard, config)
            else:
                attestations = _get_attestations_for_valid_crosslink(
                    pending_attestations.values(), state, shard, config)
                assert attestations == (pending_attestations[shard], )
        else:
            assert not _get_attestations_for_shard(
                pending_attestations.values(), shard)
            assert not _get_attestations_for_valid_crosslink(
                pending_attestations.values(), state, shard, config)
Beispiel #10
0
def _process_rewards_and_penalties_for_finality(
    state: BeaconState, config: Eth2Config,
    previous_epoch_active_validator_indices: Sequence[ValidatorIndex],
    previous_total_balance: Gwei,
    previous_epoch_attestations: Sequence[Attestation],
    previous_epoch_attester_indices: Sequence[ValidatorIndex],
    inclusion_infos: Dict[ValidatorIndex, InclusionInfo],
    effective_balances: Dict[ValidatorIndex,
                             Gwei], base_rewards: Dict[ValidatorIndex, Gwei]
) -> Tuple[Dict[ValidatorIndex, Gwei], Dict[ValidatorIndex,
                                            Gwei]]:  # noqa: E501
    previous_epoch_boundary_attestations = get_previous_epoch_boundary_attestations(
        state,
        config.SLOTS_PER_EPOCH,
        config.SLOTS_PER_HISTORICAL_ROOT,
    )
    previous_epoch_boundary_attester_indices = get_attester_indices_from_attestations(
        state=state,
        attestations=previous_epoch_boundary_attestations,
        committee_config=CommitteeConfig(config),
    )

    previous_epoch_head_attestations = get_previous_epoch_matching_head_attestations(
        state,
        config.SLOTS_PER_EPOCH,
        config.SLOTS_PER_HISTORICAL_ROOT,
    )
    previous_epoch_head_attester_indices = get_attester_indices_from_attestations(
        state=state,
        attestations=previous_epoch_head_attestations,
        committee_config=CommitteeConfig(config),
    )

    epochs_since_finality = state.next_epoch(
        config.SLOTS_PER_EPOCH) - state.finalized_epoch
    if epochs_since_finality <= 4:
        return _compute_normal_justification_and_finalization_deltas(
            state,
            config,
            previous_epoch_active_validator_indices,
            previous_total_balance,
            previous_epoch_attester_indices,
            previous_epoch_boundary_attester_indices,
            previous_epoch_head_attester_indices,
            inclusion_infos,
            effective_balances,
            base_rewards,
        )

    # epochs_since_finality > 4
    else:
        return _compute_inactivity_leak_deltas(
            state,
            config,
            previous_epoch_active_validator_indices,
            previous_epoch_attester_indices,
            previous_epoch_boundary_attester_indices,
            previous_epoch_head_attester_indices,
            inclusion_infos,
            effective_balances,
            base_rewards,
            epochs_since_finality,
        )
Beispiel #11
0
def process_rewards_and_penalties(state: BeaconState,
                                  config: Eth2Config) -> BeaconState:
    # Compute previous epoch active validator indices and the total balance they account for
    # for later use.
    previous_epoch_active_validator_indices = set(
        get_active_validator_indices(
            state.validator_registry,
            state.previous_epoch(config.SLOTS_PER_EPOCH)))
    previous_total_balance: Gwei = get_total_balance(
        state.validator_balances,
        tuple(previous_epoch_active_validator_indices),
        config.MAX_DEPOSIT_AMOUNT,
    )

    # Compute previous epoch attester indices and the total balance they account for
    # for later use.
    previous_epoch_attestations = state.previous_epoch_attestations
    previous_epoch_attester_indices = get_attester_indices_from_attestations(
        state=state,
        attestations=previous_epoch_attestations,
        committee_config=CommitteeConfig(config),
    )

    # Compute inclusion slot/distance of previous attestations for later use.
    inclusion_infos = get_inclusion_infos(
        state=state,
        attestations=previous_epoch_attestations,
        committee_config=CommitteeConfig(config),
    )

    # Compute effective balance of each previous epoch active validator for later use
    effective_balances = {
        ValidatorIndex(index): get_effective_balance(
            state.validator_balances,
            ValidatorIndex(index),
            config.MAX_DEPOSIT_AMOUNT,
        )
        for index in range(len(state.validator_registry))
    }
    # Compute base reward of each previous epoch active validator for later use
    base_rewards = {
        ValidatorIndex(index): get_base_reward(
            state=state,
            index=ValidatorIndex(index),
            base_reward_quotient=config.BASE_REWARD_QUOTIENT,
            previous_total_balance=previous_total_balance,
            max_deposit_amount=config.MAX_DEPOSIT_AMOUNT,
        )
        for index in range(len(state.validator_registry))
    }

    # 1. Process rewards and penalties for justification and finalization
    finality_rewards, finality_penalties = _process_rewards_and_penalties_for_finality(
        state,
        config,
        previous_epoch_active_validator_indices,
        previous_total_balance,
        previous_epoch_attestations,
        previous_epoch_attester_indices,
        inclusion_infos,
        effective_balances,
        base_rewards,
    )
    # 2. Process rewards and penalties for crosslinks
    crosslinks_rewards, crosslinks_penalties = _process_rewards_and_penalties_for_crosslinks(
        state,
        config,
        effective_balances,
        base_rewards,
    )

    # Apply the overall rewards/penalties
    for index in range(len(state.validator_registry)):
        state = state.update_validator_balance(
            ValidatorIndex(index),
            # Prevent validator balance under flow
            max(
                (state.validator_balances[index] + finality_rewards[index] +
                 crosslinks_rewards[index] - finality_penalties[index] -
                 crosslinks_penalties[index]),
                0,
            ),
        )

    return state
Beispiel #12
0
def _compute_normal_justification_and_finalization_deltas(
    state: BeaconState, config: Eth2Config,
    previous_epoch_active_validator_indices: Sequence[ValidatorIndex],
    previous_total_balance: Gwei,
    previous_epoch_attester_indices: Sequence[ValidatorIndex],
    previous_epoch_boundary_attester_indices: Sequence[ValidatorIndex],
    previous_epoch_head_attester_indices: Sequence[ValidatorIndex],
    inclusion_infos: Dict[ValidatorIndex, InclusionInfo],
    effective_balances: Dict[ValidatorIndex,
                             Gwei], base_rewards: Dict[ValidatorIndex, Gwei]
) -> Tuple[Dict[ValidatorIndex, Gwei], Dict[ValidatorIndex,
                                            Gwei]]:  # noqa: E501
    rewards_received = {
        ValidatorIndex(index): Gwei(0)
        for index in range(len(state.validator_registry))
    }
    penalties_received = rewards_received.copy()
    previous_epoch_attesting_balance = get_total_balance_from_effective_balances(
        effective_balances,
        previous_epoch_attester_indices,
    )
    previous_epoch_boundary_attesting_balance = get_total_balance_from_effective_balances(
        effective_balances,
        previous_epoch_boundary_attester_indices,
    )
    previous_epoch_head_attesting_balance = get_total_balance_from_effective_balances(
        effective_balances,
        previous_epoch_head_attester_indices,
    )
    for index in previous_epoch_active_validator_indices:
        # Expected FFG source
        if index in previous_epoch_attester_indices:
            rewards_received = _update_rewards_or_penalies(
                index,
                base_rewards[index] * previous_epoch_attesting_balance //
                previous_total_balance,
                rewards_received,
            )
            # Inclusion speed bonus
            rewards_received = _update_rewards_or_penalies(
                index,
                (base_rewards[index] *
                 config.MIN_ATTESTATION_INCLUSION_DELAY //
                 inclusion_infos[index].inclusion_distance),
                rewards_received,
            )
        else:
            penalties_received = _update_rewards_or_penalies(
                index,
                base_rewards[index],
                penalties_received,
            )
        # Expected FFG target
        if index in previous_epoch_boundary_attester_indices:
            rewards_received = _update_rewards_or_penalies(
                index,
                (base_rewards[index] *
                 previous_epoch_boundary_attesting_balance //
                 previous_total_balance),
                rewards_received,
            )
        else:
            penalties_received = _update_rewards_or_penalies(
                index,
                base_rewards[index],
                penalties_received,
            )
        # Expected head
        if index in previous_epoch_head_attester_indices:
            rewards_received = _update_rewards_or_penalies(
                index,
                (base_rewards[index] * previous_epoch_head_attesting_balance //
                 previous_total_balance),
                rewards_received,
            )
        else:
            penalties_received = _update_rewards_or_penalies(
                index,
                base_rewards[index],
                penalties_received,
            )
        # Proposer bonus
        if index in previous_epoch_attester_indices:
            proposer_index = get_beacon_proposer_index(
                state,
                inclusion_infos[index].inclusion_slot,
                CommitteeConfig(config),
            )
            rewards_received = _update_rewards_or_penalies(
                proposer_index,
                base_rewards[index] //
                config.ATTESTATION_INCLUSION_REWARD_QUOTIENT,
                rewards_received,
            )
    return (rewards_received, penalties_received)
Beispiel #13
0
def process_crosslinks(state: BeaconState, config: Eth2Config) -> BeaconState:
    """
    Implement 'per-epoch-processing.crosslinks' portion of Phase 0 spec:
    https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#crosslinks

    For each shard from the past two epochs, find the shard block
    root that has been attested to by the most stake.
    If enough(>= 2/3 total stake) attesting stake, update the crosslink record of that shard.
    Return resulting ``state``
    """
    latest_crosslinks = state.latest_crosslinks
    effective_balances = {
        ValidatorIndex(index): get_effective_balance(
            state.validator_balances,
            ValidatorIndex(index),
            config.MAX_DEPOSIT_AMOUNT,
        )
        for index in range(len(state.validator_registry))
    }
    previous_epoch_start_slot = get_epoch_start_slot(
        state.previous_epoch(config.SLOTS_PER_EPOCH),
        config.SLOTS_PER_EPOCH,
    )
    next_epoch_start_slot = get_epoch_start_slot(
        state.next_epoch(config.SLOTS_PER_EPOCH),
        config.SLOTS_PER_EPOCH,
    )
    for slot in range(previous_epoch_start_slot, next_epoch_start_slot):
        crosslink_committees_at_slot = get_crosslink_committees_at_slot(
            state,
            slot,
            CommitteeConfig(config),
        )
        for crosslink_committee, shard in crosslink_committees_at_slot:
            winning_root, attesting_validator_indices = get_winning_root_and_participants(
                state=state,
                shard=shard,
                effective_balances=effective_balances,
                committee_config=CommitteeConfig(config),
            )
            if len(attesting_validator_indices) > 0:
                total_attesting_balance = get_total_balance(
                    state.validator_balances,
                    attesting_validator_indices,
                    config.MAX_DEPOSIT_AMOUNT,
                )
                total_balance = get_total_balance(
                    state.validator_balances,
                    crosslink_committee,
                    config.MAX_DEPOSIT_AMOUNT,
                )
                if 3 * total_attesting_balance >= 2 * total_balance:
                    latest_crosslinks = update_tuple_item(
                        latest_crosslinks,
                        shard,
                        CrosslinkRecord(
                            epoch=slot_to_epoch(Slot(slot),
                                                config.SLOTS_PER_EPOCH),
                            crosslink_data_root=winning_root,
                        ),
                    )
    state = state.copy(latest_crosslinks=latest_crosslinks, )
    return state
def test_process_rewards_and_penalties_for_crosslinks(
    genesis_state,
    config,
    slots_per_epoch,
    target_committee_size,
    shard_count,
    current_slot,
    num_attesting_validators,
    max_effective_balance,
    min_attestation_inclusion_delay,
    sample_attestation_data_params,
    sample_pending_attestation_record_params,
):
    state = genesis_state.copy(slot=current_slot)
    previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH,
                                          config.GENESIS_EPOCH)

    prev_epoch_start_slot = compute_start_slot_of_epoch(
        previous_epoch, slots_per_epoch)
    prev_epoch_crosslink_committees = [
        get_crosslink_committees_at_slot(state, slot,
                                         CommitteeConfig(config))[0]
        for slot in range(prev_epoch_start_slot, prev_epoch_start_slot +
                          slots_per_epoch)
    ]

    # Record which validators attest during each slot for reward collation.
    each_slot_attestion_validators_list = []

    epoch_start_shard = get_start_shard(state, previous_epoch,
                                        CommitteeConfig(config))
    shard_delta = get_shard_delta(state, previous_epoch,
                                  CommitteeConfig(config))

    a = epoch_start_shard
    b = epoch_start_shard + shard_delta
    if a > b:
        valid_shards_for_epoch = range(b, a)
    else:
        valid_shards_for_epoch = range(a, b)

    indices_to_check = set()

    previous_epoch_attestations = []
    for committee, shard in prev_epoch_crosslink_committees:
        if shard not in valid_shards_for_epoch:
            continue
        for index in committee:
            indices_to_check.add(index)
        # Randomly sample `num_attesting_validators` validators
        # from the committee to attest in this slot.
        crosslink_attesting_validators = random.sample(
            committee, num_attesting_validators)
        each_slot_attestion_validators_list.append(
            crosslink_attesting_validators)
        participants_bitfield = get_empty_bitfield(len(committee))
        for index in crosslink_attesting_validators:
            participants_bitfield = set_voted(participants_bitfield,
                                              committee.index(index))
        previous_epoch_attestations.append(
            PendingAttestation(
                **sample_pending_attestation_record_params).copy(
                    aggregation_bits=participants_bitfield,
                    data=AttestationData(
                        **sample_attestation_data_params).copy(
                            target=Checkpoint(epoch=previous_epoch),
                            crosslink=Crosslink(
                                shard=shard,
                                parent_root=Crosslink().hash_tree_root),
                        ),
                ))
    state = state.copy(
        previous_epoch_attestations=tuple(previous_epoch_attestations))

    rewards_received, penalties_received = get_crosslink_deltas(state, config)

    expected_rewards_received = {
        index: 0
        for index in range(len(state.validators))
    }
    validator_balance = max_effective_balance
    for i in range(slots_per_epoch):
        crosslink_committee, shard = prev_epoch_crosslink_committees[i]
        if shard not in valid_shards_for_epoch:
            continue
        attesting_validators = each_slot_attestion_validators_list[i]
        total_attesting_balance = len(attesting_validators) * validator_balance
        total_committee_balance = len(crosslink_committee) * validator_balance
        for index in crosslink_committee:
            if index in attesting_validators:
                reward = (
                    get_base_reward(state=state, index=index, config=config) *
                    total_attesting_balance // total_committee_balance)
                expected_rewards_received[index] += reward
            else:
                penalty = get_base_reward(state=state,
                                          index=index,
                                          config=config)
                expected_rewards_received[index] -= penalty

    # Check the rewards/penalties match
    for index in range(len(state.validators)):
        if index not in indices_to_check:
            continue
        assert (rewards_received[index] -
                penalties_received[index] == expected_rewards_received[index])
Beispiel #15
0
def get_crosslink_deltas(state: BeaconState,
                         config: Eth2Config) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
    rewards = tuple(
        0 for _ in range(len(state.validators))
    )
    penalties = tuple(
        0 for _ in range(len(state.validators))
    )
    epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH)
    active_validators_indices = get_active_validator_indices(state.validators, epoch)
    epoch_committee_count = get_epoch_committee_count(
        len(active_validators_indices),
        config.SHARD_COUNT,
        config.SLOTS_PER_EPOCH,
        config.TARGET_COMMITTEE_SIZE,
    )
    epoch_start_shard = get_epoch_start_shard(
        state,
        epoch,
        CommitteeConfig(config),
    )
    for shard_offset in range(epoch_committee_count):
        shard = Shard((epoch_start_shard + shard_offset) % config.SHARD_COUNT)
        crosslink_committee = get_crosslink_committee(
            state,
            epoch,
            shard,
            CommitteeConfig(config),
        )
        _, attesting_indices = get_winning_crosslink_and_attesting_indices(
            state=state,
            epoch=epoch,
            shard=shard,
            config=config,
        )
        total_attesting_balance = get_total_balance(
            state,
            attesting_indices,
        )
        total_committee_balance = get_total_balance(
            state,
            crosslink_committee,
        )
        for index in crosslink_committee:
            base_reward = get_base_reward(state, index, config)
            if index in attesting_indices:
                rewards = update_tuple_item_with_fn(
                    rewards,
                    index,
                    lambda balance, delta: balance + delta,
                    base_reward * total_attesting_balance // total_committee_balance
                )
            else:
                penalties = update_tuple_item_with_fn(
                    penalties,
                    index,
                    lambda balance, delta: balance + delta,
                    base_reward,
                )
    return tuple(
        Gwei(reward) for reward in rewards
    ), tuple(
        Gwei(penalty) for penalty in penalties
    )
def process_attestations(state: BeaconState,
                         block: BaseBeaconBlock,
                         config: Eth2Config) -> BeaconState:
    """
    Implements 'per-block-processing.operations.attestations' portion of Phase 0 spec:
    https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestations-1

    Validate the ``attestations`` contained within the ``block`` in the context of ``state``.
    If any invalid, throw ``ValidationError``.
    Otherwise, append a ``PendingAttestationRecords`` for each to ``previous_epoch_attestations``
    or ``current_epoch_attestations``.
    Return resulting ``state``.
    """
    if len(block.body.attestations) > config.MAX_ATTESTATIONS:
        raise ValidationError(
            f"The block ({block}) has too many attestations:\n"
            f"\tFound {len(block.body.attestations)} attestations, "
            f"maximum: {config.MAX_ATTESTATIONS}"
        )

    for attestation in block.body.attestations:
        validate_attestation(
            state,
            attestation,
            config.MIN_ATTESTATION_INCLUSION_DELAY,
            config.SLOTS_PER_HISTORICAL_ROOT,
            CommitteeConfig(config),
        )

    # update attestations
    previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH)
    current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH)
    new_previous_epoch_pending_attestations = []
    new_current_epoch_pending_attestations = []
    for attestation in block.body.attestations:
        if slot_to_epoch(attestation.data.slot, config.SLOTS_PER_EPOCH) == current_epoch:
            new_current_epoch_pending_attestations.append(
                PendingAttestationRecord(
                    data=attestation.data,
                    aggregation_bitfield=attestation.aggregation_bitfield,
                    custody_bitfield=attestation.custody_bitfield,
                    slot_included=state.slot,
                )
            )
        elif slot_to_epoch(attestation.data.slot, config.SLOTS_PER_EPOCH) == previous_epoch:
            new_previous_epoch_pending_attestations.append(
                PendingAttestationRecord(
                    data=attestation.data,
                    aggregation_bitfield=attestation.aggregation_bitfield,
                    custody_bitfield=attestation.custody_bitfield,
                    slot_included=state.slot,
                )
            )

    state = state.copy(
        previous_epoch_attestations=(
            state.previous_epoch_attestations + tuple(new_previous_epoch_pending_attestations)
        ),
        current_epoch_attestations=(
            state.current_epoch_attestations + tuple(new_current_epoch_pending_attestations)
        ),
    )
    return state
Beispiel #17
0
def committee_config(config):
    return CommitteeConfig(config)
def test_get_attestation_deltas(
    genesis_state,
    config,
    slots_per_epoch,
    target_committee_size,
    max_committees_per_slot,
    min_attestation_inclusion_delay,
    inactivity_penalty_quotient,
    finalized_epoch,
    current_slot,
    sample_pending_attestation_record_params,
    sample_attestation_data_params,
):

    state = genesis_state.mset(
        "slot",
        current_slot,
        "finalized_checkpoint",
        Checkpoint.create(epoch=finalized_epoch),
    )
    previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH,
                                          config.GENESIS_EPOCH)
    has_inactivity_penalty = (previous_epoch - finalized_epoch >
                              config.MIN_EPOCHS_TO_INACTIVITY_PENALTY)

    indices_to_check = set()

    prev_epoch_attestations = tuple()

    for committee, committee_index, slot in iterate_committees_at_epoch(
            state, previous_epoch, config):
        participants_bitfield = get_empty_bitfield(len(committee))
        for i, index in enumerate(committee):
            indices_to_check.add(index)
            participants_bitfield = set_voted(participants_bitfield, i)
        prev_epoch_attestations += (PendingAttestation.create(
            **sample_pending_attestation_record_params).mset(
                "aggregation_bits",
                participants_bitfield,
                "inclusion_delay",
                min_attestation_inclusion_delay,
                "proposer_index",
                get_beacon_proposer_index(state.set("slot", slot),
                                          CommitteeConfig(config)),
                "data",
                AttestationData.create(**sample_attestation_data_params).mset(
                    "slot",
                    slot,
                    "index",
                    committee_index,
                    "beacon_block_root",
                    get_block_root_at_slot(state, slot,
                                           config.SLOTS_PER_HISTORICAL_ROOT),
                    "target",
                    Checkpoint.create(
                        epoch=previous_epoch,
                        root=get_block_root(
                            state,
                            previous_epoch,
                            config.SLOTS_PER_EPOCH,
                            config.SLOTS_PER_HISTORICAL_ROOT,
                        ),
                    ),
                ),
            ), )
    state = state.set("previous_epoch_attestations", prev_epoch_attestations)

    rewards_received, penalties_received = get_attestation_deltas(
        state, config)
    if has_inactivity_penalty:
        assert sum(penalties_received) > 0
    else:
        assert sum(penalties_received) == 0
    assert all(reward > 0 for reward in rewards_received)
Beispiel #19
0
    async def aggregate(
        self,
        slot: Slot
    ) -> Tuple[AggregateAndProof, ...]:
        """
        Aggregate the attestations at ``slot`` and broadcast them.
        """
        # Check the aggregators selection
        aggregate_and_proofs: Tuple[AggregateAndProof, ...] = ()
        state_machine = self.chain.get_state_machine()
        state = self.chain.get_head_state()
        config = state_machine.config

        attesting_committee_assignments_at_slot = self._get_attesting_assignments_at_slot(slot)
        # 1. For each committee_assignment at the given slot
        for committee_assignment in attesting_committee_assignments_at_slot:
            committee_index = committee_assignment.committee_index

            local_attesters = self._get_local_attesters_at_assignment(committee_assignment)
            # Get the validator_index -> privkey map of the attesting validators
            attesting_validator_privkeys = {
                index: self.validator_privkeys[index]
                for index in local_attesters
            }

            selected_proofs: Dict[ValidatorIndex, BLSSignature] = {}
            # 2. For each attester
            for validator_index, privkey in attesting_validator_privkeys.items():
                # Check if the vallidator is one of the aggregators
                signature = get_slot_signature(
                    state, slot, privkey, CommitteeConfig(config),
                )
                is_aggregator_result = is_aggregator(
                    state,
                    slot,
                    committee_index,
                    signature,
                    CommitteeConfig(config),
                )
                if is_aggregator_result:
                    self.logger.debug(
                        f"validator ({validator_index}) is aggregator of"
                        f" committee_index={committee_index} at slot={slot}"
                    )
                    selected_proofs[validator_index] = signature
                else:
                    continue

                aggregates = self._get_aggregates(slot, committee_index, config)
                # 3. For each aggregate
                # (it's possible with same CommitteeIndex and different AttesatationData)
                for aggregate in aggregates:
                    aggregate_and_proof = AggregateAndProof.create(
                        aggregator_index=validator_index,
                        aggregate=aggregate,
                        selection_proof=selected_proofs[validator_index],
                    )
                    self.import_attestation(aggregate_and_proof.aggregate, True)
                    await self.p2p_node.broadcast_beacon_aggregate_and_proof(aggregate_and_proof)
                    aggregate_and_proofs += (aggregate_and_proof,)

        return aggregate_and_proofs
Beispiel #20
0
def test_validate_proposer_signature(
        slots_per_epoch,
        shard_count,
        proposer_privkey,
        proposer_pubkey,
        is_valid_signature,
        sample_beacon_block_params,
        sample_beacon_state_params,
        beacon_chain_shard_number,
        target_committee_size,
        max_deposit_amount,
        config):

    state = BeaconState(**sample_beacon_state_params).copy(
        validator_registry=tuple(
            mock_validator_record(proposer_pubkey, config)
            for _ in range(10)
        ),
        validator_balances=(max_deposit_amount,) * 10,
    )

    default_block = BeaconBlock(**sample_beacon_block_params)
    empty_signature_block_root = default_block.block_without_signature_root

    proposal_signed_root = Proposal(
        state.slot,
        beacon_chain_shard_number,
        empty_signature_block_root,
    ).signed_root

    proposed_block = BeaconBlock(**sample_beacon_block_params).copy(
        signature=bls.sign(
            message_hash=proposal_signed_root,
            privkey=proposer_privkey,
            domain=get_domain(
                Fork(
                    config.GENESIS_FORK_VERSION.to_bytes(4, 'little'),
                    config.GENESIS_FORK_VERSION.to_bytes(4, 'little'),
                    config.GENESIS_EPOCH,
                ),
                slot_to_epoch(state.slot, slots_per_epoch),
                SignatureDomain.DOMAIN_BEACON_BLOCK,
            ),
        ),
    )

    if is_valid_signature:
        validate_proposer_signature(
            state,
            proposed_block,
            beacon_chain_shard_number,
            CommitteeConfig(config),
        )
    else:
        with pytest.raises(ValidationError):
            validate_proposer_signature(
                state,
                proposed_block,
                beacon_chain_shard_number,
                CommitteeConfig(config),
            )