예제 #1
0
def test_get_start_shard(genesis_state, current_epoch, target_epoch,
                         expected_epoch_start_shard, config):
    state = genesis_state.copy(slot=compute_start_slot_of_epoch(
        current_epoch, config.SLOTS_PER_EPOCH), )

    if expected_epoch_start_shard is None:
        with pytest.raises(ValidationError):
            get_start_shard(state, target_epoch, CommitteeConfig(config))
    else:
        epoch_start_shard = get_start_shard(state, target_epoch,
                                            CommitteeConfig(config))
        assert epoch_start_shard == expected_epoch_start_shard
예제 #2
0
def get_attestation_data_slot(state: BeaconState,
                              data: AttestationData,
                              config: Eth2Config) -> Slot:
    active_validator_indices = get_active_validator_indices(
        state.validators,
        data.target.epoch,
    )
    committee_count = get_committee_count(
        len(active_validator_indices),
        config.SHARD_COUNT,
        config.SLOTS_PER_EPOCH,
        config.TARGET_COMMITTEE_SIZE,
    )
    offset = (
        data.crosslink.shard + config.SHARD_COUNT - get_start_shard(
            state,
            data.target.epoch,
            CommitteeConfig(config),
        )
    ) % config.SHARD_COUNT
    committees_per_slot = committee_count // config.SLOTS_PER_EPOCH
    return compute_start_slot_of_epoch(
        data.target.epoch,
        config.SLOTS_PER_EPOCH,
    ) + offset // committees_per_slot
예제 #3
0
def test_calculate_first_committee_at_slot(genesis_state, config):
    state = genesis_state
    slots_per_epoch = config.SLOTS_PER_EPOCH
    shard_count = config.SHARD_COUNT
    target_committee_size = config.TARGET_COMMITTEE_SIZE

    current_epoch = state.current_epoch(slots_per_epoch)

    active_validator_indices = get_active_validator_indices(
        state.validators, current_epoch)

    committees_per_slot = get_committees_per_slot(
        len(active_validator_indices),
        shard_count,
        slots_per_epoch,
        target_committee_size,
    )

    assert state.slot % config.SLOTS_PER_EPOCH == 0
    for slot in range(state.slot, state.slot + config.SLOTS_PER_EPOCH):
        offset = committees_per_slot * (slot % slots_per_epoch)
        shard = (get_start_shard(state, current_epoch, config) +
                 offset) % shard_count
        committee = get_crosslink_committee(
            state,
            current_epoch,
            shard,
            config,
        )

        assert committee == _calculate_first_committee_at_slot(
            state, slot, CommitteeConfig(config))
예제 #4
0
def test_validate_attestation_data(genesis_state,
                                   sample_attestation_data_params,
                                   attestation_source_epoch,
                                   attestation_target_epoch,
                                   current_epoch,
                                   previous_justified_epoch,
                                   current_justified_epoch,
                                   slots_per_epoch,
                                   config,
                                   is_valid):
    state = genesis_state.copy(
        slot=compute_start_slot_of_epoch(current_epoch, slots_per_epoch) + 5,
        previous_justified_checkpoint=Checkpoint(
            epoch=previous_justified_epoch,
        ),
        current_justified_checkpoint=Checkpoint(
            epoch=current_justified_epoch,
        ),
    )
    start_shard = get_start_shard(
        state,
        current_epoch,
        CommitteeConfig(config),
    )
    if attestation_target_epoch == current_epoch:
        crosslinks = state.current_crosslinks
    else:
        crosslinks = state.previous_crosslinks

    parent_crosslink = crosslinks[start_shard]
    attestation_data = AttestationData(**sample_attestation_data_params).copy(
        source=Checkpoint(
            epoch=attestation_source_epoch,
        ),
        target=Checkpoint(
            epoch=attestation_target_epoch,
        ),
        crosslink=Crosslink(
            start_epoch=parent_crosslink.end_epoch,
            end_epoch=attestation_target_epoch,
            parent_root=parent_crosslink.hash_tree_root,
            shard=start_shard,
        ),
    )

    if is_valid:
        _validate_attestation_data(
            state,
            attestation_data,
            config,
        )
    else:
        with pytest.raises(ValidationError):
            _validate_attestation_data(
                state,
                attestation_data,
                config,
            )
예제 #5
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_committee_count(
        len(active_validators_indices),
        config.SHARD_COUNT,
        config.SLOTS_PER_EPOCH,
        config.TARGET_COMMITTEE_SIZE,
    )
    epoch_start_shard = get_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 = set(
            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)
