示例#1
0
    async def respond_signature(self, response: harvester_protocol.RespondSignature):
        """
        Receives a signature on a block header hash, which is required for submitting
        a block to the blockchain.
        """
        header_hash = response.message
        proof_of_space: bytes32 = self.header_hash_to_pos[header_hash]
        validates: bool = False
        for sk in self._get_private_keys():
            pk = sk.get_g1()
            if pk == response.farmer_pk:
                agg_pk = ProofOfSpace.generate_plot_public_key(response.local_pk, pk)
                assert agg_pk == proof_of_space.plot_public_key
                farmer_share = AugSchemeMPL.sign(sk, header_hash, agg_pk)
                agg_sig = AugSchemeMPL.aggregate(
                    [response.message_signature, farmer_share]
                )
                validates = AugSchemeMPL.verify(agg_pk, header_hash, agg_sig)

                if validates:
                    break
        assert validates

        pos_hash: bytes32 = proof_of_space.get_hash()

        request = farmer_protocol.HeaderSignature(pos_hash, header_hash, agg_sig)
        yield OutboundMessage(
            NodeType.FULL_NODE, Message("header_signature", request), Delivery.BROADCAST
        )
示例#2
0
    async def respond_header_signature(
        self, response: harvester_protocol.RespondHeaderSignature
    ):
        """
        Receives a signature on a block header hash, which is required for submitting
        a block to the blockchain.
        """
        header_hash: bytes32 = self.harvester_responses_header_hash[
            response.quality_string
        ]
        proof_of_space: bytes32 = self.harvester_responses_proofs[
            response.quality_string
        ]
        plot_pubkey = self.harvester_responses_proofs[
            response.quality_string
        ].plot_pubkey

        assert response.header_hash_signature.verify(
            [Util.hash256(header_hash)], [plot_pubkey]
        )

        pos_hash: bytes32 = proof_of_space.get_hash()

        request = farmer_protocol.HeaderSignature(
            pos_hash, header_hash, response.header_hash_signature
        )
        yield OutboundMessage(
            NodeType.FULL_NODE, Message("header_signature", request), Delivery.BROADCAST
        )
    async def request_peers(
        self,
        request: RequestPeers,
        peer: WSChiaConnection,
    ) -> Optional[Message]:
        max_peers = self.introducer.max_peers_to_send
        if self.introducer.server is None or self.introducer.server.introducer_peers is None:
            return None
        rawpeers = self.introducer.server.introducer_peers.get_peers(
            max_peers * 5, True, self.introducer.recent_peer_threshold)

        peers = []
        for r_peer in rawpeers:
            if r_peer.get_hash() not in self.introducer.vetted:
                continue
            if self.introducer.vetted[r_peer.get_hash()]:
                if r_peer.host == peer.peer_host and r_peer.port == peer.peer_server_port:
                    continue
                peer_without_timestamp = TimestampedPeerInfo(
                    r_peer.host,
                    r_peer.port,
                    uint64(0),
                )
                peers.append(peer_without_timestamp)

            if len(peers) >= max_peers:
                break

        self.introducer.log.info(f"Sending vetted {peers}")

        msg = Message("respond_peers", RespondPeers(peers))
        return msg
