Esempio n. 1
0
 def __init__(self,
              chain: BeaconChain,
              p2p_node: Node,
              validator_privkeys: Dict[ValidatorIndex, int],
              event_bus: EndpointAPI,
              get_ready_attestations_fn: GetReadyAttestationsFn,
              token: CancelToken = None) -> None:
     super().__init__(token)
     self.chain = chain
     self.p2p_node = p2p_node
     self.validator_privkeys = validator_privkeys
     self.event_bus = event_bus
     config = self.chain.get_state_machine().config
     self.slots_per_epoch = config.SLOTS_PER_EPOCH
     # TODO: `latest_proposed_epoch` and `latest_attested_epoch` should be written
     # into/read from validator's own db.
     self.latest_proposed_epoch = {}
     self.latest_attested_epoch = {}
     self.this_epoch_assignment = {}
     for validator_index in validator_privkeys:
         self.latest_proposed_epoch[validator_index] = Epoch(-1)
         self.latest_attested_epoch[validator_index] = Epoch(-1)
         self.this_epoch_assignment[validator_index] = (
             Epoch(-1),
             CommitteeAssignment((), Shard(-1), Slot(-1), False),
         )
     self.get_ready_attestations: GetReadyAttestationsFn = get_ready_attestations_fn
Esempio n. 2
0
    def __init__(
            self,
            chain: BaseBeaconChain,
            p2p_node: Node,
            validator_privkeys: Dict[ValidatorIndex, int],
            event_bus: EndpointAPI,
            get_ready_attestations_fn: GetReadyAttestationsFn,
            get_aggregatable_attestations_fn: GetAggregatableAttestationsFn,
            import_attestation_fn: ImportAttestationFn,
            token: CancelToken = None) -> None:
        super().__init__(
            chain,
            p2p_node,
            event_bus,
            get_ready_attestations_fn,
            get_aggregatable_attestations_fn,
            import_attestation_fn,
            token,
        )

        self.validator_privkeys = validator_privkeys
        config = self.chain.get_state_machine().config
        self.slots_per_epoch = config.SLOTS_PER_EPOCH
        # TODO: `latest_proposed_epoch` and `latest_attested_epoch` should be written
        # into/read from validator's own db.
        self.latest_proposed_epoch = {}
        self.latest_attested_epoch = {}
        self.local_validator_epoch_assignment = {}
        for validator_index in validator_privkeys:
            self.latest_proposed_epoch[validator_index] = Epoch(-1)
            self.latest_attested_epoch[validator_index] = Epoch(-1)
            self.local_validator_epoch_assignment[validator_index] = (
                Epoch(-1),
                CommitteeAssignment((), CommitteeIndex(-1), Slot(-1)),
            )
Esempio n. 3
0
def generate_seed(state: 'BeaconState',
                  epoch: Epoch,
                  slots_per_epoch: int,
                  min_seed_lookahead: int,
                  activation_exit_delay: int,
                  latest_active_index_roots_length: int,
                  latest_randao_mixes_length: int) -> Hash32:
    """
    Generate a seed for the given ``epoch``.
    """
    randao_mix = get_randao_mix(
        state=state,
        epoch=Epoch(epoch - min_seed_lookahead),
        slots_per_epoch=slots_per_epoch,
        latest_randao_mixes_length=latest_randao_mixes_length,
    )
    active_index_root = get_active_index_root(
        state=state,
        epoch=epoch,
        slots_per_epoch=slots_per_epoch,
        activation_exit_delay=activation_exit_delay,
        latest_active_index_roots_length=latest_active_index_roots_length,
    )
    epoch_as_bytes = epoch.to_bytes(32, byteorder="little")

    return hash_eth2(randao_mix + active_index_root + epoch_as_bytes)
