Exemple #1
0
 async def on_connect(self, peer: ws.WSChiaConnection):
     if (
         peer.is_outbound is False
         and peer.peer_server_port is not None
         and peer.connection_type is NodeType.FULL_NODE
         and self.server._local_type is NodeType.FULL_NODE
         and self.address_manager is not None
     ):
         timestamped_peer_info = TimestampedPeerInfo(
             peer.peer_host,
             peer.peer_server_port,
             uint64(int(time.time())),
         )
         await self.address_manager.add_to_new_table([timestamped_peer_info], peer.get_peer_info(), 0)
         if self.relay_queue is not None:
             self.relay_queue.put_nowait((timestamped_peer_info, 1))
     if (
         peer.is_outbound
         and peer.peer_server_port is not None
         and peer.connection_type is NodeType.FULL_NODE
         and self.server._local_type is NodeType.FULL_NODE
         and self.address_manager is not None
     ):
         msg = make_msg(ProtocolMessageTypes.request_peers, full_node_protocol.RequestPeers())
         await peer.send_message(msg)
Exemple #2
0
async def establish_connection(server: ChiaServer, dummy_port: int,
                               ssl_context) -> bool:
    timeout = aiohttp.ClientTimeout(total=10)
    session = aiohttp.ClientSession(timeout=timeout)
    try:
        incoming_queue: asyncio.Queue = asyncio.Queue()
        url = f"wss://{self_hostname}:{server._port}/ws"
        ws = await session.ws_connect(url,
                                      autoclose=False,
                                      autoping=True,
                                      ssl=ssl_context)
        wsc = WSChiaConnection(
            NodeType.FULL_NODE,
            ws,
            server._port,
            server.log,
            True,
            False,
            self_hostname,
            incoming_queue,
            lambda x, y: x,
            None,
            100,
            30,
        )
        handshake = await wsc.perform_handshake(server._network_id,
                                                protocol_version, dummy_port,
                                                NodeType.FULL_NODE)
        await session.close()
        return handshake
    except Exception:
        await session.close()
        return False
Exemple #3
0
    async def respond_peers(self, request: introducer_protocol.RespondPeers, peer: WSChiaConnection):
        if not self.wallet_node.has_full_node():
            await self.wallet_node.wallet_peers.respond_peers(request, peer.get_peer_info(), False)
        else:
            await self.wallet_node.wallet_peers.ensure_is_closed()

        if peer is not None and peer.connection_type is NodeType.INTRODUCER:
            await peer.close()
Exemple #4
0
 async def respond_peers(self, request: full_node_protocol.RespondPeers,
                         peer: WSChiaConnection):
     if not self.wallet_node.has_full_node():
         self.log.info(f"Wallet received {len(request.peer_list)} peers.")
         await self.wallet_node.wallet_peers.respond_peers(
             request, peer.get_peer_info(), True)
     else:
         self.log.info(
             f"Wallet received {len(request.peer_list)} peers, but ignoring, since we have a full node."
         )
         await self.wallet_node.wallet_peers.ensure_is_closed()
     return None
 async def update_peer_timestamp_on_message(self,
                                            peer: ws.WSChiaConnection):
     if (peer.is_outbound and peer.peer_server_port is not None
             and peer.connection_type is NodeType.FULL_NODE
             and self.server._local_type is NodeType.FULL_NODE
             and self.address_manager is not None):
         peer_info = peer.get_peer_info()
         if peer_info.host not in self.connection_time_pretest:
             self.connection_time_pretest[peer_info.host] = time.time()
         if time.time() - self.connection_time_pretest[
                 peer_info.host] > 600:
             self.connection_time_pretest[peer_info.host] = time.time()
             await self.address_manager.connect(peer_info)