示例#4
0
    async def challenge_response(
        self, challenge_response: harvester_protocol.ChallengeResponse
    ):
        """
        This is a response from the harvester, for a NewChallenge. Here we check if the proof
        of space is sufficiently good, and if so, we ask for the whole proof.
        """

        if challenge_response.quality_string in self.harvester_responses_challenge:
            log.warning(
                f"Have already seen quality string {challenge_response.quality_string}"
            )
            return
        height: uint32 = self.challenge_to_height[challenge_response.challenge_hash]
        number_iters = await self._get_required_iters(
            challenge_response.challenge_hash,
            challenge_response.quality_string,
            challenge_response.plot_size,
        )

        if height < 1000:  # As the difficulty adjusts, don't fetch all qualities
            if challenge_response.challenge_hash not in self.challenge_to_best_iters:
                self.challenge_to_best_iters[
                    challenge_response.challenge_hash
                ] = number_iters
            elif (
                number_iters
                < self.challenge_to_best_iters[challenge_response.challenge_hash]
            ):
                self.challenge_to_best_iters[
                    challenge_response.challenge_hash
                ] = number_iters
            else:
                return
        estimate_secs: float = number_iters / self.proof_of_time_estimate_ips
        if challenge_response.challenge_hash not in self.challenge_to_estimates:
            self.challenge_to_estimates[challenge_response.challenge_hash] = []
        self.challenge_to_estimates[challenge_response.challenge_hash].append(
            estimate_secs
        )

        log.info(f"Estimate: {estimate_secs}, rate: {self.proof_of_time_estimate_ips}")
        if (
            estimate_secs < self.config["pool_share_threshold"]
            or estimate_secs < self.config["propagate_threshold"]
        ):
            self.harvester_responses_challenge[
                challenge_response.quality_string
            ] = challenge_response.challenge_hash
            request = harvester_protocol.RequestProofOfSpace(
                challenge_response.quality_string
            )

            yield OutboundMessage(
                NodeType.HARVESTER,
                Message("request_proof_of_space", request),
                Delivery.RESPOND,
            )

            self._state_changed("challenge")
示例#5
0
    async def request_header_signature(
            self, request: harvester_protocol.RequestHeaderSignature):
        """
        The farmer requests a signature on the header hash, for one of the proofs that we found.
        A signature is created on the header hash using the plot private key.
        """
        if request.quality_string not in self.challenge_hashes:
            return

        _, filename, _ = self.challenge_hashes[request.quality_string]

        plot_sk = PrivateKey.from_bytes(
            bytes.fromhex(self.plot_config["plots"][filename]["sk"]))
        header_hash_signature: PrependSignature = plot_sk.sign_prepend(
            request.header_hash)
        assert header_hash_signature.verify(
            [Util.hash256(request.header_hash)], [plot_sk.get_public_key()])

        response: harvester_protocol.RespondHeaderSignature = harvester_protocol.RespondHeaderSignature(
            request.quality_string,
            header_hash_signature,
        )
        yield OutboundMessage(
            NodeType.FARMER,
            Message("respond_header_signature", response),
            Delivery.RESPOND,
        )
示例#6
0
            async def api_call(full_message: Message,
                               connection: WSChiaConnection, task_id):
                start_time = time.time()
                try:
                    if self.received_message_callback is not None:
                        await self.received_message_callback(connection)
                    connection.log.info(
                        f"<- {ProtocolMessageTypes(full_message.type).name} from peer "
                        f"{connection.peer_node_id} {connection.peer_host}")
                    message_type: str = ProtocolMessageTypes(
                        full_message.type).name

                    f = getattr(self.api, message_type, None)

                    if f is None:
                        self.log.error(
                            f"Non existing function: {message_type}")
                        raise ProtocolError(Err.INVALID_PROTOCOL_MESSAGE,
                                            [message_type])

                    if not hasattr(f, "api_function"):
                        self.log.error(
                            f"Peer trying to call non api function {message_type}"
                        )
                        raise ProtocolError(Err.INVALID_PROTOCOL_MESSAGE,
                                            [message_type])

                    if hasattr(f, "peer_required"):
                        coroutine = f(full_message.data, connection)
                    else:
                        coroutine = f(full_message.data)
                    response: Optional[Message] = await asyncio.wait_for(
                        coroutine, timeout=300)
                    connection.log.debug(
                        f"Time taken to process {message_type} from {connection.peer_node_id} is "
                        f"{time.time() - start_time} seconds")

                    if response is not None:
                        response_message = Message(response.type,
                                                   response.data,
                                                   full_message.id)
                        await connection.reply_to_request(response_message)
                except Exception as e:
                    if self.connection_close_task is None:
                        tb = traceback.format_exc()
                        connection.log.error(
                            f"Exception: {e}, closing connection {connection.get_peer_info()}. {tb}"
                        )
                    else:
                        connection.log.debug(
                            f"Exception: {e} while closing connection")
                        pass
                    await connection.close()
                finally:
                    if task_id in self.api_tasks:
                        self.api_tasks.pop(task_id)
                    if task_id in self.tasks_from_peer[
                            connection.peer_node_id]:
                        self.tasks_from_peer[connection.peer_node_id].remove(
                            task_id)
