Example #1
0
def process_validator_registry(state: BeaconState,
                               config: BeaconConfig) -> BeaconState:
    state = state.copy(
        previous_calculation_epoch=state.current_calculation_epoch,
        previous_epoch_start_shard=state.current_epoch_start_shard,
        previous_epoch_seed=state.current_epoch_seed,
    )

    need_to_update, num_shards_in_committees = _check_if_update_validator_registry(state, config)

    if need_to_update:
        state = update_validator_registry(state)

        # Update step-by-step since updated `state.current_calculation_epoch`
        # is used to calculate other value). Follow the spec tightly now.
        state = state.copy(
            current_calculation_epoch=state.next_epoch(config.EPOCH_LENGTH),
        )
        state = state.copy(
            current_epoch_start_shard=(
                state.current_epoch_start_shard + num_shards_in_committees
            ) % config.SHARD_COUNT,
        )

        # The `helpers.generate_seed` function is only present to provide an entry point
        # for mocking this out in tests.
        current_epoch_seed = helpers.generate_seed(
            state=state,
            epoch=state.current_calculation_epoch,
            epoch_length=config.EPOCH_LENGTH,
            seed_lookahead=config.SEED_LOOKAHEAD,
            entry_exit_delay=config.ENTRY_EXIT_DELAY,
            latest_index_roots_length=config.LATEST_INDEX_ROOTS_LENGTH,
            latest_randao_mixes_length=config.LATEST_RANDAO_MIXES_LENGTH,
        )
        state = state.copy(
            current_epoch_seed=current_epoch_seed,
        )
    else:
        epochs_since_last_registry_change = (
            state.current_epoch(config.EPOCH_LENGTH) - state.validator_registry_update_epoch
        )
        if is_power_of_two(epochs_since_last_registry_change):
            # Update step-by-step since updated `state.current_calculation_epoch`
            # is used to calculate other value). Follow the spec tightly now.
            state = state.copy(
                current_calculation_epoch=state.next_epoch(config.EPOCH_LENGTH),
            )

            # The `helpers.generate_seed` function is only present to provide an entry point
            # for mocking this out in tests.
            current_epoch_seed = helpers.generate_seed(
                state=state,
                epoch=state.current_calculation_epoch,
                epoch_length=config.EPOCH_LENGTH,
                seed_lookahead=config.SEED_LOOKAHEAD,
                entry_exit_delay=config.ENTRY_EXIT_DELAY,
                latest_index_roots_length=config.LATEST_INDEX_ROOTS_LENGTH,
                latest_randao_mixes_length=config.LATEST_RANDAO_MIXES_LENGTH,
            )
            state = state.copy(
                current_epoch_seed=current_epoch_seed,
            )
        else:
            pass

    return state
