Exemple #1
0
        async def peer_action(peer: ws.WSChiaConnection):

            peer_info = peer.get_peer_info()
            version = peer.get_version()
            if peer_info is not None and version is not None:
                self.version_cache.append((peer_info.host, version))
            # Ask peer for peers
            response = await peer.request_peers(
                full_node_protocol.RequestPeers(), timeout=3)
            # Add peers to DB
            if isinstance(response, full_node_protocol.RespondPeers):
                self.peers_retrieved.append(response)
            peer_info = peer.get_peer_info()
            tries = 0
            got_peak = False
            while tries < 25:
                tries += 1
                if peer_info is None:
                    break
                if peer_info in self.with_peak:
                    got_peak = True
                    break
                await asyncio.sleep(0.1)
            if not got_peak and peer_info is not None and self.crawl_store is not None:
                await self.crawl_store.peer_connected_hostname(
                    peer_info.host, False)
            await peer.close()
Exemple #2
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 or self.server._local_type is NodeType.WALLET)
         and self.address_manager is not None
     ):
         msg = make_msg(ProtocolMessageTypes.request_peers, full_node_protocol.RequestPeers())
         await peer.send_message(msg)
Exemple #3
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 #4
0
 async def new_peak(self, request: full_node_protocol.NewPeak,
                    peer: ws.WSChiaConnection):
     try:
         peer_info = peer.get_peer_info()
         tls_version = peer.get_tls_version()
         if tls_version is None:
             tls_version = "unknown"
         if peer_info is None:
             return
         if request.height >= self.minimum_height:
             if self.crawl_store is not None:
                 await self.crawl_store.peer_connected_hostname(
                     peer_info.host, True, tls_version)
         self.with_peak.add(peer_info)
     except Exception as e:
         self.log.error(
             f"Exception: {e}. Traceback: {traceback.format_exc()}.")
Exemple #5
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
Exemple #6
0
    async def respond_peers_introducer(
            self, request: introducer_protocol.RespondPeersIntroducer,
            peer: WSChiaConnection):
        if self.wallet_node.wallet_peers is not None:
            await self.wallet_node.wallet_peers.respond_peers(
                request, peer.get_peer_info(), False)

        if peer is not None and peer.connection_type is NodeType.INTRODUCER:
            await peer.close()
Exemple #7
0
    async def respond_peers(self, request: full_node_protocol.RespondPeers,
                            peer: WSChiaConnection):
        if self.wallet_node.wallet_peers is None:
            return None

        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)

        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 is None:
             return None
         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)
async def add_dummy_connection(
        server: ChiaServer,
        dummy_port: int,
        type: NodeType = NodeType.FULL_NODE) -> 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(
        type,
        ws,
        server._port,
        log,
        True,
        False,
        self_hostname,
        incoming_queue,
        lambda x, y: x,
        peer_id,
        100,
        30,
    )
    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 #10
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=30)
            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."
                )
                return False
            except asyncio.TimeoutError:
                self.log.debug(f"Timeout error connecting to {url}")
                return False
            if ws is None:
                return False

            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)
            # the session has been adopted by the connection, don't close it at
            # the end of the function
            session = None
            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
        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}")
        finally:
            if session is not None:
                await session.close()

        return False
Exemple #11
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) and not is_in_network(
                        connection.peer_host, self.exempt_peer_networks):
                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