예제 #6
0
def process_crosslinks(state: BeaconState, config: Eth2Config) -> BeaconState:
    current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH)
    previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH,
                                          config.GENESIS_EPOCH)

    new_current_crosslinks = state.current_crosslinks

    for epoch in (previous_epoch, current_epoch):
        active_validators_indices = get_active_validator_indices(
            state.validators, epoch)
        epoch_committee_count = get_committee_count(
            len(active_validators_indices),
            config.SHARD_COUNT,
            config.SLOTS_PER_EPOCH,
            config.TARGET_COMMITTEE_SIZE,
        )
        epoch_start_shard = get_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 = set(
                get_crosslink_committee(
                    state,
                    epoch,
                    shard,
                    CommitteeConfig(config),
                ))

            if not crosslink_committee:
                # empty crosslink committee this epoch
                continue

            winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(
                state=state,
                epoch=epoch,
                shard=shard,
                config=config,
            )
            threshold_met = _is_threshold_met_against_committee(
                state,
                attesting_indices,
                crosslink_committee,
            )
            if threshold_met:
                new_current_crosslinks = update_tuple_item(
                    new_current_crosslinks,
                    shard,
                    winning_crosslink,
                )

    return state.copy(
        previous_crosslinks=state.current_crosslinks,
        current_crosslinks=new_current_crosslinks,
    )
예제 #7
0
def _mk_attestation_inputs_in_epoch(epoch, state, config):
    active_validators_indices = get_active_validator_indices(state.validators, epoch)
    epoch_committee_count = get_committee_count(
        len(active_validators_indices),
        config.SHARD_COUNT,
        config.SLOTS_PER_EPOCH,
        config.TARGET_COMMITTEE_SIZE,
    )
    epoch_start_shard = get_start_shard(
        state,
        epoch,
        CommitteeConfig(config),
    )
    for shard_offset in random.sample(range(epoch_committee_count), epoch_committee_count):
        shard = Shard((epoch_start_shard + shard_offset) % config.SHARD_COUNT)
        committee = get_crosslink_committee(
            state,
            epoch,
            shard,
            CommitteeConfig(config),
        )

        if not committee:
            # empty crosslink committee this epoch
            continue

        attestation_data = AttestationData(
            target=Checkpoint(
                epoch=epoch,
            ),
            crosslink=Crosslink(
                shard=shard,
            ),
        )
        committee_count = len(committee)
        aggregation_bits = bitfield.get_empty_bitfield(committee_count)
        for index in range(committee_count):
            aggregation_bits = bitfield.set_voted(aggregation_bits, index)

            for index in committee:
                yield (
                    index,
                    (
                        get_attestation_data_slot(
                            state,
                            attestation_data,
                            config,
                        ),
                        (
                            aggregation_bits,
                            attestation_data,
                        ),
                    ),
                )
예제 #8
0
def get_committee_assignment(
    state: BeaconState,
    config: Eth2Config,
    epoch: Epoch,
    validator_index: ValidatorIndex,
) -> CommitteeAssignment:
    """
    Return the ``CommitteeAssignment`` in the ``epoch`` for ``validator_index``.
    ``CommitteeAssignment.committee`` is the tuple array of validators in the committee
    ``CommitteeAssignment.shard`` is the shard to which the committee is assigned
    ``CommitteeAssignment.slot`` is the slot at which the committee is assigned
    ``CommitteeAssignment.is_proposer`` is a bool signalling if the validator is expected to
        propose a beacon block at the assigned slot.
    """
    next_epoch = state.next_epoch(config.SLOTS_PER_EPOCH)
    if epoch > next_epoch:
        raise ValidationError(
            f"Epoch for committee assignment ({epoch}) must not be after next epoch {next_epoch}."
        )

    active_validators = get_active_validator_indices(state.validators, epoch)
    committees_per_slot = (
        get_committee_count(
            len(active_validators),
            config.SHARD_COUNT,
            config.SLOTS_PER_EPOCH,
            config.TARGET_COMMITTEE_SIZE,
        )
        // config.SLOTS_PER_EPOCH
    )
    epoch_start_slot = compute_start_slot_of_epoch(epoch, config.SLOTS_PER_EPOCH)
    epoch_start_shard = get_start_shard(state, epoch, CommitteeConfig(config))

    for slot in range(epoch_start_slot, epoch_start_slot + config.SLOTS_PER_EPOCH):
        offset = committees_per_slot * (slot % config.SLOTS_PER_EPOCH)
        slot_start_shard = (epoch_start_shard + offset) % config.SHARD_COUNT
        for i in range(committees_per_slot):
            shard = Shard((slot_start_shard + i) % config.SHARD_COUNT)
            committee = get_crosslink_committee(
                state, epoch, shard, CommitteeConfig(config)
            )
            if validator_index in committee:
                is_proposer = validator_index == get_beacon_proposer_index(
                    state.copy(slot=slot), CommitteeConfig(config)
                )
                return CommitteeAssignment(
                    committee, Shard(shard), Slot(slot), is_proposer
                )

    raise NoCommitteeAssignment