示例#7
0
 async def _on_connect(self):
     # Sends a handshake to the harvester
     msg = harvester_protocol.HarvesterHandshake(
         self._get_public_keys(), self.pool_public_keys
     )
     yield OutboundMessage(
         NodeType.HARVESTER, Message("harvester_handshake", msg), Delivery.RESPOND
     )
     if self.current_weight in self.challenges:
         for posf in self.challenges[self.current_weight]:
             message = harvester_protocol.NewChallenge(posf.challenge_hash)
             yield OutboundMessage(
                 NodeType.HARVESTER,
                 Message("new_challenge", message),
                 Delivery.BROADCAST,
             )
示例#8
0
    async def respond_peers(
            self, request: introducer_protocol.RespondPeers
    ) -> OutboundMessageGenerator:
        """
        We have received a list of full node peers that we can connect to.
        """
        if self.server is None or self.wallet_state_manager is None:
            return
        conns = self.global_connections
        for peer in request.peer_list:
            conns.peers.add(peer)

        # Pseudo-message to close the connection
        yield OutboundMessage(NodeType.INTRODUCER, Message("", None),
                              Delivery.CLOSE)

        unconnected = conns.get_unconnected_peers(
            recent_threshold=self.config["recent_peer_threshold"])
        to_connect = unconnected[:self._num_needed_peers()]
        if not len(to_connect):
            return

        self.log.info(f"Trying to connect to peers: {to_connect}")
        tasks = []
        for peer in to_connect:
            tasks.append(
                asyncio.create_task(
                    self.server.start_client(peer, self._on_connect)))
        await asyncio.gather(*tasks)
示例#9
0
    async def new_proof_of_time(
        self, new_proof_of_time: peer_protocol.NewProofOfTime
    ) -> OutboundMessageGenerator:
        """
        A proof of time, received by a peer full node. If we have the rest of the block,
        we can complete it. Otherwise, we just verify and propagate the proof.
        """
        finish_block: bool = False
        propagate_proof: bool = False
        if self.store.get_unfinished_block(
            (
                new_proof_of_time.proof.challenge_hash,
                new_proof_of_time.proof.number_of_iterations,
            )
        ):

            finish_block = True
        elif new_proof_of_time.proof.is_valid(constants["DISCRIMINANT_SIZE_BITS"]):
            propagate_proof = True

        if finish_block:
            request = timelord_protocol.ProofOfTimeFinished(new_proof_of_time.proof)
            async for msg in self.proof_of_time_finished(request):
                yield msg
        if propagate_proof:
            yield OutboundMessage(
                NodeType.FULL_NODE,
                Message("new_proof_of_time", new_proof_of_time),
                Delivery.BROADCAST_TO_OTHERS,
            )
示例#10
0
 async def successful_handshake(self, connection):
     if connection.connection_type == NodeType.FULL_NODE:
         if connection.is_outbound:
             if self.full_node_peers_callback is not None:
                 self.full_node_peers_callback(
                     "mark_tried",
                     connection.get_peer_info(),
                 )
             if self.wallet_callback is not None:
                 self.wallet_callback(
                     "make_tried",
                     connection.get_peer_info(),
                 )
             if connection.is_feeler:
                 connection.close()
                 self.close(connection)
                 return
             # Request peers after handshake.
             if connection.local_type == NodeType.FULL_NODE:
                 await connection.send(Message("request_peers", ""))
         else:
             if self.full_node_peers_callback is not None:
                 self.full_node_peers_callback(
                     "new_inbound_connection",
                     connection.get_peer_info(),
                 )
     yield connection
