示例#1
0
    async def _keep_ticking(self) -> None:
        """
        Ticker should tick twice in one slot:
        one for a new slot, one for the second half of an already ticked slot,
        e.g., if `seconds_per_slot` is `6`, for slot `49` it should tick once
        for the first 3 seconds and once for the last 3 seconds.
        """
        # `has_sent_second_half_slot_tick` is used to prevent another tick
        # for the second half of a ticked slot.
        has_sent_second_half_slot_tick = False
        while self.is_operational:
            elapsed_time = Second(int(time.time()) - self.genesis_time)
            if elapsed_time >= self.seconds_per_slot:
                slot = Slot(elapsed_time // self.seconds_per_slot +
                            self.genesis_slot)
                is_second_tick = ((elapsed_time % self.seconds_per_slot) >=
                                  (self.seconds_per_slot / 2))
                # Case 1: new slot
                if slot > self.latest_slot:
                    self.logger.debug(
                        bold_green("Tick  this_slot=%s elapsed=%s"),
                        slot,
                        elapsed_time,
                    )
                    self.latest_slot = slot
                    await self.event_bus.broadcast(
                        SlotTickEvent(
                            slot=slot,
                            elapsed_time=elapsed_time,
                            is_second_tick=is_second_tick,
                        ),
                        BroadcastConfig(internal=True),
                    )
                    has_sent_second_half_slot_tick = is_second_tick
                # Case 2: second half of an already ticked slot and it hasn't tick yet
                elif is_second_tick and not has_sent_second_half_slot_tick:
                    self.logger.debug(
                        bold_green("Tick  this_slot=%s (second-tick)"), slot)
                    await self.event_bus.broadcast(
                        SlotTickEvent(
                            slot=slot,
                            elapsed_time=elapsed_time,
                            is_second_tick=is_second_tick,
                        ),
                        BroadcastConfig(internal=True),
                    )
                    has_sent_second_half_slot_tick = True

            await asyncio.sleep(self.seconds_per_slot //
                                DEFAULT_CHECK_FREQUENCY)
示例#2
0
文件: validator.py 项目: veox/trinity
 async def _run(self) -> None:
     self.logger.info(
         bold_green("validating with indices %s"),
         sorted(tuple(self.validator_privkeys.keys()))
     )
     self.run_daemon_task(self.handle_slot_tick())
     await self.cancellation()
示例#3
0
 async def propose_block(self,
                         proposer_index: ValidatorIndex,
                         slot: Slot,
                         state: BeaconState,
                         state_machine: BaseBeaconStateMachine,
                         head_block: BaseBeaconBlock) -> BaseBeaconBlock:
     ready_attestations = self.get_ready_attestations(slot)
     block = self._make_proposing_block(
         proposer_index=proposer_index,
         slot=slot,
         state=state,
         state_machine=state_machine,
         parent_block=head_block,
         attestations=ready_attestations,
     )
     self.logger.debug(
         bold_green("validator %s is proposing a block %s with attestations %s"),
         proposer_index,
         block,
         block.body.attestations,
     )
     self.chain.import_block(block)
     self.logger.debug("broadcasting block %s", block)
     await self.p2p_node.broadcast_beacon_block(block)
     return block
示例#4
0
 def propose_block(self, proposer_index: ValidatorIndex, slot: Slot,
                   state: BeaconState,
                   state_machine: BaseBeaconStateMachine,
                   head_block: BaseBeaconBlock) -> BaseBeaconBlock:
     ready_attestations = self.get_ready_attestations()
     block = self._make_proposing_block(
         proposer_index=proposer_index,
         slot=slot,
         state=state,
         state_machine=state_machine,
         parent_block=head_block,
         attestations=ready_attestations,
     )
     self.logger.info(
         bold_green("Validator=%s proposing block=%s with attestations=%s"),
         proposer_index,
         block,
         block.body.attestations,
     )
     for peer in self.peer_pool.connected_nodes.values():
         peer = cast(BCCPeer, peer)
         self.logger.debug(bold_red("Sending block=%s to peer=%s"), block,
                           peer)
         peer.sub_proto.send_new_block(block)
     self.chain.import_block(block)
     return block