def _settle_penality_to_validator_and_whistleblower(
        *, state: BeaconState, validator_index: ValidatorIndex,
        latest_penalized_exit_length: int, whistleblower_reward_quotient: int,
        max_deposit_amount: Gwei,
        committee_config: CommitteeConfig) -> BeaconState:
    """
    Apply penality/reward to validator and whistleblower and update the meta data

    More intuitive pseudo-code:
    current_epoch_penalization_index = (state.slot // EPOCH_LENGTH) % LATEST_PENALIZED_EXIT_LENGTH
    state.latest_penalized_balances[current_epoch_penalization_index] += (
        get_effective_balance(state, index)
    )
    whistleblower_index = get_beacon_proposer_index(state, state.slot)
    whistleblower_reward = get_effective_balance(state, index) // WHISTLEBLOWER_REWARD_QUOTIENT
    state.validator_balances[whistleblower_index] += whistleblower_reward
    state.validator_balances[index] -= whistleblower_reward
    validator.penalized_epoch = slot_to_epoch(state.slot)
    """
    epoch_length = committee_config.EPOCH_LENGTH

    # Update `state.latest_penalized_balances`
    current_epoch_penalization_index = state.current_epoch(
        epoch_length) % latest_penalized_exit_length
    effective_balance = get_effective_balance(
        state.validator_balances,
        validator_index,
        max_deposit_amount,
    )
    penalized_exit_balance = (
        state.latest_penalized_balances[current_epoch_penalization_index] +
        effective_balance)
    latest_penalized_balances = update_tuple_item(
        tuple_data=state.latest_penalized_balances,
        index=current_epoch_penalization_index,
        new_value=penalized_exit_balance,
    )
    state = state.copy(latest_penalized_balances=latest_penalized_balances, )

    # Update whistleblower's balance
    whistleblower_reward = (effective_balance // whistleblower_reward_quotient)
    whistleblower_index = get_beacon_proposer_index(
        state,
        state.slot,
        committee_config,
    )
    state = state.update_validator_balance(
        whistleblower_index,
        state.validator_balances[whistleblower_index] + whistleblower_reward,
    )

    # Update validator's balance and `penalized_epoch` field
    validator = state.validator_registry[validator_index]
    validator = validator.copy(
        penalized_epoch=state.current_epoch(epoch_length), )
    state = state.update_validator(
        validator_index,
        validator,
        state.validator_balances[validator_index] - whistleblower_reward,
    )

    return state
Example #3
0
def get_total_active_balance(state: BeaconState, config: Eth2Config) -> Gwei:
    current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH)
    active_validator_indices = get_active_validator_indices(
        state.validators, current_epoch)
    return get_total_balance(state, set(active_validator_indices))
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
Example #5
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
Example #6
0
def update_validator_registry(state: BeaconState,
                              config: BeaconConfig) -> BeaconState:
    """
    Update validator registry.
    """
    current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH)
    # The active validators
    active_validator_indices = get_active_validator_indices(
        state.validator_registry, current_epoch)
    # The total effective balance of active validators
    total_balance = get_total_balance(
        state.validator_balances,
        active_validator_indices,
        config.MAX_DEPOSIT_AMOUNT,
    )

    # The maximum balance churn in Gwei (for deposits and exits separately)
    max_balance_churn = max(
        config.MAX_DEPOSIT_AMOUNT,
        total_balance // (2 * config.MAX_BALANCE_CHURN_QUOTIENT))

    # Activate validators within the allowable balance churn
    # linter didn't like a bare lambda
    state = _churn_validators(
        state=state,
        config=config,
        check_should_churn_fn=lambda state, index: _is_ready_to_activate(
            state,
            index,
            max_deposit_amount=config.MAX_DEPOSIT_AMOUNT,
        ),
        churn_fn=lambda state, index: activate_validator(
            state,
            index,
            is_genesis=False,
            genesis_epoch=config.GENESIS_EPOCH,
            slots_per_epoch=config.SLOTS_PER_EPOCH,
            activation_exit_delay=config.ACTIVATION_EXIT_DELAY,
        ),
        max_balance_churn=max_balance_churn,
    )

    # Exit validators within the allowable balance churn
    # linter didn't like a bare lambda
    state = _churn_validators(
        state=state,
        config=config,
        check_should_churn_fn=lambda state, index: _is_ready_to_exit(
            state, index),
        churn_fn=lambda state, index: exit_validator(
            state,
            index,
            slots_per_epoch=config.SLOTS_PER_EPOCH,
            activation_exit_delay=config.ACTIVATION_EXIT_DELAY,
        ),
        max_balance_churn=max_balance_churn,
    )

    state = state.copy(validator_registry_update_epoch=current_epoch, )

    return state
Example #7
0
def _process_rewards_and_penalties_for_crosslinks(
    state: BeaconState, config: BeaconConfig,
    previous_epoch_attestations: Iterable[PendingAttestationRecord],
    effective_balances: Dict[ValidatorIndex,
                             Gwei], base_rewards: Dict[ValidatorIndex, Gwei],
    old_rewards_received: Dict[ValidatorIndex, SignedGwei]
) -> Dict[ValidatorIndex, SignedGwei]:
    rewards_received = old_rewards_received.copy()
    previous_epoch_start_slot = get_epoch_start_slot(
        state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH),
        config.SLOTS_PER_EPOCH,
    )
    current_epoch_start_slot = get_epoch_start_slot(
        state.current_epoch(config.SLOTS_PER_EPOCH),
        config.SLOTS_PER_EPOCH,
    )
    # Also need current epoch attestations to compute the winning root.
    current_epoch_attestations = get_current_epoch_attestations(
        state, config.SLOTS_PER_EPOCH)
    for slot in range(previous_epoch_start_slot, current_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:
            filtered_attestations = _filter_attestations_by_shard(
                previous_epoch_attestations + current_epoch_attestations,
                shard,
            )
            try:
                winning_root, total_attesting_balance = get_winning_root(
                    state=state,
                    shard=shard,
                    attestations=filtered_attestations,
                    max_deposit_amount=config.MAX_DEPOSIT_AMOUNT,
                    committee_config=CommitteeConfig(config),
                )
            except NoWinningRootError:
                # No winning shard block root found for this shard.
                # Hence no one is counted as attesting validator.
                attesting_validator_indices: Iterable[ValidatorIndex] = set()
            else:
                attesting_validator_indices = get_attester_indices_from_attesttion(
                    state=state,
                    attestations=(a for a in filtered_attestations
                                  if a.data.shard == shard and
                                  a.data.crosslink_data_root == winning_root),
                    committee_config=CommitteeConfig(config),
                )
            total_balance = get_total_balance_from_effective_balances(
                effective_balances,
                crosslink_committee,
            )
            for index in attesting_validator_indices:
                reward = base_rewards[
                    index] * total_attesting_balance // total_balance
                rewards_received[index] = SignedGwei(rewards_received[index] +
                                                     reward)
            for index in set(crosslink_committee).difference(
                    attesting_validator_indices):
                penalty = base_rewards[index]
                rewards_received[index] = SignedGwei(rewards_received[index] -
                                                     penalty)
    return rewards_received
Example #8
0
def process_crosslinks(state: BeaconState,
                       config: BeaconConfig) -> 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
    previous_epoch_attestations = get_previous_epoch_attestations(
        state,
        config.SLOTS_PER_EPOCH,
        config.GENESIS_EPOCH,
    )
    current_epoch_attestations = get_current_epoch_attestations(
        state, config.SLOTS_PER_EPOCH)
    previous_epoch_start_slot = get_epoch_start_slot(
        state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_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:
            try:
                winning_root, total_attesting_balance = get_winning_root(
                    state=state,
                    shard=shard,
                    # Use `_filter_attestations_by_shard` to filter out attestations
                    # not attesting to this shard so we don't need to going over
                    # irrelevent attestations over and over again.
                    attestations=_filter_attestations_by_shard(
                        previous_epoch_attestations +
                        current_epoch_attestations,
                        shard,
                    ),
                    max_deposit_amount=config.MAX_DEPOSIT_AMOUNT,
                    committee_config=CommitteeConfig(config),
                )
            except NoWinningRootError:
                # No winning shard block root found for this shard.
                pass
            else:
                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=state.current_epoch(config.SLOTS_PER_EPOCH),
                            crosslink_data_root=winning_root,
                        ),
                    )
                else:
                    # Don't update the crosslink of this shard
                    pass
    state = state.copy(latest_crosslinks=latest_crosslinks, )
    return state