def get_beacon_proposer_index(state: 'BeaconState',
                              slot: Slot,
                              committee_config: CommitteeConfig,
                              registry_change: bool = False) -> ValidatorIndex:
    """
    Return the beacon proposer index for the ``slot``.
    """
    epoch = slot_to_epoch(slot, committee_config.SLOTS_PER_EPOCH)
    current_epoch = state.current_epoch(committee_config.SLOTS_PER_EPOCH)
    previous_epoch = state.previous_epoch(
        committee_config.SLOTS_PER_EPOCH,
        committee_config.GENESIS_EPOCH,
    )
    next_epoch = Epoch(current_epoch + 1)

    validate_epoch_within_previous_and_next(epoch, previous_epoch, next_epoch)

    crosslink_committees_at_slot = get_crosslink_committees_at_slot(
        state=state,
        slot=slot,
        committee_config=committee_config,
        registry_change=registry_change,
    )
    try:
        first_crosslink_committee = crosslink_committees_at_slot[0]
    except IndexError:
        raise ValidationError("crosslink_committees should not be empty.")

    first_committee, _ = first_crosslink_committee
    if len(first_committee) <= 0:
        raise ValidationError("The first committee should not be empty")

    return first_committee[slot % len(first_committee)]
Esempio n. 5
0
async def _fetch_latest_duties(
    tick: Tick,
    beacon_node: BeaconNodeAPI,
    validator_public_keys: Collection[BLSPubkey],
    duty_store: DutyStore,
) -> None:
    current_epoch = tick.epoch
    next_epoch = Epoch(current_epoch + 1)

    current_duties = await beacon_node.fetch_duties(tick,
                                                    validator_public_keys,
                                                    current_epoch)
    upcoming_duties = await beacon_node.fetch_duties(tick,
                                                     validator_public_keys,
                                                     next_epoch)
    latest_duties = cast(Tuple[Duty, ...], current_duties) + cast(
        Tuple[Duty, ...], upcoming_duties)
    if not latest_duties:
        return

    logger.debug("%s: found duties %s", tick, latest_duties)

    # TODO manage duties correctly, accounting for re-orgs, etc.
    # NOTE: the naive strategy is likely "last write wins"
    await duty_store.add_duties(*latest_duties)
Esempio n. 6
0
 def previous_epoch(self, slots_per_epoch: int,
                    genesis_epoch: Epoch) -> Epoch:
     current_epoch = self.current_epoch(slots_per_epoch)
     if current_epoch == genesis_epoch:
         return genesis_epoch
     else:
         return Epoch(current_epoch - 1)
Esempio n. 7
0
def compute_activation_exit_epoch(epoch: Epoch,
                                  activation_exit_delay: int) -> Epoch:
    """
    An entry or exit triggered in the ``epoch`` given by the input takes effect at
    the epoch given by the output.
    """
    return Epoch(epoch + 1 + activation_exit_delay)
Esempio n. 8
0
def compute_activation_exit_epoch(epoch: Epoch,
                                  max_seed_lookahead: int) -> Epoch:
    """
    An entry or exit triggered in the ``epoch`` given by the input takes effect at
    the epoch given by the output.
    """
    return Epoch(epoch + 1 + max_seed_lookahead)
def _set_validator_slashed(v: Validator, current_epoch: Epoch,
                           epochs_per_slashings_vector: int) -> Validator:
    return v.copy(
        slashed=True,
        withdrawable_epoch=max(
            v.withdrawable_epoch,
            Epoch(current_epoch + epochs_per_slashings_vector)),
    )
Esempio n. 10
0
def _set_validator_slashed(v: Validator, current_epoch: Epoch,
                           epochs_per_slashings_vector: int) -> Validator:
    return v.mset(
        "slashed",
        True,
        "withdrawable_epoch",
        max(v.withdrawable_epoch,
            Epoch(current_epoch + epochs_per_slashings_vector)),
    )
Esempio n. 11
0
async def _get_validator_duties(context: Context, request: Request) -> Response:
    if not isinstance(request, dict):
        return ()

    if "validator_pubkeys" not in request:
        return ()
    public_keys = tuple(map(decode_hex, request["validator_pubkeys"].split(",")))
    epoch = Epoch(int(request["epoch"]))
    duties = context.get_validator_duties(public_keys, epoch)
    return tuple(map(_marshal_duty, duties))