예제 #9
0
def _find_collision(state, config, index, epoch):
    """
    Given a target epoch, make the attestation expected for the
    validator w/ the given index.
    """
    active_validators = get_active_validator_indices(state.validators, epoch)
    committees_per_slot = get_committee_count(
        len(active_validators),
        config.SHARD_COUNT,
        config.SLOTS_PER_EPOCH,
        config.TARGET_COMMITTEE_SIZE,
    ) // config.SLOTS_PER_EPOCH
    epoch_start_slot = compute_start_slot_of_epoch(
        epoch,
        config.SLOTS_PER_EPOCH,
    )
    epoch_start_shard = get_start_shard(state, epoch, CommitteeConfig(config))

    for slot in range(epoch_start_slot, epoch_start_slot + config.SLOTS_PER_EPOCH):
        offset = committees_per_slot * (slot % config.SLOTS_PER_EPOCH)
        slot_start_shard = (epoch_start_shard + offset) % config.SHARD_COUNT
        for i in range(committees_per_slot):
            shard = Shard((slot_start_shard + i) % config.SHARD_COUNT)
            committee = get_crosslink_committee(state, epoch, shard, CommitteeConfig(config))
            if index in committee:
                # TODO(ralexstokes) refactor w/ tools/builder
                attestation_data = AttestationData(
                    target=Checkpoint(
                        epoch=epoch,
                    ),
                    crosslink=Crosslink(
                        shard=shard,
                    ),
                )
                committee_count = len(committee)
                aggregation_bits = bitfield.get_empty_bitfield(committee_count)
                for i in range(committee_count):
                    aggregation_bits = bitfield.set_voted(aggregation_bits, i)

                return {
                    index: (
                        slot, (aggregation_bits, attestation_data)
                    )
                    for index in committee
                }
    else:
        raise Exception("should have found a duplicate validator")
예제 #10
0
def _mk_some_pending_attestations_with_some_participation_in_epoch(
        state: BeaconState, epoch: Epoch, config: Eth2Config,
        participation_ratio: float,
        number_of_shards_to_check: int) -> Iterable[PendingAttestation]:
    block_root = get_block_root(
        state,
        epoch,
        config.SLOTS_PER_EPOCH,
        config.SLOTS_PER_HISTORICAL_ROOT,
    )
    epoch_start_shard = get_start_shard(
        state,
        epoch,
        CommitteeConfig(config),
    )

    if epoch == state.current_epoch(config.SLOTS_PER_EPOCH):
        parent_crosslinks = state.current_crosslinks
    else:
        parent_crosslinks = state.previous_crosslinks

    for shard in range(epoch_start_shard,
                       epoch_start_shard + number_of_shards_to_check):
        shard = Shard(shard % config.SHARD_COUNT)
        crosslink_committee = get_crosslink_committee(
            state,
            epoch,
            shard,
            CommitteeConfig(config),
        )
        if not crosslink_committee:
            continue

        participants_count = math.ceil(participation_ratio *
                                       len(crosslink_committee))
        if not participants_count:
            return tuple()

        yield mk_pending_attestation_from_committee(
            parent_crosslinks[shard],
            participants_count,
            shard,
            target_epoch=epoch,
            target_root=block_root,
        )
예제 #11
0
def get_crosslink_committees_at_slot(
    state: BeaconState, slot: Slot, config: Eth2Config
) -> Tuple[Tuple[Tuple[ValidatorIndex, ...], Shard], ...]:
    epoch = compute_epoch_of_slot(slot, config.SLOTS_PER_EPOCH)
    active_validators = get_active_validator_indices(state.validators, epoch)
    committees_per_slot = (get_committee_count(
        len(active_validators),
        config.SHARD_COUNT,
        config.SLOTS_PER_EPOCH,
        config.TARGET_COMMITTEE_SIZE,
    ) // config.SLOTS_PER_EPOCH)
    results = []
    offset = committees_per_slot * (slot % config.SLOTS_PER_EPOCH)
    slot_start_shard = Shard(
        (get_start_shard(state, epoch, CommitteeConfig(config)) + offset) %
        config.SHARD_COUNT)
    for i in range(committees_per_slot):
        shard = (slot_start_shard + i) % config.SHARD_COUNT
        committee = get_crosslink_committee(state, epoch, shard,
                                            CommitteeConfig(config))
        results.append((committee, Shard(shard)))

    return tuple(results)
예제 #12
0
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])
예제 #13
0
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)