示例#11
0
    async def challenge_response(
            self, challenge_response: harvester_protocol.ChallengeResponse):
        """
        This is a response from the harvester, for a NewChallenge. Here we check if the proof
        of space is sufficiently good, and if so, we ask for the whole proof.
        """

        if challenge_response.quality_string in self.harvester_responses_challenge:
            log.warning(
                f"Have already seen quality string {challenge_response.quality_string}"
            )
            return
        weight: uint128 = self.challenge_to_weight[
            challenge_response.challenge_hash]
        height: uint32 = self.challenge_to_height[
            challenge_response.challenge_hash]
        difficulty: uint64 = uint64(0)
        for posf in self.challenges[weight]:
            if posf.challenge_hash == challenge_response.challenge_hash:
                difficulty = posf.difficulty
        if difficulty == 0:
            raise RuntimeError("Did not find challenge")

        estimate_min = (self.proof_of_time_estimate_ips *
                        self.constants["BLOCK_TIME_TARGET"] /
                        self.constants["MIN_ITERS_PROPORTION"])
        number_iters: uint64 = calculate_iterations_quality(
            challenge_response.quality_string,
            challenge_response.plot_size,
            difficulty,
            estimate_min,
        )
        if height < 1000:  # As the difficulty adjusts, don't fetch all qualities
            if challenge_response.challenge_hash not in self.challenge_to_best_iters:
                self.challenge_to_best_iters[
                    challenge_response.challenge_hash] = number_iters
            elif (number_iters < self.challenge_to_best_iters[
                    challenge_response.challenge_hash]):
                self.challenge_to_best_iters[
                    challenge_response.challenge_hash] = number_iters
            else:
                return
        estimate_secs: float = number_iters / self.proof_of_time_estimate_ips

        log.info(
            f"Estimate: {estimate_secs}, rate: {self.proof_of_time_estimate_ips}"
        )
        if (estimate_secs < self.config["pool_share_threshold"]
                or estimate_secs < self.config["propagate_threshold"]):
            self.harvester_responses_challenge[
                challenge_response.
                quality_string] = challenge_response.challenge_hash
            request = harvester_protocol.RequestProofOfSpace(
                challenge_response.quality_string)

            yield OutboundMessage(
                NodeType.HARVESTER,
                Message("request_proof_of_space", request),
                Delivery.RESPOND,
            )
示例#12
0
        async def invoke(*args, **kwargs):
            timeout = 60
            if "timeout" in kwargs:
                timeout = kwargs["timeout"]
            attribute = getattr(class_for_type(self.connection_type), attr_name, None)
            if attribute is None:
                raise AttributeError(f"Node type {self.connection_type} does not have method {attr_name}")

            msg = Message(uint8(getattr(ProtocolMessageTypes, attr_name).value), args[0])
            request_start_t = time.time()
            result = await self.create_request(msg, timeout)
            self.log.debug(
                f"Time for request {attr_name}: {self.get_peer_info()} = {time.time() - request_start_t}, "
                f"None? {result is None}"
            )
            if result is not None:
                ret_attr = getattr(class_for_type(self.local_type), ProtocolMessageTypes(result.type).name, None)

                req_annotations = ret_attr.__annotations__
                req = None
                for key in req_annotations:
                    if key == "return" or key == "peer":
                        continue
                    else:
                        req = req_annotations[key]
                assert req is not None
                result = req.from_bytes(result.data)
            return result
示例#13
0
    async def request_peers_with_peer_info(
        self,
        request: RequestPeers,
        peer_info: PeerInfo,
    ) -> AsyncGenerator[OutboundMessage, None]:
        max_peers = self.max_peers_to_send
        if self.global_connections.introducer_peers is None:
            return
        rawpeers = self.global_connections.introducer_peers.get_peers(
            max_peers * 5, True, self.recent_peer_threshold
        )

        peers = []
        for peer in rawpeers:
            if peer.get_hash() not in self.vetted:
                continue
            if self.vetted[peer.get_hash()]:
                if peer.host == peer_info.host and peer.port == peer_info.port:
                    continue
                peer_without_timestamp = TimestampedPeerInfo(
                    peer.host,
                    peer.port,
                    uint64(0),
                )
                peers.append(peer_without_timestamp)

            if len(peers) >= max_peers:
                break

        log.info(f"Sending vetted {peers}")

        msg = Message("respond_peers", RespondPeers(peers))
        yield OutboundMessage(NodeType.FULL_NODE, msg, Delivery.RESPOND)
        yield OutboundMessage(NodeType.WALLET, msg, Delivery.RESPOND)