示例#5
0
 async def new_slot(self, slot: Slot) -> None:
     head = self.chain.get_canonical_head()
     state_machine = self.chain.get_state_machine()
     state = state_machine.state
     self.logger.debug(
         bold_green(
             f"head: slot={head.slot}, state root={head.state_root}"))
     proposer_index = _get_proposer_index(
         state,
         slot,
         state_machine.config,
     )
     if self.validator_index == proposer_index:
         self.propose_block(
             slot=slot,
             state=state,
             state_machine=state_machine,
             head_block=head,
         )
     else:
         self.skip_block(
             slot=slot,
             state=state,
             state_machine=state_machine,
         )
示例#6
0
 async def propose_or_skip_block(self, slot: Slot,
                                 is_second_tick: bool) -> None:
     head = self.chain.get_canonical_head()
     state_machine = self.chain.get_state_machine()
     state = state_machine.state
     self.logger.debug(
         bold_green(
             f"head: slot={head.slot}, state root={head.state_root.hex()}"))
     proposer_index = _get_proposer_index(
         state,
         slot,
         state_machine.config,
     )
     # Since it's expected to tick twice in one slot, `latest_proposed_epoch` is used to prevent
     # proposing twice in the same slot.
     has_proposed = slot_to_epoch(
         slot, self.slots_per_epoch) <= self.latest_proposed_epoch
     if not has_proposed and proposer_index in self.validator_privkeys:
         self.propose_block(
             proposer_index=proposer_index,
             slot=slot,
             state=state,
             state_machine=state_machine,
             head_block=head,
         )
         self.latest_proposed_epoch = slot_to_epoch(slot,
                                                    self.slots_per_epoch)
     # skip the block if it's second half of the slot and we are not proposing
     elif is_second_tick and proposer_index not in self.validator_privkeys:
         self.skip_block(
             slot=slot,
             state=state,
             state_machine=state_machine,
         )
示例#7
0
 async def _run(self) -> None:
     await self.event_bus.wait_until_serving()
     self.logger.info(
         bold_green("Validator service up  Handle indices=%s"),
         tuple(self.validator_privkeys.keys())
     )
     self.run_daemon_task(self.handle_slot_tick())
     await self.cancellation()
