Esempio n. 1
0
    async def perform_handshake(
        self, network_id: bytes32, protocol_version: str, server_port: int, local_type: NodeType
    ):
        if self.is_outbound:
            outbound_handshake = make_msg(
                ProtocolMessageTypes.handshake,
                Handshake(
                    network_id,
                    protocol_version,
                    chia_full_version_str(),
                    uint16(server_port),
                    uint8(local_type.value),
                ),
            )
            payload: Optional[Payload] = Payload(outbound_handshake, None)
            assert payload is not None
            await self._send_message(payload)
            payload = await self._read_one_message()
            if payload is None:
                raise ProtocolError(Err.INVALID_HANDSHAKE)
            inbound_handshake = Handshake.from_bytes(payload.msg.data)
            if ProtocolMessageTypes(payload.msg.type) != ProtocolMessageTypes.handshake:
                raise ProtocolError(Err.INVALID_HANDSHAKE)
            if inbound_handshake.protocol_version != protocol_version:
                raise ProtocolError(Err.INCOMPATIBLE_PROTOCOL_VERSION)
            self.peer_server_port = inbound_handshake.server_port
            self.connection_type = NodeType(inbound_handshake.node_type)

        else:
            try:
                payload = await self._read_one_message()
            except Exception:
                raise ProtocolError(Err.INVALID_HANDSHAKE)

            if payload is None:
                raise ProtocolError(Err.INVALID_HANDSHAKE)
            inbound_handshake = Handshake.from_bytes(payload.msg.data)
            if ProtocolMessageTypes(payload.msg.type) != ProtocolMessageTypes.handshake:
                raise ProtocolError(Err.INVALID_HANDSHAKE)
            if inbound_handshake.protocol_version != protocol_version:
                raise ProtocolError(Err.INCOMPATIBLE_PROTOCOL_VERSION)
            outbound_handshake = make_msg(
                ProtocolMessageTypes.handshake,
                Handshake(
                    network_id,
                    protocol_version,
                    chia_full_version_str(),
                    uint16(server_port),
                    uint8(local_type.value),
                ),
            )
            payload = Payload(outbound_handshake, None)
            await self._send_message(payload)
            self.peer_server_port = inbound_handshake.server_port
            self.connection_type = NodeType(inbound_handshake.node_type)

        self.outbound_task = asyncio.create_task(self.outbound_handler())
        self.inbound_task = asyncio.create_task(self.inbound_handler())
        return True
    async def perform_handshake(self, network_id, protocol_version, node_id,
                                server_port, local_type):
        if self.is_outbound:
            outbound_handshake = Message(
                "handshake",
                Handshake(
                    network_id,
                    protocol_version,
                    node_id,
                    uint16(server_port),
                    local_type,
                ),
            )
            payload = Payload(outbound_handshake, None)
            await self._send_message(payload)
            payload = await self._read_one_message()
            inbound_handshake = Handshake(**payload.msg.data)
            if payload.msg.function != "handshake" or not inbound_handshake or not inbound_handshake.node_type:
                raise ProtocolError(Err.INVALID_HANDSHAKE)
            self.peer_node_id = inbound_handshake.node_id
            self.peer_server_port = int(inbound_handshake.server_port)
            self.connection_type = inbound_handshake.node_type

        else:
            payload = await self._read_one_message()
            inbound_handshake = Handshake(**payload.msg.data)
            if payload.msg.function != "handshake" or not inbound_handshake or not inbound_handshake.node_type:
                raise ProtocolError(Err.INVALID_HANDSHAKE)
            outbound_handshake = Message(
                "handshake",
                Handshake(
                    network_id,
                    protocol_version,
                    node_id,
                    uint16(server_port),
                    local_type,
                ),
            )
            payload = Payload(outbound_handshake, None)
            await self._send_message(payload)
            self.peer_node_id = inbound_handshake.node_id
            self.peer_server_port = int(inbound_handshake.server_port)
            self.connection_type = inbound_handshake.node_type

        if self.peer_node_id == node_id:
            raise ProtocolError(Err.SELF_CONNECTION)

        self.outbound_task = asyncio.create_task(self.outbound_handler())
        self.inbound_task = asyncio.create_task(self.inbound_handler())
        return True
    async def create_request(self, message: Message, timeout: int = 15):
        """ Sends a message and waits for a response. """
        if self.closed:
            return None

        event = asyncio.Event()
        payload = Payload(message, token_bytes(8))

        self.pending_requests[payload.id] = event
        await self.outgoing_queue.put(payload)

        async def time_out(req_id, req_timeout):
            await asyncio.sleep(req_timeout)
            if req_id in self.pending_requests:
                self.pending_requests[req_id].set()

        asyncio.create_task(time_out(payload.id, timeout))
        await event.wait()

        self.pending_requests.pop(payload.id)
        result: Optional[Message] = None
        if payload.id in self.request_results:
            result_payload: Payload = self.request_results[payload.id]
            result = result_payload.msg
            self.log.info(
                f"<- {result_payload.msg.function} from: {self.peer_host}:{self.peer_port}"
            )
            self.request_results.pop(payload.id)

        return result
Esempio n. 4
0
            async def api_call(payload: Payload, connection: WSChiaConnection,
                               task_id):
                start_time = time.time()
                try:
                    if self.received_message_callback is not None:
                        await self.received_message_callback(connection)
                    full_message = payload.msg
                    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:
                        payload_id = payload.id
                        response_payload = Payload(response, payload_id)
                        await connection.reply_to_request(response_payload)
                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)
Esempio n. 5
0
    async def _read_one_message(self) -> Optional[Payload]:
        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: Payload = Payload.from_bytes(data)
            self.bytes_read += len(data)
            self.last_message_time = time.time()
            return full_message_loaded
        else:
            self.log.error(f"Unexpected WebSocket message type: {message}")
            asyncio.create_task(self.close())
            await asyncio.sleep(3)
        return None
Esempio n. 6
0
    async def create_request(self, message: Message, timeout: int):
        """ Sends a message and waits for a response. """
        if self.closed:
            return None

        # We will wait for this event, it will be set either by the response, or the timeout
        event = asyncio.Event()

        # The request nonce is an integer between 0 and 2**16 - 1, which is used to match requests to responses
        request_id = self.request_nonce
        self.request_nonce = uint16(self.request_nonce + 1) if self.request_nonce != (2 ** 16 - 1) else uint16(0)

        payload = Payload(message, request_id)

        self.pending_requests[payload.id] = event
        await self.outgoing_queue.put(payload)

        # If the timeout passes, we set the event
        async def time_out(req_id, req_timeout):
            await asyncio.sleep(req_timeout)
            if req_id in self.pending_requests:
                self.pending_requests[req_id].set()

        asyncio.create_task(time_out(payload.id, timeout))
        await event.wait()

        self.pending_requests.pop(payload.id)
        result: Optional[Message] = None
        if payload.id in self.request_results:
            result_payload: Payload = self.request_results[payload.id]
            result = result_payload.msg
            self.log.info(
                f"<- {ProtocolMessageTypes(result_payload.msg.type).name} from: {self.peer_host}:{self.peer_port}"
            )
            self.request_results.pop(payload.id)

        return result
Esempio n. 7
0
 async def send_messages(self, messages: List[Message]):
     if self.closed:
         return
     for message in messages:
         payload = Payload(message, None)
         await self.outgoing_queue.put(payload)
Esempio n. 8
0
 async def send_message(self, message: Message):
     """ Send message sends a message with no tracking / callback. """
     if self.closed:
         return
     payload = Payload(message, None)
     await self.outgoing_queue.put(payload)