Esempio n. 12
0
 def process_attestation(self, validator_index: ValidatorIndex,
                         block_root: Root, target_epoch: Epoch) -> None:
     if validator_index >= len(self.votes):
         self.votes.extend([
             VoteTracker(default_root, default_root, Epoch(0))
             for _ in range(validator_index - len(self.votes) + 1)
         ])
     vote = self.votes[validator_index]
     if target_epoch > vote.next_epoch:
         vote.next_root = block_root
         vote.next_epoch = target_epoch
Esempio n. 13
0
 def _randao_provider_of_epoch_signature(
     public_key: BLSPubkey, epoch: Epoch
 ) -> BLSSignature:
     private_key = private_key_provider(public_key)
     # TODO: fix how we get the signing root
     message = Hash32(epoch.to_bytes(32, byteorder="big"))
     domain = Domain(
         b"\x00" * 4 + signature_domain_to_domain_type(SignatureDomain.DOMAIN_RANDAO)
     )
     sig = bls.sign(message, private_key, domain)
     return sig
Esempio n. 14
0
def get_committee_assignment(
        state: BeaconState,
        config: BeaconConfig,
        epoch: Epoch,
        validator_index: ValidatorIndex,
        registry_change: bool = False) -> CommitteeAssignment:
    """
    Return the ``CommitteeAssignment`` in the ``epoch`` for ``validator_index``
    and ``registry_change``.
    ``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.
    """
    current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH)
    previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH,
                                          config.GENESIS_EPOCH)
    next_epoch = Epoch(current_epoch + 1)

    validate_epoch_within_previous_and_next(epoch, previous_epoch, next_epoch)

    epoch_start_slot = get_epoch_start_slot(epoch, config.SLOTS_PER_EPOCH)

    committee_config = CommitteeConfig(config)

    for slot in range(epoch_start_slot,
                      epoch_start_slot + config.SLOTS_PER_EPOCH):
        crosslink_committees = get_crosslink_committees_at_slot(
            state,
            slot,
            committee_config,
            registry_change=registry_change,
        )
        selected_committees = [
            committee for committee in crosslink_committees
            if validator_index in committee[0]
        ]
        if len(selected_committees) > 0:
            validators = selected_committees[0][0]
            shard = selected_committees[0][1]
            is_proposer = validator_index == get_beacon_proposer_index(
                state,
                Slot(slot),
                committee_config,
                registry_change=registry_change,
            )

            return CommitteeAssignment(validators, shard, Slot(slot),
                                       is_proposer)

    raise NoCommitteeAssignment
Esempio n. 15
0
def _compute_exit_queue_epoch(state: BeaconState, churn_limit: int,
                              config: Eth2Config) -> Epoch:
    slots_per_epoch = config.SLOTS_PER_EPOCH

    exit_epochs = tuple(v.exit_epoch for v in state.validators
                        if v.exit_epoch != FAR_FUTURE_EPOCH)
    exit_queue_epoch = max(exit_epochs + (compute_activation_exit_epoch(
        state.current_epoch(slots_per_epoch), config.MAX_SEED_LOOKAHEAD), ))
    exit_queue_churn = len(
        tuple(v for v in state.validators if v.exit_epoch == exit_queue_epoch))
    if exit_queue_churn >= churn_limit:
        exit_queue_epoch += 1
    return Epoch(exit_queue_epoch)
Esempio n. 16
0
def _compute_next_active_index_roots(state: BeaconState,
                                     config: Eth2Config) -> Tuple[Hash32, ...]:
    next_epoch = state.next_epoch(config.SLOTS_PER_EPOCH)
    index_root_position = (next_epoch + config.ACTIVATION_EXIT_DELAY
                           ) % config.EPOCHS_PER_HISTORICAL_VECTOR
    validator_indices_for_new_active_index_root = get_active_validator_indices(
        state.validators, Epoch(next_epoch + config.ACTIVATION_EXIT_DELAY))
    new_active_index_root = ssz.get_hash_tree_root(
        validator_indices_for_new_active_index_root,
        ssz.sedes.List(ssz.uint64, config.VALIDATOR_REGISTRY_LIMIT),
    )
    return update_tuple_item(state.active_index_roots, index_root_position,
                             new_active_index_root)
