Example #1
0
    async def _send_challenges_to_timelords(
            self,
            delivery: Delivery = Delivery.BROADCAST
    ) -> OutboundMessageGenerator:
        """
        Sends all of the current heads (as well as Pos infos) to all timelord peers.
        """
        challenge_requests: List[timelord_protocol.ChallengeStart] = []
        pos_info_requests: List[timelord_protocol.ProofOfSpaceInfo] = []
        async with self.store.lock:
            tips: List[HeaderBlock] = self.blockchain.get_current_tips()
            for tip in tips:
                assert tip.challenge
                challenge_hash = tip.challenge.get_hash()
                challenge_requests.append(
                    timelord_protocol.ChallengeStart(
                        challenge_hash, tip.challenge.total_weight))

            tip_hashes = [tip.header_hash for tip in tips]
            tip_infos = [
                tup[0] for tup in list((
                    await self.store.get_unfinished_blocks()).items())
                if tup[1].prev_header_hash in tip_hashes
            ]
            for chall, iters in tip_infos:
                pos_info_requests.append(
                    timelord_protocol.ProofOfSpaceInfo(chall, iters))
        for challenge_msg in challenge_requests:
            yield OutboundMessage(NodeType.TIMELORD,
                                  Message("challenge_start", challenge_msg),
                                  delivery)
        for pos_info_msg in pos_info_requests:
            yield OutboundMessage(
                NodeType.TIMELORD,
                Message("proof_of_space_info", pos_info_msg),
                delivery,
            )
Example #2
0
    async def unfinished_block(
        self, unfinished_block: peer_protocol.UnfinishedBlock
    ) -> OutboundMessageGenerator:
        """
        We have received an unfinished block, either created by us, or from another peer.
        We can validate it and if it's a good block, propagate it to other peers and
        timelords.
        """
        # Adds the unfinished block to seen, and check if it's seen before
        if self.store.seen_unfinished_block(
                unfinished_block.block.header_hash):
            return

        if not self.blockchain.is_child_of_head(unfinished_block.block):
            return

        if not await self.blockchain.validate_unfinished_block(
                unfinished_block.block):
            raise InvalidUnfinishedBlock()

        prev_full_block: Optional[FullBlock] = await self.store.get_block(
            unfinished_block.block.prev_header_hash)
        assert prev_full_block

        prev_block: HeaderBlock = prev_full_block.header_block

        assert prev_block.challenge

        challenge_hash: bytes32 = prev_block.challenge.get_hash()
        difficulty: uint64 = self.blockchain.get_next_difficulty(
            unfinished_block.block.header_block.prev_header_hash)
        vdf_ips: uint64 = self.blockchain.get_next_ips(
            unfinished_block.block.header_block.prev_header_hash)

        iterations_needed: uint64 = calculate_iterations(
            unfinished_block.block.header_block.proof_of_space,
            difficulty,
            vdf_ips,
            constants["MIN_BLOCK_TIME"],
        )

        if (await self.store.get_unfinished_block(
            (challenge_hash, iterations_needed)) is not None):
            return

        expected_time: uint64 = uint64(
            int(iterations_needed /
                (await self.store.get_proof_of_time_estimate_ips())))

        if expected_time > constants["PROPAGATION_DELAY_THRESHOLD"]:
            log.info(
                f"Block is slow, expected {expected_time} seconds, waiting")
            # If this block is slow, sleep to allow faster blocks to come out first
            await asyncio.sleep(5)

        async with self.store.lock:
            leader: Tuple[uint32,
                          uint64] = self.store.get_unfinished_block_leader()
            if leader is None or unfinished_block.block.height > leader[0]:
                log.info(
                    f"This is the first unfinished block at height {unfinished_block.block.height}, so propagate."
                )
                # If this is the first block we see at this height, propagate
                self.store.set_unfinished_block_leader(
                    (unfinished_block.block.height, expected_time))
            elif unfinished_block.block.height == leader[0]:
                if expected_time > leader[1] + constants[
                        "PROPAGATION_THRESHOLD"]:
                    # If VDF is expected to finish X seconds later than the best, don't propagate
                    log.info(
                        f"VDF will finish too late {expected_time} seconds, so don't propagate"
                    )
                    return
                elif expected_time < leader[1]:
                    log.info(
                        f"New best unfinished block at height {unfinished_block.block.height}"
                    )
                    # If this will be the first block to finalize, update our leader
                    self.store.set_unfinished_block_leader(
                        (leader[0], expected_time))
            else:
                # If we have seen an unfinished block at a greater or equal height, don't propagate
                log.info(f"Unfinished block at old height, so don't propagate")
                return

            await self.store.add_unfinished_block(
                (challenge_hash, iterations_needed), unfinished_block.block)

        timelord_request = timelord_protocol.ProofOfSpaceInfo(
            challenge_hash, iterations_needed)

        yield OutboundMessage(
            NodeType.TIMELORD,
            Message("proof_of_space_info", timelord_request),
            Delivery.BROADCAST,
        )
        yield OutboundMessage(
            NodeType.FULL_NODE,
            Message("unfinished_block", unfinished_block),
            Delivery.BROADCAST_TO_OTHERS,
        )