Example #9
0
def _compute_inactivity_leak_deltas(
    state: BeaconState, config: Eth2Config,
    previous_epoch_active_validator_indices: Sequence[ValidatorIndex],
    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], epochs_since_finality: int
) -> Tuple[Dict[ValidatorIndex, Gwei], Dict[ValidatorIndex,
                                            Gwei]]:  # noqa: E501
    inactivity_penalties = {
        ValidatorIndex(index): get_inactivity_penalty(
            base_reward=base_rewards[ValidatorIndex(index)],
            effective_balance=effective_balances[ValidatorIndex(index)],
            epochs_since_finality=epochs_since_finality,
            inactivity_penalty_quotient=config.INACTIVITY_PENALTY_QUOTIENT,
        )
        for index in range(len(state.validator_registry))
    }
    rewards_received = {
        ValidatorIndex(index): Gwei(0)
        for index in range(len(state.validator_registry))
    }
    penalties_received = rewards_received.copy()
    for index in previous_epoch_active_validator_indices:
        if index not in previous_epoch_attester_indices:
            penalties_received = _update_rewards_or_penalies(
                index,
                inactivity_penalties[index],
                penalties_received,
            )
        else:
            # If a validator did attest, apply a small penalty
            # for getting attestations included late
            rewards_received = _update_rewards_or_penalies(
                index,
                (base_rewards[index] //
                 config.MIN_ATTESTATION_INCLUSION_DELAY //
                 inclusion_infos[index].inclusion_distance),
                rewards_received,
            )
            penalties_received = _update_rewards_or_penalies(
                index,
                base_rewards[index],
                penalties_received,
            )
        if index not in previous_epoch_boundary_attester_indices:
            penalties_received = _update_rewards_or_penalies(
                index,
                inactivity_penalties[index],
                penalties_received,
            )
        if index not in previous_epoch_head_attester_indices:
            penalties_received = _update_rewards_or_penalies(
                index,
                base_rewards[index],
                penalties_received,
            )

    # Penalize slashed-but-inactive validators as though they were active but offline
    current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH)
    for i in range(len(state.validator_registry)):
        eligible = (
            i not in previous_epoch_active_validator_indices
            and state.validator_registry[ValidatorIndex(i)].slashed
            and current_epoch < state.validator_registry[i].withdrawable_epoch)
        if eligible:
            penalties_received = _update_rewards_or_penalies(
                ValidatorIndex(i),
                2 * inactivity_penalties[ValidatorIndex(i)] +
                base_rewards[ValidatorIndex(i)],
                penalties_received,
            )
    return (rewards_received, penalties_received)