Esempio n. 17
0
def generate_seed(state: 'BeaconState', epoch: Epoch,
                  committee_config: CommitteeConfig) -> Hash32:
    """
    Generate a seed for the given ``epoch``.
    """
    randao_mix = get_randao_mix(
        state=state,
        epoch=Epoch(epoch - committee_config.MIN_SEED_LOOKAHEAD),
        slots_per_epoch=committee_config.SLOTS_PER_EPOCH,
        latest_randao_mixes_length=committee_config.LATEST_RANDAO_MIXES_LENGTH,
    )
    active_index_root = get_active_index_root(
        state=state,
        epoch=epoch,
        slots_per_epoch=committee_config.SLOTS_PER_EPOCH,
        activation_exit_delay=committee_config.ACTIVATION_EXIT_DELAY,
        latest_active_index_roots_length=committee_config.
        LATEST_ACTIVE_INDEX_ROOTS_LENGTH,
    )
    epoch_as_bytes = epoch.to_bytes(32, byteorder="little")

    return hash_eth2(randao_mix + active_index_root + epoch_as_bytes)
Esempio n. 18
0
    def __init__(
            self,
            chain: BaseBeaconChain,
            p2p_node: Node,
            validator_privkeys: Dict[ValidatorIndex, int],
            event_bus: EndpointAPI,
            get_ready_attestations_fn: GetReadyAttestationsFn,
            get_aggregatable_attestations_fn: GetAggregatableAttestationsFn,
            import_attestation_fn: ImportAttestationFn,
            token: CancelToken = None) -> None:
        super().__init__(token)
        self.genesis_time = chain.get_head_state().genesis_time
        self.chain = chain
        self.p2p_node = p2p_node
        self.validator_privkeys = validator_privkeys
        self.event_bus = event_bus
        config = self.chain.get_state_machine().config
        self.slots_per_epoch = config.SLOTS_PER_EPOCH
        # TODO: `latest_proposed_epoch` and `latest_attested_epoch` should be written
        # into/read from validator's own db.
        self.latest_proposed_epoch = {}
        self.latest_attested_epoch = {}
        self.local_validator_epoch_assignment = {}
        for validator_index in validator_privkeys:
            self.latest_proposed_epoch[validator_index] = Epoch(-1)
            self.latest_attested_epoch[validator_index] = Epoch(-1)
            self.local_validator_epoch_assignment[validator_index] = (
                Epoch(-1),
                CommitteeAssignment((), CommitteeIndex(-1), Slot(-1)),
            )
        self.get_ready_attestations: GetReadyAttestationsFn = get_ready_attestations_fn
        self.get_aggregatable_attestations: GetAggregatableAttestationsFn = get_aggregatable_attestations_fn  # noqa: E501
        self.import_attestation: ImportAttestationFn = import_attestation_fn

        # `state.eth1_data` can be updated in the middle of voting period and thus
        # the starting `eth1_data.block_hash` must be stored separately.
        self.starting_eth1_block_hash = chain.get_head_state(
        ).eth1_data.block_hash
Esempio n. 19
0
def get_start_shard(state: BeaconState, epoch: Epoch,
                    config: CommitteeConfig) -> Shard:
    current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH)
    next_epoch = state.next_epoch(config.SLOTS_PER_EPOCH)
    if epoch > next_epoch:
        raise ValidationError("Asking for start shard for an epoch after next")

    check_epoch = int(next_epoch)
    shard = (state.start_shard + get_shard_delta(state, current_epoch,
                                                 config)) % config.SHARD_COUNT
    while check_epoch > epoch:
        check_epoch -= 1
        shard = (shard + config.SHARD_COUNT - get_shard_delta(
            state, Epoch(check_epoch), config)) % config.SHARD_COUNT
    return shard