示例#14
0
    async def test_blocks_load(self, two_nodes):
        num_blocks = 100
        full_node_1, full_node_2, server_1, server_2 = two_nodes
        blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10)

        await server_2.start_client(
            PeerInfo("localhost", uint16(server_1._port)), None)

        await asyncio.sleep(2)  # Allow connections to get made

        start_unf = time.time()
        for i in range(1, num_blocks):
            msg = Message("respond_block",
                          full_node_protocol.RespondBlock(blocks[i]))
            server_1.push_message(
                OutboundMessage(NodeType.FULL_NODE, msg, Delivery.BROADCAST))

        while time.time() - start_unf < 100:
            if (max(
                [h.height for h in full_node_2.blockchain.get_current_tips()
                 ]) == num_blocks - 1):
                print(
                    f"Time taken to process {num_blocks} is {time.time() - start_unf}"
                )
                return
            await asyncio.sleep(0.1)

        raise Exception("Took too long to process blocks")
示例#15
0
    async def request_header_blocks(
        self, request: peer_protocol.RequestHeaderBlocks
    ) -> OutboundMessageGenerator:
        """
        A peer requests a list of header blocks, by height. Used for syncing or light clients.
        """
        start = time.time()
        if len(request.heights) > self.config["max_headers_to_send"]:
            raise errors.TooManyheadersRequested(
                f"The max number of headers is {self.config['max_headers_to_send']},\
                                                but requested {len(request.heights)}"
            )

        try:
            header_hashes: List[
                HeaderBlock
            ] = self.blockchain.get_header_hashes_by_height(
                request.heights, request.tip_header_hash
            )
            header_blocks: List[
                HeaderBlock
            ] = await self.store.get_header_blocks_by_hash(header_hashes)
            log.info(f"Got header blocks by height {time.time() - start}")
        except KeyError:
            return
        except BlockNotInBlockchain as e:
            log.info(f"{e}")
            return

        response = peer_protocol.HeaderBlocks(request.tip_header_hash, header_blocks)
        yield OutboundMessage(
            NodeType.FULL_NODE, Message("header_blocks", response), Delivery.RESPOND
        )
    async def farm_block(self, request):
        raw_puzzle_hash = decode_puzzle_hash(request["address"])
        request = FarmNewBlockProtocol(raw_puzzle_hash)
        msg = Message("farm_new_block", request)

        await self.service.server.send_to_all([msg], NodeType.FULL_NODE)
        return {}
示例#17
0
    async def proof_of_space_finalized(
            self,
            proof_of_space_finalized: farmer_protocol.ProofOfSpaceFinalized):
        """
        Full node notifies farmer that a proof of space has been completed. It gets added to the
        challenges list at that weight, and weight is updated if necessary
        """
        get_proofs: bool = False
        if (proof_of_space_finalized.weight >= self.current_weight
                and proof_of_space_finalized.challenge_hash
                not in self.seen_challenges):
            # Only get proofs for new challenges, at a current or new weight
            get_proofs = True
            if proof_of_space_finalized.weight > self.current_weight:
                self.current_weight = proof_of_space_finalized.weight

            # TODO: ask the pool for this information
            coinbase: CoinbaseInfo = CoinbaseInfo(
                uint32(proof_of_space_finalized.height + 1),
                calculate_block_reward(proof_of_space_finalized.height),
                bytes.fromhex(self.key_config["pool_target"]),
            )

            pool_sks: List[PrivateKey] = [
                PrivateKey.from_bytes(bytes.fromhex(ce))
                for ce in self.key_config["pool_sks"]
            ]
            coinbase_signature: PrependSignature = pool_sks[0].sign_prepend(
                bytes(coinbase))
            self.coinbase_rewards[uint32(proof_of_space_finalized.height +
                                         1)] = (
                                             coinbase,
                                             coinbase_signature,
                                         )

            log.info(f"\tCurrent weight set to {self.current_weight}")
        self.seen_challenges.add(proof_of_space_finalized.challenge_hash)
        if proof_of_space_finalized.weight not in self.challenges:
            self.challenges[proof_of_space_finalized.weight] = [
                proof_of_space_finalized
            ]
        else:
            self.challenges[proof_of_space_finalized.weight].append(
                proof_of_space_finalized)
        self.challenge_to_weight[
            proof_of_space_finalized.
            challenge_hash] = proof_of_space_finalized.weight
        self.challenge_to_height[
            proof_of_space_finalized.
            challenge_hash] = proof_of_space_finalized.height

        if get_proofs:
            message = harvester_protocol.NewChallenge(
                proof_of_space_finalized.challenge_hash)
            yield OutboundMessage(
                NodeType.HARVESTER,
                Message("new_challenge", message),
                Delivery.BROADCAST,
            )