Exemple #6
0
async def add_dummy_connection(
        server: ChiaServer, dummy_port: int) -> Tuple[asyncio.Queue, bytes32]:
    timeout = aiohttp.ClientTimeout(total=10)
    session = aiohttp.ClientSession(timeout=timeout)
    incoming_queue: asyncio.Queue = asyncio.Queue()
    dummy_crt_path = server._private_key_path.parent / "dummy.crt"
    dummy_key_path = server._private_key_path.parent / "dummy.key"
    generate_ca_signed_cert(server.chia_ca_crt_path.read_bytes(),
                            server.chia_ca_key_path.read_bytes(),
                            dummy_crt_path, dummy_key_path)
    ssl_context = ssl_context_for_client(server.chia_ca_crt_path,
                                         server.chia_ca_key_path,
                                         dummy_crt_path, dummy_key_path)
    pem_cert = x509.load_pem_x509_certificate(dummy_crt_path.read_bytes(),
                                              default_backend())
    der_cert = x509.load_der_x509_certificate(
        pem_cert.public_bytes(serialization.Encoding.DER), default_backend())
    peer_id = bytes32(der_cert.fingerprint(hashes.SHA256()))
    url = f"wss://{self_hostname}:{server._port}/ws"
    ws = await session.ws_connect(url,
                                  autoclose=True,
                                  autoping=True,
                                  ssl=ssl_context)
    wsc = WSChiaConnection(
        NodeType.FULL_NODE,
        ws,
        server._port,
        log,
        True,
        False,
        self_hostname,
        incoming_queue,
        lambda x: x,
        peer_id,
    )
    handshake = await wsc.perform_handshake(server._network_id,
                                            protocol_version, dummy_port,
                                            NodeType.FULL_NODE)
    assert handshake is True
    return incoming_queue, peer_id
Exemple #7
0
    async def start_client(
        self,
        target_node: PeerInfo,
        on_connect: Callable = None,
        auth: bool = False,
        is_feeler: bool = False,
    ) -> bool:
        """
        Tries to connect to the target node, adding one connection into the pipeline, if successful.
        An on connect method can also be specified, and this will be saved into the instance variables.
        """
        if self.is_duplicate_or_self_connection(target_node):
            return False

        if target_node.host in self.banned_peers and time.time(
        ) < self.banned_peers[target_node.host]:
            self.log.warning(
                f"Peer {target_node.host} is still banned, not connecting to it"
            )
            return False

        if auth:
            ssl_context = ssl_context_for_client(self.ca_private_crt_path,
                                                 self.ca_private_key_path,
                                                 self._private_cert_path,
                                                 self._private_key_path)
        else:
            ssl_context = ssl_context_for_client(self.chia_ca_crt_path,
                                                 self.chia_ca_key_path,
                                                 self.p2p_crt_path,
                                                 self.p2p_key_path)
        session = None
        connection: Optional[WSChiaConnection] = None
        try:
            timeout = ClientTimeout(total=10)
            session = ClientSession(timeout=timeout)

            try:
                if type(ip_address(target_node.host)) is IPv6Address:
                    target_node = PeerInfo(f"[{target_node.host}]",
                                           target_node.port)
            except ValueError:
                pass

            url = f"wss://{target_node.host}:{target_node.port}/ws"
            self.log.debug(f"Connecting: {url}, Peer info: {target_node}")
            try:
                ws = await session.ws_connect(url,
                                              autoclose=True,
                                              autoping=True,
                                              heartbeat=60,
                                              ssl=ssl_context,
                                              max_msg_size=50 * 1024 * 1024)
            except ServerDisconnectedError:
                self.log.debug(
                    f"Server disconnected error connecting to {url}. Perhaps we are banned by the peer."
                )
                await session.close()
                return False
            except asyncio.TimeoutError:
                self.log.debug(f"Timeout error connecting to {url}")
                await session.close()
                return False
            if ws is not None:
                assert ws._response.connection is not None and ws._response.connection.transport is not None
                transport = ws._response.connection.transport  # type: ignore
                cert_bytes = transport._ssl_protocol._extra[
                    "ssl_object"].getpeercert(True)  # type: ignore
                der_cert = x509.load_der_x509_certificate(
                    cert_bytes, default_backend())
                peer_id = bytes32(der_cert.fingerprint(hashes.SHA256()))
                if peer_id == self.node_id:
                    raise RuntimeError(
                        f"Trying to connect to a peer ({target_node}) with the same peer_id: {peer_id}"
                    )

                connection = WSChiaConnection(
                    self._local_type,
                    ws,
                    self._port,
                    self.log,
                    True,
                    False,
                    target_node.host,
                    self.incoming_messages,
                    self.connection_closed,
                    peer_id,
                    self._inbound_rate_limit_percent,
                    self._outbound_rate_limit_percent,
                    session=session,
                )
                handshake = await connection.perform_handshake(
                    self._network_id,
                    protocol_version,
                    self._port,
                    self._local_type,
                )
                assert handshake is True
                await self.connection_added(connection, on_connect)
                connection_type_str = ""
                if connection.connection_type is not None:
                    connection_type_str = connection.connection_type.name.lower(
                    )
                self.log.info(
                    f"Connected with {connection_type_str} {target_node}")
                if is_feeler:
                    asyncio.create_task(connection.close())
                return True
            else:
                await session.close()
                return False
        except client_exceptions.ClientConnectorError as e:
            self.log.info(f"{e}")
        except ProtocolError as e:
            if connection is not None:
                await connection.close(self.invalid_protocol_ban_seconds,
                                       WSCloseCode.PROTOCOL_ERROR, e.code)
            if e.code == Err.INVALID_HANDSHAKE:
                self.log.warning(
                    f"Invalid handshake with peer {target_node}. Maybe the peer is running old software."
                )
            elif e.code == Err.INCOMPATIBLE_NETWORK_ID:
                self.log.warning(
                    "Incompatible network ID. Maybe the peer is on another network"
                )
            elif e.code == Err.SELF_CONNECTION:
                pass
            else:
                error_stack = traceback.format_exc()
                self.log.error(
                    f"Exception {e}, exception Stack: {error_stack}")
        except Exception as e:
            if connection is not None:
                await connection.close(self.invalid_protocol_ban_seconds,
                                       WSCloseCode.PROTOCOL_ERROR, Err.UNKNOWN)
            error_stack = traceback.format_exc()
            self.log.error(f"Exception {e}, exception Stack: {error_stack}")

        if session is not None:
            await session.close()

        return False