Esempio n. 20
0
    def _read_state_randao_mixes(self, state_root: Root,
                                 EPOCHS_PER_HISTORICAL_VECTOR: int,
                                 SLOTS_PER_EPOCH: int) -> Iterable[Root]:
        """
        Reconstructs the ``randao_mixes`` at a given state root.
        """
        state_slot = self._read_state_slot(state_root)
        state_epoch = compute_epoch_at_slot(state_slot, SLOTS_PER_EPOCH)

        finalized_slot = self.get_finalized_head(BeaconBlock).slot
        non_finalized_state_roots = dict(
            enumerate(
                self.get_state_parents(state_root,
                                       state_slot - finalized_slot),
                finalized_slot,
            ))

        # create a list of epochs that corresponds to each mix in ``state.randao_mixes``
        epochs = [
            Epoch(n)
            for n in range(state_epoch - EPOCHS_PER_HISTORICAL_VECTOR +
                           1, state_epoch + 1)
        ]
        offset = EPOCHS_PER_HISTORICAL_VECTOR - epochs[
            0] % EPOCHS_PER_HISTORICAL_VECTOR
        epochs = epochs[offset:] + epochs[:offset]

        genesis_root = self._read_state_root_at_slot(Slot(0))
        genesis_randao_mix = Root(
            Hash32(self.db[SchemaV1.state_root_to_randao_mix(genesis_root)]))

        for epoch in epochs:
            if epoch < 0:
                yield genesis_randao_mix
            elif epoch == state_epoch:
                # yield the randao mix at the particular slot
                key = SchemaV1.state_root_to_randao_mix(state_root)
                yield Root(Hash32(self.db[key]))
            else:
                # yield the randao mix at the last slot in the epoch
                slot = Slot((epoch + 1) * SLOTS_PER_EPOCH - 1)
                if slot in non_finalized_state_roots:
                    root = non_finalized_state_roots[slot]
                else:
                    root = self._read_state_root_at_slot(slot)

                key = SchemaV1.state_root_to_randao_mix(root)
                yield Root(Hash32(self.db[key]))
Esempio n. 21
0
def _get_seed(
    state: "BeaconState",
    epoch: Epoch,
    domain_type: DomainType,
    randao_provider: RandaoProvider,
    epoch_provider: Callable[[Epoch], Hash32],
    committee_config: CommitteeConfig,
) -> Hash32:
    randao_mix = randao_provider(
        state,
        Epoch(epoch + committee_config.EPOCHS_PER_HISTORICAL_VECTOR -
              committee_config.MIN_SEED_LOOKAHEAD - 1),
        committee_config.EPOCHS_PER_HISTORICAL_VECTOR,
    )
    epoch_as_bytes = epoch_provider(epoch)

    return hash_eth2(domain_type + epoch_as_bytes + randao_mix)
Esempio n. 22
0
def initiate_exit_for_validator(validator: Validator, state: BeaconState,
                                config: Eth2Config) -> Validator:
    """
    Performs the mutations to ``validator`` used to initiate an exit.
    More convenient given our immutability patterns compared to ``initiate_validator_exit``.
    """
    if validator.exit_epoch != FAR_FUTURE_EPOCH:
        return validator

    churn_limit = get_validator_churn_limit(state, config)
    exit_queue_epoch = _compute_exit_queue_epoch(state, churn_limit, config)

    return validator.copy(
        exit_epoch=exit_queue_epoch,
        withdrawable_epoch=Epoch(exit_queue_epoch +
                                 config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY),
    )
Esempio n. 23
0
def validate_randao_reveal(randao_reveal: BLSSignature,
                           proposer_pubkey: BLSPubkey, epoch: Epoch,
                           fork: Fork) -> None:
    message_hash = Hash32(epoch.to_bytes(32, byteorder="little"))
    domain = get_domain(fork, epoch, SignatureDomain.DOMAIN_RANDAO)

    is_randao_reveal_valid = bls.verify(
        pubkey=proposer_pubkey,
        message_hash=message_hash,
        signature=randao_reveal,
        domain=domain,
    )

    if not is_randao_reveal_valid:
        raise ValidationError(
            f"RANDAO reveal is invalid. "
            f"reveal={randao_reveal}, proposer_pubkey={proposer_pubkey}, "
            f"message_hash={message_hash}, domain={domain}")