示例#18
0
async def handle_message(
    triple: Tuple[ChiaConnection, Message, PeerConnections], api: Any
) -> AsyncGenerator[Tuple[ChiaConnection, OutboundMessage, PeerConnections],
                    None]:
    """
    Async generator which takes messages, parses, them, executes the right
    api function, and yields responses (to same connection, propagated, etc).
    """
    connection, full_message, global_connections = triple

    try:
        if len(full_message.function) == 0 or full_message.function.startswith(
                "_"):
            # This prevents remote calling of private methods that start with "_"
            raise ProtocolError(Err.INVALID_PROTOCOL_MESSAGE,
                                [full_message.function])

        connection.log.info(
            f"<- {full_message.function} from peer {connection.get_peername()}"
        )
        if full_message.function == "ping":
            ping_msg = Ping(full_message.data["nonce"])
            assert connection.connection_type
            outbound_message = OutboundMessage(
                connection.connection_type,
                Message("pong", Pong(ping_msg.nonce)),
                Delivery.RESPOND,
            )
            yield connection, outbound_message, global_connections
            return
        elif full_message.function == "pong":
            return

        f_with_peer_name = getattr(api,
                                   full_message.function + "_with_peer_name",
                                   None)

        if f_with_peer_name is not None:
            result = f_with_peer_name(full_message.data,
                                      connection.get_peername())
        else:
            f = getattr(api, full_message.function, None)

            if f is None:
                raise ProtocolError(Err.INVALID_PROTOCOL_MESSAGE,
                                    [full_message.function])

            result = f(full_message.data)

        if isinstance(result, AsyncGenerator):
            async for outbound_message in result:
                yield connection, outbound_message, global_connections
        else:
            await result
    except Exception:
        tb = traceback.format_exc()
        connection.log.error(f"Error, closing connection {connection}. {tb}")
        # TODO: Exception means peer gave us invalid information, so ban this peer.
        global_connections.close(connection)
示例#19
0
 async def read_one_message(self) -> Message:
     size = await self.reader.readexactly(LENGTH_BYTES)
     full_message_length = int.from_bytes(size, "big")
     full_message: bytes = await self.reader.readexactly(full_message_length)
     full_message_loaded: Any = cbor.loads(full_message)
     self.bytes_read += LENGTH_BYTES + full_message_length
     self.last_message_time = time.time()
     return Message(full_message_loaded["f"], full_message_loaded["d"])
    async def farm_block(self, websocket, request, response_api):
        puzzle_hash = bytes.fromhex(request["puzzle_hash"])
        request = FarmNewBlockProtocol(puzzle_hash)
        msg = OutboundMessage(
            NodeType.FULL_NODE, Message("farm_new_block", request), Delivery.BROADCAST,
        )

        self.wallet_node.server.push_message(msg)
示例#21
0
 async def request_peers(
     self, request: RequestPeers
 ) -> AsyncGenerator[OutboundMessage, None]:
     max_peers = self.config["max_peers_to_send"]
     peers = self.server.global_connections.peers.get_peers(
         max_peers, True, self.config["recent_peer_threshold"]
     )
     msg = Message("peers", Peers(peers))
     yield OutboundMessage(NodeType.FULL_NODE, msg, Delivery.RESPOND)