示例#8
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.message.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
示例#9
0
 async def handle_first_tick(self, slot: Slot) -> None:
     head = self.chain.get_canonical_head()
     state_machine = self.chain.get_state_machine()
     state = state_machine.state
     self.logger.debug(
         # Align with debug log below
         bold_green("Head       epoch=%s slot=%s state_root=%s"),
         state.current_epoch(self.slots_per_epoch),
         head.slot,
         encode_hex(head.state_root),
     )
     self.logger.debug(
         bold_green("Justified  epoch=%s root=%s  (current)"),
         state.current_justified_epoch,
         encode_hex(state.current_justified_root),
     )
     self.logger.debug(
         bold_green("Justified  epoch=%s root=%s  (previous)"),
         state.previous_justified_epoch,
         encode_hex(state.previous_justified_root),
     )
     self.logger.debug(
         bold_green("Finalized  epoch=%s root=%s"),
         state.finalized_epoch,
         encode_hex(state.finalized_root),
     )
     proposer_index = _get_proposer_index(
         state,
         slot,
         state_machine.config,
     )
     # `latest_proposed_epoch` is used to prevent validator from erraneously proposing twice
     # in the same epoch due to service crashing.
     epoch = slot_to_epoch(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:
             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
示例#10
0
 def propose_block(self, slot: Slot, state: BeaconState,
                   state_machine: BaseBeaconStateMachine,
                   head_block: BaseBeaconBlock) -> BaseBeaconBlock:
     block = self._make_proposing_block(slot, state, state_machine,
                                        head_block)
     self.logger.debug(bold_green(f"proposing block, block={block}"))
     for peer in self.peer_pool.connected_nodes.values():
         peer = cast(BCCPeer, peer)
         self.logger.debug(bold_red(f"sending block to peer={peer}"))
         peer.sub_proto.send_new_block(block)
     self.chain.import_block(block)
     return block
示例#11
0
 def skip_block(self, slot: Slot, state: BeaconState,
                state_machine: BaseBeaconStateMachine) -> Hash32:
     post_state = state_machine.state_transition.apply_state_transition_without_block(
         state,
         # TODO: Change back to `slot` instead of `slot + 1`.
         # Currently `apply_state_transition_without_block` only returns the post state
         # of `slot - 1`, so we increment it by one to get the post state of `slot`.
         cast(Slot, slot + 1),
     )
     self.logger.debug(
         bold_green(f"skipping block, post state={post_state.root}"))
     # FIXME: We might not need to persist state for skip slots since `create_block_on_state`
     # will run the state transition which also includes the state transition for skipped slots.
     self.chain.chaindb.persist_state(post_state)
     return post_state.root
示例#12
0
 def skip_block(self, slot: Slot, state: BeaconState,
                state_machine: BaseBeaconStateMachine) -> BeaconState:
     post_state = state_machine.state_transition.apply_state_transition(
         state,
         future_slot=slot,
     )
     self.logger.debug(
         bold_green("Skip block at slot=%s  post_state=%s"),
         slot,
         post_state,
     )
     # FIXME: We might not need to persist state for skip slots since `create_block_on_state`
     # will run the state transition which also includes the state transition for skipped slots.
     self.chain.chaindb.persist_state(post_state)
     return post_state
示例#13
0
 def skip_block(self, slot: Slot, state: BeaconState,
                state_machine: BaseBeaconStateMachine) -> BeaconState:
     """
     Forward state to the target ``slot`` and persist the state.
     """
     post_state = state_machine.state_transition.apply_state_transition(
         state, future_slot=slot)
     self.logger.debug(bold_green("Skip block at slot=%s  post_state=%s"),
                       slot, repr(post_state))
     # FIXME: We might not need to persist state for skip slots since `create_block_on_state`
     # will run the state transition which also includes the state transition for skipped slots.
     self.chain.chaindb.persist_state(post_state)
     self.chain.chaindb.update_head_state(post_state.slot,
                                          post_state.hash_tree_root)
     return post_state
示例#14
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,
     )
     proposer_index = _get_proposer_index(
         state.copy(slot=slot, ),
         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_of_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
示例#15
0
    def run_generate_testnet_dir(cls, args: Namespace,
                                 trinity_config: TrinityConfig) -> None:
        logger = cls.get_logger()
        logger.info("Generating testnet")
        network_dir = args.network_dir
        if len(os.listdir(network_dir)) > 0:
            logger.error(
                "This directory is not empty, won't create network files here."
            )
            sys.exit(1)

        clients = cls.generate_trinity_root_dirs(network_dir)
        keymap = cls.generate_keys(args.num, network_dir, clients)
        cls.generate_genesis_state(args.genesis_delay, network_dir, keymap,
                                   clients)

        logger.info(bold_green("Network generation completed"))
示例#16
0
    async def propose_block(
        self,
        proposer_index: ValidatorIndex,
        slot: Slot,
        state: BeaconState,
        state_machine: BaseBeaconStateMachine,
        head_block: BaseBeaconBlock,
    ) -> BaseBeaconBlock:
        """
        Propose a block and broadcast it.
        """
        eth1_vote = await self._get_eth1_vote(slot, state, state_machine)
        deposits = await self._get_deposit_data(state, state_machine,
                                                eth1_vote)
        # TODO(hwwhww): Check if need to aggregate and if they are overlapping.
        aggregated_attestations = self.get_ready_attestations(slot, True)
        unaggregated_attestations = self.get_ready_attestations(slot, False)
        ready_attestations = aggregated_attestations + unaggregated_attestations

        block = create_block_on_state(
            state=state,
            config=state_machine.config,
            state_machine=state_machine,
            signed_block_class=
            SerenitySignedBeaconBlock,  # TODO: Should get block class from slot
            parent_block=head_block,
            slot=slot,
            validator_index=proposer_index,
            privkey=self.validator_privkeys[proposer_index],
            attestations=ready_attestations,
            eth1_data=eth1_vote,
            deposits=deposits,
            check_proposer_index=False,
        )
        self.logger.debug(
            bold_green(
                "validator %s is proposing a block %s with attestations %s"),
            proposer_index,
            block,
            block.body.attestations,
        )
        self.chain.import_block(block)
        self.logger.debug("broadcasting block %s", block)
        await self.p2p_node.broadcast_beacon_block(block)
        metrics.validator_proposed_blocks.inc()
        return block
示例#17
0
    async def propose_block(
        self,
        proposer_index: ValidatorIndex,
        slot: Slot,
        state: BeaconState,
        state_machine: BaseBeaconStateMachine,
        head_block: BaseBeaconBlock,
    ) -> BaseBeaconBlock:
        """
        Propose a block and broadcast it.
        """
        eth1_vote = await self._get_eth1_vote(slot, state, state_machine)
        # deposits = await self._get_deposit_data(state, state_machine, eth1_vote)
        # TODO(hwwhww): Check if need to aggregate and if they are overlapping.
        aggregated_attestations = self.get_ready_attestations(slot, True)
        unaggregated_attestations = self.get_ready_attestations(slot, False)
        ready_attestations = aggregated_attestations + unaggregated_attestations
        private_key = self.validator_privkeys[proposer_index]
        randao_reveal = generate_randao_reveal(
            private_key, slot, state, state_machine.config
        )

        block = create_block_proposal(
            slot,
            head_block.message.hash_tree_root,
            randao_reveal,
            eth1_vote,
            ready_attestations,
            state,
            state_machine,
        )

        block = sign_block(state, block, private_key, self.slots_per_epoch)
        self.logger.debug(
            bold_green("validator %s is proposing a block %s with attestations %s"),
            proposer_index,
            block,
            block.body.attestations,
        )
        self.chain.import_block(block)
        self.logger.debug("broadcasting block %s", block)
        await self.p2p_node.broadcast_beacon_block(block)
        metrics.validator_proposed_blocks.inc()
        return block
示例#18
0
 async def _keep_ticking(self) -> None:
     while self.is_operational:
         elapsed_time = Second(int(time.time()) - self.genesis_time)
         if elapsed_time >= self.seconds_per_slot:
             slot = Slot(elapsed_time // self.seconds_per_slot +
                         self.genesis_slot)
             if slot > self.latest_slot:
                 self.logger.debug(
                     bold_green(
                         f"New slot: {slot}\tElapsed time: {elapsed_time}"))
                 self.latest_slot = slot
                 self.event_bus.broadcast(
                     NewSlotEvent(
                         slot=slot,
                         elapsed_time=elapsed_time,
                     ),
                     BroadcastConfig(internal=True),
                 )
         await asyncio.sleep(self.seconds_per_slot //
                             DEFAULT_CHECK_FREQUENCY)
示例#19
0
    async def attest(self, slot: Slot) -> Tuple[Attestation, ...]:
        """
        Attest the block at the given ``slot`` and broadcast them.
        """
        result_attestations: Tuple[Attestation, ...] = ()
        head = self.chain.get_canonical_head()
        state_machine = self.chain.get_state_machine()
        state = self.chain.get_head_state()
        epoch = compute_epoch_at_slot(slot, self.slots_per_epoch)

        attesting_committee_assignments_at_slot = self._get_attesting_assignments_at_slot(
            slot)

        for committee_assignment in attesting_committee_assignments_at_slot:
            committee_index = committee_assignment.committee_index
            committee = committee_assignment.committee

            attesting_validators_indices = tuple(
                filter(
                    lambda attester: self.latest_attested_epoch[attester] <
                    epoch,
                    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 attesting_validators_indices
            }
            attestations = create_signed_attestations_at_slot(
                state,
                state_machine.config,
                state_machine,
                slot,
                head.message.hash_tree_root,
                attesting_validator_privkeys,
                committee,
                committee_index,
                tuple(
                    CommitteeValidatorIndex(committee.index(index))
                    for index in attesting_validators_indices),
            )
            self.logger.debug(
                bold_green(
                    "validators %s attesting to block %s with attestation %s"),
                attesting_validators_indices,
                head,
                attestations,
            )

            # await self.p2p_node.broadcast_attestation(attestation)
            subnet_id = SubnetId(committee_index % ATTESTATION_SUBNET_COUNT)

            # Import attestation to pool and then broadcast it
            for attestation in attestations:
                self.import_attestation(attestation, False)
                await self.p2p_node.broadcast_attestation_to_subnet(
                    attestation, subnet_id)

            # Log the last epoch that the validator attested
            for index in attesting_validators_indices:
                self.latest_attested_epoch[index] = epoch

            metrics.validator_sent_attestation.inc()

            result_attestations = result_attestations + attestations
        # TODO: Aggregate attestations

        return result_attestations
示例#20
0
 async def _run(self) -> None:
     await self.event_bus.wait_until_serving()
     self.logger.debug(bold_green("validator running!!!"))
     self.run_daemon_task(self.handle_slot_tick())
     await self.cancellation()
示例#21
0
    async def attest(self, slot: Slot) -> Tuple[Attestation, ...]:
        attestations: Tuple[Attestation, ...] = ()
        head = self.chain.get_canonical_head()
        state_machine = self.chain.get_state_machine()
        state = self.chain.get_head_state()
        epoch = compute_epoch_of_slot(slot, self.slots_per_epoch)

        validator_assignments = {
            validator_index: self._get_this_epoch_assignment(
                validator_index,
                epoch,
            )
            for validator_index in self.validator_privkeys
        }
        attesting_validators = self._get_attesting_validator_and_shard(
            validator_assignments,
            slot,
            epoch,
        )
        if len(attesting_validators) == 0:
            return ()

        # Sort the attesting validators by shard
        sorted_attesting_validators = sorted(
            attesting_validators,
            key=itemgetter(1),
        )
        # Group the attesting validators by shard
        attesting_validators_groups = groupby(
            sorted_attesting_validators,
            key=itemgetter(1),
        )
        for shard, group in attesting_validators_groups:
            # Get the validator_index -> privkey map of the attesting validators
            attesting_validator_privkeys = {
                attesting_data[0]: self.validator_privkeys[attesting_data[0]]
                for attesting_data in group
            }
            attesting_validators_indices = tuple(
                attesting_validator_privkeys.keys())
            # Get one of the attesting validator's assignment in order to get the committee info
            assignment = self._get_this_epoch_assignment(
                attesting_validators_indices[0],
                epoch,
            )
            attestation = create_signed_attestation_at_slot(
                state,
                state_machine.config,
                state_machine,
                slot,
                head.signing_root,
                attesting_validator_privkeys,
                assignment.committee,
                shard,
            )
            self.logger.debug(
                bold_green("Validators=%s attest to block=%s  attestation=%s"),
                attesting_validators_indices,
                head,
                attestation,
            )
            for validator_index in attesting_validators_indices:
                self.latest_attested_epoch[validator_index] = epoch
            attestations = attestations + (attestation, )

        self.logger.debug("Brodcasting attestations %s", attestations)
        await self.p2p_node.broadcast_attestations(attestations)
        return attestations
示例#22
0
    async def _run(self) -> None:
        self.logger.info(bold_green("ValidatorHandler is running"))
        self.run_daemon_task(self.handle_get_block_requests())

        await self.cancellation()
    async def _run(self) -> None:
        self.logger.info(bold_green("ChainMaintainer is running"))
        self.run_daemon_task(self.handle_slot_tick())

        await self.cancellation()