Esempio n. 24
0
def _decoder(
    # NOTE: mypy incorrectly thinks `Field` is a generic type
    data: Dict[str, EncodedConfigTypes],
    fields: Collection[Field],  # type: ignore
) -> Iterable[Tuple[str, ConfigTypes]]:
    # NOTE: this code is unwieldly but it satisfies `mypy`
    for field in fields:
        if field.type is Gwei:
            yield field.name, Gwei(cast(int, data[field.name]))
        elif field.type is Slot:
            yield field.name, Slot(cast(int, data[field.name]))
        elif field.type is Epoch:
            yield field.name, Epoch(cast(int, data[field.name]))
        elif field.type is Second:
            yield field.name, Second(cast(int, data[field.name]))
        elif field.type is bytes:
            yield field.name, decode_hex(cast(str, data[field.name]))
        else:
            yield field.name, int(data[field.name])
Esempio n. 25
0
def _get_seed(state: 'BeaconState', epoch: Epoch,
              randao_provider: RandaoProvider,
              active_index_root_provider: ActiveIndexRootProvider,
              epoch_provider: Callable[[Epoch], Hash32],
              committee_config: CommitteeConfig) -> Hash32:
    randao_mix = randao_provider(
        state,
        Epoch(epoch + committee_config.EPOCHS_PER_HISTORICAL_VECTOR -
              committee_config.MIN_SEED_LOOKAHEAD - 1),
        committee_config.EPOCHS_PER_HISTORICAL_VECTOR,
    )
    active_index_root = active_index_root_provider(
        state,
        epoch,
        committee_config.EPOCHS_PER_HISTORICAL_VECTOR,
    )
    epoch_as_bytes = epoch_provider(epoch)

    return hash_eth2(randao_mix + active_index_root + epoch_as_bytes)
Esempio n. 26
0
def _update_latest_active_index_roots(
        state: BeaconState, committee_config: CommitteeConfig) -> BeaconState:
    """
    Return the BeaconState with updated `latest_active_index_roots`.
    """
    next_epoch = state.next_epoch(committee_config.SLOTS_PER_EPOCH)

    # TODO: chanege to hash_tree_root
    active_validator_indices = get_active_validator_indices(
        state.validator_registry,
        Epoch(next_epoch + committee_config.ACTIVATION_EXIT_DELAY),
    )
    index_root = hash_eth2(b''.join(
        [index.to_bytes(32, 'little') for index in active_validator_indices]))

    latest_active_index_roots = update_tuple_item(
        state.latest_active_index_roots,
        ((next_epoch + committee_config.ACTIVATION_EXIT_DELAY) %
         committee_config.LATEST_ACTIVE_INDEX_ROOTS_LENGTH),
        index_root,
    )

    return state.copy(latest_active_index_roots=latest_active_index_roots, )
Esempio n. 27
0
def _update_latest_active_index_roots(
        state: BeaconState, committee_config: CommitteeConfig) -> BeaconState:
    """
    Return the BeaconState with updated `latest_active_index_roots`.
    """
    next_epoch = state.next_epoch(committee_config.SLOTS_PER_EPOCH)

    active_validator_indices = get_active_validator_indices(
        state.validator_registry,
        Epoch(next_epoch + committee_config.ACTIVATION_EXIT_DELAY),
    )
    index_root = ssz.hash_tree_root(
        active_validator_indices,
        ssz.sedes.List(ssz.uint64),
    )

    latest_active_index_roots = update_tuple_item(
        state.latest_active_index_roots,
        ((next_epoch + committee_config.ACTIVATION_EXIT_DELAY) %
         committee_config.LATEST_ACTIVE_INDEX_ROOTS_LENGTH),
        index_root,
    )

    return state.copy(latest_active_index_roots=latest_active_index_roots, )
Esempio n. 28
0
 def next_epoch(self, slots_per_epoch: int) -> Epoch:
     return Epoch(self.current_epoch(slots_per_epoch) + 1)
Esempio n. 29
0
 def _compute_epoch(self, slot: Slot) -> Epoch:
     return Epoch(slot // self._slots_per_epoch)
Esempio n. 30
0
def compute_epoch_at_slot(slot: Slot, slots_per_epoch: int) -> Epoch:
    return Epoch(slot // slots_per_epoch)