示例#22
0
 async def on_connect(self, peer: WSChiaConnection):
     # Sends a handshake to the harvester
     msg = harvester_protocol.HarvesterHandshake(
         self.get_public_keys(),
         self.pool_public_keys,
     )
     if peer.connection_type is NodeType.HARVESTER:
         msg = Message("harvester_handshake", msg)
         await peer.send_message(msg)
示例#23
0
    async def request_proof_of_space(
            self, request: harvester_protocol.RequestProofOfSpace):
        """
        The farmer requests a proof of space, for one of the plots.
        We look up the correct plot based on the plot id and response number, lookup the proof,
        and return it.
        """
        response: Optional[harvester_protocol.RespondProofOfSpace] = None
        challenge_hash = request.challenge_hash
        filename = Path(request.plot_id).resolve()
        index = request.response_number
        proof_xs: bytes
        plot_info = self.provers[filename]

        try:
            try:
                proof_xs = plot_info.prover.get_full_proof(
                    challenge_hash, index)
            except RuntimeError:
                prover = DiskProver(str(filename))
                self.provers[filename] = PlotInfo(
                    prover,
                    plot_info.pool_public_key,
                    plot_info.farmer_public_key,
                    plot_info.plot_public_key,
                    plot_info.local_sk,
                    plot_info.file_size,
                    plot_info.time_modified,
                )
                proof_xs = self.provers[filename].prover.get_full_proof(
                    challenge_hash, index)
        except KeyError:
            log.warning(f"KeyError plot {filename} does not exist.")

        plot_info = self.provers[filename]
        plot_public_key = ProofOfSpace.generate_plot_public_key(
            plot_info.local_sk.get_g1(), plot_info.farmer_public_key)

        proof_of_space: ProofOfSpace = ProofOfSpace(
            challenge_hash,
            plot_info.pool_public_key,
            plot_public_key,
            uint8(self.provers[filename].prover.get_size()),
            proof_xs,
        )
        response = harvester_protocol.RespondProofOfSpace(
            request.plot_id,
            request.response_number,
            proof_of_space,
        )
        if response:
            yield OutboundMessage(
                NodeType.FARMER,
                Message("respond_proof_of_space", response),
                Delivery.RESPOND,
            )
示例#24
0
    async def farm_block(self, request):
        puzzle_hash = request["puzzle_hash"]
        raw_puzzle_hash = decode_puzzle_hash(puzzle_hash)
        request = FarmNewBlockProtocol(raw_puzzle_hash)
        msg = OutboundMessage(
            NodeType.FULL_NODE, Message("farm_new_block", request), Delivery.BROADCAST,
        )

        self.service.server.push_message(msg)
        return {"success": True}
示例#25
0
 async def on_connect():
     # Sends a handshake to the harvester
     pool_sks: List[PrivateKey] = [
         PrivateKey.from_bytes(bytes.fromhex(ce))
         for ce in key_config["pool_sks"]
     ]
     msg = HarvesterHandshake([sk.get_public_key() for sk in pool_sks])
     yield OutboundMessage(NodeType.HARVESTER,
                           Message("harvester_handshake", msg),
                           Delivery.BROADCAST)
 async def reject_block(self, header_hash: bytes32,
                        node_id: bytes32) -> OutboundMessageGenerator:
     """
     A rejection was received from a peer, so we remove this peer and close the connection,
     since we assume this peer cannot help us sync up. All blocks are removed from the
     request set.
     """
     self.current_outbound_sets.pop(node_id, None)
     yield OutboundMessage(NodeType.FULL_NODE, Message("", None),
                           Delivery.CLOSE)