Exemple #8
0
    async def incoming_connection(self, request):
        if request.remote in self.banned_peers and time.time(
        ) < self.banned_peers[request.remote]:
            self.log.warning(
                f"Peer {request.remote} is banned, refusing connection")
            return None
        ws = web.WebSocketResponse(max_msg_size=50 * 1024 * 1024)
        await ws.prepare(request)
        close_event = asyncio.Event()
        cert_bytes = request.transport._ssl_protocol._extra[
            "ssl_object"].getpeercert(True)
        der_cert = x509.load_der_x509_certificate(cert_bytes)
        peer_id = bytes32(der_cert.fingerprint(hashes.SHA256()))
        if peer_id == self.node_id:
            return ws
        connection: Optional[WSChiaConnection] = None
        try:
            connection = WSChiaConnection(
                self._local_type,
                ws,
                self._port,
                self.log,
                False,
                False,
                request.remote,
                self.incoming_messages,
                self.connection_closed,
                peer_id,
                self._inbound_rate_limit_percent,
                self._outbound_rate_limit_percent,
                close_event,
            )
            handshake = await connection.perform_handshake(
                self._network_id,
                protocol_version,
                self._port,
                self._local_type,
            )

            assert handshake is True
            # Limit inbound connections to config's specifications.
            if not self.accept_inbound_connections(connection.connection_type):
                self.log.info(
                    f"Not accepting inbound connection: {connection.get_peer_info()}.Inbound limit reached."
                )
                await connection.close()
                close_event.set()
            else:
                await self.connection_added(connection, self.on_connect)
                if self._local_type is NodeType.INTRODUCER and connection.connection_type is NodeType.FULL_NODE:
                    self.introducer_peers.add(connection.get_peer_info())
        except ProtocolError as e:
            if connection is not None:
                await connection.close(self.invalid_protocol_ban_seconds,
                                       WSCloseCode.PROTOCOL_ERROR, e.code)
            if e.code == Err.INVALID_HANDSHAKE:
                self.log.warning(
                    "Invalid handshake with peer. Maybe the peer is running old software."
                )
                close_event.set()
            elif e.code == Err.INCOMPATIBLE_NETWORK_ID:
                self.log.warning(
                    "Incompatible network ID. Maybe the peer is on another network"
                )
                close_event.set()
            elif e.code == Err.SELF_CONNECTION:
                close_event.set()
            else:
                error_stack = traceback.format_exc()
                self.log.error(
                    f"Exception {e}, exception Stack: {error_stack}")
                close_event.set()
        except Exception as e:
            if connection is not None:
                await connection.close(
                    ws_close_code=WSCloseCode.PROTOCOL_ERROR,
                    error=Err.UNKNOWN)
            error_stack = traceback.format_exc()
            self.log.error(f"Exception {e}, exception Stack: {error_stack}")
            close_event.set()

        await close_event.wait()
        return ws