Example #10
0
def process_justification(state: BeaconState,
                          config: Eth2Config) -> BeaconState:
    current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH)
    previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH)

    current_epoch_justifiable = _is_epoch_justifiable(
        state,
        state.current_epoch_attestations,
        current_epoch,
        config,
    )
    previous_epoch_justifiable = _is_epoch_justifiable(
        state,
        state.previous_epoch_attestations,
        previous_epoch,
        config,
    )

    _justification_bitfield = state.justification_bitfield << 1
    if previous_epoch_justifiable and current_epoch_justifiable:
        justification_bitfield = _justification_bitfield | 3
    elif previous_epoch_justifiable:
        justification_bitfield = _justification_bitfield | 2
    elif current_epoch_justifiable:
        justification_bitfield = _justification_bitfield | 1
    else:
        justification_bitfield = _justification_bitfield

    if current_epoch_justifiable:
        new_justified_epoch = current_epoch
    elif previous_epoch_justifiable:
        new_justified_epoch = previous_epoch
    else:
        new_justified_epoch = state.current_justified_epoch

    new_finalized_epoch, _ = _get_finalized_epoch(
        justification_bitfield,
        state.previous_justified_epoch,
        state.current_justified_epoch,
        state.finalized_epoch,
        previous_epoch,
    )

    # Update state
    state = state.copy(
        previous_justified_epoch=state.current_justified_epoch,
        previous_justified_root=state.current_justified_root,
        justification_bitfield=justification_bitfield,
    )

    if new_justified_epoch != state.current_justified_epoch:
        state = state.copy(
            current_justified_epoch=new_justified_epoch,
            current_justified_root=get_block_root(
                state,
                get_epoch_start_slot(new_justified_epoch,
                                     config.SLOTS_PER_EPOCH),
                config.SLOTS_PER_HISTORICAL_ROOT,
            ),
        )

    if new_finalized_epoch != state.finalized_epoch:
        state = state.copy(finalized_epoch=new_finalized_epoch,
                           finalized_root=get_block_root(
                               state,
                               get_epoch_start_slot(new_finalized_epoch,
                                                    config.SLOTS_PER_EPOCH),
                               config.SLOTS_PER_HISTORICAL_ROOT,
                           ))

    return state