示例#27
0
 async def request_all_header_hashes(
     self, request: peer_protocol.RequestAllHeaderHashes
 ) -> OutboundMessageGenerator:
     try:
         header_hashes = self.blockchain.get_header_hashes(request.tip_header_hash)
         message = Message(
             "all_header_hashes", peer_protocol.AllHeaderHashes(header_hashes)
         )
         yield OutboundMessage(NodeType.FULL_NODE, message, Delivery.RESPOND)
     except ValueError:
         log.info("Do not have requested header hashes.")
示例#28
0
 async def request_block(
         self, request_block: peer_protocol.RequestBlock
 ) -> OutboundMessageGenerator:
     block: Optional[FullBlock] = await self.store.get_block(
         request_block.header_hash)
     if block is not None:
         yield OutboundMessage(
             NodeType.FULL_NODE,
             Message("block", peer_protocol.Block(block)),
             Delivery.RESPOND,
         )
示例#29
0
    async def test_unfinished_blocks_load(self, two_nodes):
        num_blocks = 10
        full_node_1, full_node_2, server_1, server_2 = two_nodes
        blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10)

        for i in range(1, num_blocks - 1):
            async for _ in full_node_1.block(peer_protocol.Block(blocks[i])):
                pass

        await server_2.start_client(
            PeerInfo(server_1._host, uint16(server_1._port)), None
        )

        await asyncio.sleep(2)  # Allow connections to get made

        num_unfinished_blocks = 1000
        start_unf = time.time()
        for i in range(num_unfinished_blocks):
            msg = Message("unfinished_block", peer_protocol.UnfinishedBlock(blocks[9]))
            server_1.push_message(
                OutboundMessage(NodeType.FULL_NODE, msg, Delivery.BROADCAST)
            )

        # Send the whole block ast the end so we can detect when the node is done
        block_msg = Message("block", peer_protocol.Block(blocks[9]))
        server_1.push_message(
            OutboundMessage(NodeType.FULL_NODE, block_msg, Delivery.BROADCAST)
        )

        while time.time() - start_unf < 100:
            if (
                max([h.height for h in full_node_2.blockchain.get_current_tips()])
                == num_blocks - 1
            ):
                print(
                    f"Time taken to process {num_unfinished_blocks} is {time.time() - start_unf}"
                )
                return
            await asyncio.sleep(0.1)

        raise Exception("Took too long to process blocks")
示例#30
0
    async def _read_one_message(self) -> Optional[Message]:
        try:
            message: WSMessage = await self.ws.receive(30)
        except asyncio.TimeoutError:
            # self.ws._closed if we didn't receive a ping / pong
            if self.ws._closed:
                asyncio.create_task(self.close())
                await asyncio.sleep(3)
                return None
            return None

        if self.connection_type is not None:
            connection_type_str = NodeType(self.connection_type).name.lower()
        else:
            connection_type_str = ""
        if message.type == WSMsgType.CLOSING:
            self.log.debug(
                f"Closing connection to {connection_type_str} {self.peer_host}:"
                f"{self.peer_server_port}/"
                f"{self.peer_port}")
            asyncio.create_task(self.close())
            await asyncio.sleep(3)
        elif message.type == WSMsgType.CLOSE:
            self.log.debug(
                f"Peer closed connection {connection_type_str} {self.peer_host}:"
                f"{self.peer_server_port}/"
                f"{self.peer_port}")
            asyncio.create_task(self.close())
            await asyncio.sleep(3)
        elif message.type == WSMsgType.CLOSED:
            if not self.closed:
                asyncio.create_task(self.close())
                await asyncio.sleep(3)
                return None
        elif message.type == WSMsgType.BINARY:
            data = message.data
            full_message_loaded: Message = Message.from_bytes(data)
            self.bytes_read += len(data)
            self.last_message_time = time.time()
            return full_message_loaded
        elif message.type == WSMsgType.ERROR:
            self.log.error(f"WebSocket Error: {message}")
            if message.data.code == WSCloseCode.MESSAGE_TOO_BIG:
                asyncio.create_task(self.close(300))
            else:
                asyncio.create_task(self.close())
            await asyncio.sleep(3)

        else:
            self.log.error(f"Unexpected WebSocket message type: {message}")
            asyncio.create_task(self.close())
            await asyncio.sleep(3)
        return None