From 656bd8f687210684f46a8454f79b112966b953f7 Mon Sep 17 00:00:00 2001 From: Jean Demeusy Date: Mon, 4 Mar 2024 16:25:10 +0100 Subject: [PATCH 1/8] added timeout on api calls --- .gitignore | 5 +- ct-app/core/components/hoprd_api.py | 101 ++++++++++++++++------------ 2 files changed, 61 insertions(+), 45 deletions(-) diff --git a/.gitignore b/.gitignore index e5ca0339..ad1189ad 100755 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ simulation.env .vscode/ *.env *.log - +*_rules.yaml # logs net_viz-*.png @@ -23,5 +23,4 @@ foo*.* foo*/ run_*.sh -.DS_Store -*/test_endurance_rules.yaml +.DS_Store \ No newline at end of file diff --git a/ct-app/core/components/hoprd_api.py b/ct-app/core/components/hoprd_api.py index 0cb7b5c6..fc074ba5 100644 --- a/ct-app/core/components/hoprd_api.py +++ b/ct-app/core/components/hoprd_api.py @@ -1,3 +1,4 @@ +import asyncio from typing import Callable, Optional from hoprd_sdk import ApiClient, Configuration @@ -40,38 +41,44 @@ def _refresh_token_hook(self): def print_prefix(self) -> str: return "api" - def __call_api( - self, obj: Callable[..., object], method: str, *args, **kwargs + async def __call_api( + self, + obj: Callable[..., object], + method: str, + timeout: int = 60, + *args, + **kwargs, ) -> tuple[bool, Optional[object]]: self.debug( f"Calling {obj.__name__}.{method} with kwargs: {kwargs}, args: {args}" ) - try: - with ApiClient(self.configuration) as client: - api_callback = getattr(obj(client), method) - kwargs["async_req"] = True - thread = api_callback(*args, **kwargs) - response = thread.get() - - except ApiException as e: - self.error( - f"ApiException calling {obj.__name__}.{method} " - + f"with kwargs: {kwargs}, args: {args}, error is: {e}" - ) - except OSError: - self.error( - f"OSError calling {obj.__name__}.{method} " - + f"with kwargs: {kwargs}, args: {args}:" - ) - except MaxRetryError: - self.error( - f"MaxRetryError calling {obj.__name__}.{method} " - + f"with kwargs: {kwargs}, args: {args}" - ) - else: - return (True, response) + async with asyncio.timeout(timeout): + try: + with ApiClient(self.configuration) as client: + api_callback = getattr(obj(client), method) + kwargs["async_req"] = True + thread = api_callback(*args, **kwargs) + response = thread.get() + + except ApiException as e: + self.error( + f"ApiException calling {obj.__name__}.{method} " + + f"with kwargs: {kwargs}, args: {args}, error is: {e}" + ) + except OSError: + self.error( + f"OSError calling {obj.__name__}.{method} " + + f"with kwargs: {kwargs}, args: {args}:" + ) + except MaxRetryError: + self.error( + f"MaxRetryError calling {obj.__name__}.{method} " + + f"with kwargs: {kwargs}, args: {args}" + ) + else: + return (True, response) - return (False, None) + return (False, None) async def balances(self, type: str or list[str] = "all"): """ @@ -86,7 +93,7 @@ async def balances(self, type: str or list[str] = "all"): elif isinstance(type, str): type = [type] - is_ok, response = self.__call_api(AccountApi, "account_get_balances") + is_ok, response = await self.__call_api(AccountApi, "account_get_balances") if not is_ok: return None @@ -110,7 +117,7 @@ async def open_channel(self, peer_address: str, amount: str): """ body = ChannelsBody(peer_address, amount) - is_ok, response = self.__call_api( + is_ok, response = await self.__call_api( ChannelsApi, "channels_open_channel", body=body ) return response.channel_id if is_ok else None @@ -123,7 +130,7 @@ async def fund_channel(self, channel_id: str, amount: str): :return: bool """ body = ChannelidFundBody(amount=f"{amount:.0f}") - is_ok, _ = self.__call_api( + is_ok, _ = await self.__call_api( ChannelsApi, "channels_fund_channel", channel_id, body=body ) return is_ok @@ -134,7 +141,9 @@ async def close_channel(self, channel_id: str): :param: channel_id: str :return: bool """ - is_ok, _ = self.__call_api(ChannelsApi, "channels_close_channel", channel_id) + is_ok, _ = await self.__call_api( + ChannelsApi, "channels_close_channel", channel_id + ) return is_ok async def incoming_channels(self, only_id: bool = False) -> list: @@ -143,7 +152,7 @@ async def incoming_channels(self, only_id: bool = False) -> list: :return: channels: list """ - is_ok, response = self.__call_api( + is_ok, response = await self.__call_api( ChannelsApi, "channels_get_channels", full_topology="false", @@ -170,7 +179,7 @@ async def outgoing_channels(self, only_id: bool = False): Returns all open outgoing channels. :return: channels: list """ - is_ok, response = self.__call_api(ChannelsApi, "channels_get_channels") + is_ok, response = await self.__call_api(ChannelsApi, "channels_get_channels") if is_ok: if not hasattr(response, "outgoing"): self.warning("Response does not contain 'outgoing'") @@ -193,7 +202,9 @@ async def get_channel(self, channel_id: str): :param: channel_id: str :return: channel: response """ - _, response = self.__call_api(ChannelsApi, "channels_get_channel", channel_id) + _, response = await self.__call_api( + ChannelsApi, "channels_get_channel", channel_id + ) return response async def all_channels(self, include_closed: bool): @@ -202,7 +213,7 @@ async def all_channels(self, include_closed: bool): :param: include_closed: bool :return: channels: list """ - is_ok, response = self.__call_api( + is_ok, response = await self.__call_api( ChannelsApi, "channels_get_channels", full_topology="true", @@ -216,7 +227,7 @@ async def ping(self, peer_id: str): :param: peer_id: str :return: response: dict """ - _, response = self.__call_api(PeersApi, "peers_ping_peer", peer_id) + _, response = await self.__call_api(PeersApi, "peers_ping_peer", peer_id) return response async def peers( @@ -233,7 +244,9 @@ async def peers( :return: peers: list """ - is_ok, response = self.__call_api(NodeApi, "node_get_peers", quality=quality) + is_ok, response = await self.__call_api( + NodeApi, "node_get_peers", quality=quality + ) if not is_ok: return [] @@ -272,7 +285,7 @@ async def get_address( elif isinstance(address, str): address = [address] - is_ok, response = self.__call_api(AccountApi, "account_get_address") + is_ok, response = await self.__call_api(AccountApi, "account_get_address") if not is_ok: return None @@ -298,7 +311,9 @@ async def send_message( :return: bool """ body = MessagesBody(tag, message, destination, path=hops) - is_ok, _ = self.__call_api(MessagesApi, "messages_send_message", body=body) + is_ok, _ = await self.__call_api( + MessagesApi, "messages_send_message", body=body + ) return is_ok async def messages_pop(self, tag: int = MESSAGE_TAG) -> bool: @@ -309,7 +324,9 @@ async def messages_pop(self, tag: int = MESSAGE_TAG) -> bool: """ body = MessagesPopBody(tag=tag) - _, response = self.__call_api(MessagesApi, "messages_pop_message", body=body) + _, response = await self.__call_api( + MessagesApi, "messages_pop_message", body=body + ) return response async def messages_pop_all(self, tag: int = MESSAGE_TAG) -> list: @@ -320,13 +337,13 @@ async def messages_pop_all(self, tag: int = MESSAGE_TAG) -> list: """ body = MessagesPopallBody(tag=tag) - _, response = self.__call_api( + _, response = await self.__call_api( MessagesApi, "messages_pop_all_message", body=body ) return response.messages if hasattr(response, "messages") else [] async def node_info(self): - _, response = self.__call_api(NodeApi, "node_get_info") + _, response = await self.__call_api(NodeApi, "node_get_info") return response async def channel_balance(self, src_peer_id: str, dest_peer_id: str) -> float: From c9625e654b13ad9179a4d26b8528ac105ce05bb9 Mon Sep 17 00:00:00 2001 From: Jean Demeusy Date: Mon, 4 Mar 2024 16:29:03 +0100 Subject: [PATCH 2/8] ensured all api calls are behind "connectguard" decorator --- ct-app/core/components/hoprd_api.py | 10 +++++----- ct-app/core/node.py | 6 +----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/ct-app/core/components/hoprd_api.py b/ct-app/core/components/hoprd_api.py index fc074ba5..37ccb2a6 100644 --- a/ct-app/core/components/hoprd_api.py +++ b/ct-app/core/components/hoprd_api.py @@ -78,9 +78,9 @@ async def __call_api( else: return (True, response) - return (False, None) + return (False, None) - async def balances(self, type: str or list[str] = "all"): + async def balances(self, type: str | list[str] = "all"): """ Returns the balance of the node. :param: type: str = "all" | "hopr" | "native" | "safe_native" | "safe_hopr" @@ -232,7 +232,7 @@ async def ping(self, peer_id: str): async def peers( self, - params: list or str = "peer_id", + params: list | str = "peer_id", status: str = "connected", quality: float = 0.5, ): @@ -271,8 +271,8 @@ async def peers( return output_list async def get_address( - self, address: str or list[str] = "hopr" - ) -> Optional[dict[str, str]] or Optional[str]: + self, address: str | list[str] = "hopr" + ) -> Optional[dict[str, str]] | Optional[str]: """ Returns the address of the node. :param: address: str = "hopr" | "native" diff --git a/ct-app/core/node.py b/ct-app/core/node.py index ce799c7f..62664606 100644 --- a/ct-app/core/node.py +++ b/ct-app/core/node.py @@ -76,10 +76,6 @@ def __init__(self, url: str, key: str): self.started = False - @property - async def balance(self) -> dict[str, int]: - return await self.api.balances() - @property def print_prefix(self): return ".".join(self.url.split("//")[-1].split(".")[:2]) @@ -111,7 +107,7 @@ async def healthcheck(self): @formalin("Retrieving balances") @connectguard async def retrieve_balances(self): - for token, balance in (await self.balance).items(): + for token, balance in (await self.api.balances()).items(): BALANCE.labels(self.address.id, token).set(balance) @flagguard From fd83c3bed6a3b99b5fbdbbff1b7b5347f127178f Mon Sep 17 00:00:00 2001 From: Jean Demeusy Date: Mon, 4 Mar 2024 16:46:06 +0100 Subject: [PATCH 3/8] fix timeout --- ct-app/core/components/hoprd_api.py | 31 ++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/ct-app/core/components/hoprd_api.py b/ct-app/core/components/hoprd_api.py index 37ccb2a6..d9e4a682 100644 --- a/ct-app/core/components/hoprd_api.py +++ b/ct-app/core/components/hoprd_api.py @@ -1,5 +1,5 @@ import asyncio -from typing import Callable, Optional +from typing import Callable, Optional, Union from hoprd_sdk import ApiClient, Configuration from hoprd_sdk.api import ( @@ -52,7 +52,13 @@ async def __call_api( self.debug( f"Calling {obj.__name__}.{method} with kwargs: {kwargs}, args: {args}" ) - async with asyncio.timeout(timeout): + + async def call( + obj: Callable[..., object], + method: str, + *args, + **kwargs, + ): try: with ApiClient(self.configuration) as client: api_callback = getattr(obj(client), method) @@ -78,9 +84,20 @@ async def __call_api( else: return (True, response) - return (False, None) + return (False, None) + + try: + return await asyncio.wait_for( + asyncio.create_task(call(obj, method, *args, **kwargs)), timeout=timeout + ) + except asyncio.TimeoutError: + self.error( + f"TimeoutError calling {obj.__name__}.{method} " + + f"with kwargs: {kwargs}, args: {args}" + ) + return (False, None) - async def balances(self, type: str | list[str] = "all"): + async def balances(self, type: Union[str, list[str]] = "all"): """ Returns the balance of the node. :param: type: str = "all" | "hopr" | "native" | "safe_native" | "safe_hopr" @@ -232,7 +249,7 @@ async def ping(self, peer_id: str): async def peers( self, - params: list | str = "peer_id", + params: Union[list, str] = "peer_id", status: str = "connected", quality: float = 0.5, ): @@ -271,8 +288,8 @@ async def peers( return output_list async def get_address( - self, address: str | list[str] = "hopr" - ) -> Optional[dict[str, str]] | Optional[str]: + self, address: Union[str, list[str]] = "hopr" + ) -> Optional[Union[dict[str, str], str]]: """ Returns the address of the node. :param: address: str = "hopr" | "native" From ed2d4c411c16512b416a994c7dee9d6e07b877d1 Mon Sep 17 00:00:00 2001 From: Jean Demeusy Date: Mon, 4 Mar 2024 16:50:38 +0100 Subject: [PATCH 4/8] Catching any other exception from API --- ct-app/core/components/hoprd_api.py | 5 +++++ final_waitlist.xlsx | Bin 0 -> 7652 bytes registry.xlsx | Bin 0 -> 8350 bytes waitlist.xlsx | Bin 0 -> 21344 bytes 4 files changed, 5 insertions(+) create mode 100644 final_waitlist.xlsx create mode 100644 registry.xlsx create mode 100644 waitlist.xlsx diff --git a/ct-app/core/components/hoprd_api.py b/ct-app/core/components/hoprd_api.py index d9e4a682..939afb3c 100644 --- a/ct-app/core/components/hoprd_api.py +++ b/ct-app/core/components/hoprd_api.py @@ -81,6 +81,11 @@ async def call( f"MaxRetryError calling {obj.__name__}.{method} " + f"with kwargs: {kwargs}, args: {args}" ) + except Exception as e: + self.error( + f"Exception calling {obj.__name__}.{method} " + + f"with kwargs: {kwargs}, args: {args}, error is: {e}" + ) else: return (True, response) diff --git a/final_waitlist.xlsx b/final_waitlist.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..6a289e08f2758483534485e09df1afd00c29279b GIT binary patch literal 7652 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 literal 0 HcmV?d00001 diff --git a/registry.xlsx b/registry.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..ec74176c6f9d27b70a7db8d3b53b902e3612fb85 GIT binary patch literal 8350 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 M3w|Uz2_Js_AG{qGPXGV_ literal 0 HcmV?d00001 diff --git a/waitlist.xlsx b/waitlist.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..15a2dc7618228cc62b5b8a2e5bd96294d418436e GIT binary patch literal 21344 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 Date: Mon, 4 Mar 2024 16:55:38 +0100 Subject: [PATCH 5/8] func renamed --- ct-app/core/components/hoprd_api.py | 5 +++-- final_waitlist.xlsx | Bin 7652 -> 0 bytes registry.xlsx | Bin 8350 -> 0 bytes waitlist.xlsx | Bin 21344 -> 0 bytes 4 files changed, 3 insertions(+), 2 deletions(-) delete mode 100644 final_waitlist.xlsx delete mode 100644 registry.xlsx delete mode 100644 waitlist.xlsx diff --git a/ct-app/core/components/hoprd_api.py b/ct-app/core/components/hoprd_api.py index 939afb3c..17f1ab9b 100644 --- a/ct-app/core/components/hoprd_api.py +++ b/ct-app/core/components/hoprd_api.py @@ -53,7 +53,7 @@ async def __call_api( f"Calling {obj.__name__}.{method} with kwargs: {kwargs}, args: {args}" ) - async def call( + async def __call( obj: Callable[..., object], method: str, *args, @@ -93,7 +93,8 @@ async def call( try: return await asyncio.wait_for( - asyncio.create_task(call(obj, method, *args, **kwargs)), timeout=timeout + asyncio.create_task(__call(obj, method, *args, **kwargs)), + timeout=timeout, ) except asyncio.TimeoutError: self.error( diff --git a/final_waitlist.xlsx b/final_waitlist.xlsx deleted file mode 100644 index 6a289e08f2758483534485e09df1afd00c29279b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7652 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 diff --git a/registry.xlsx b/registry.xlsx deleted file mode 100644 index ec74176c6f9d27b70a7db8d3b53b902e3612fb85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8350 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 M3w|Uz2_Js_AG{qGPXGV_ diff --git a/waitlist.xlsx b/waitlist.xlsx deleted file mode 100644 index 15a2dc7618228cc62b5b8a2e5bd96294d418436e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21344 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 Date: Mon, 4 Mar 2024 17:15:08 +0100 Subject: [PATCH 6/8] node address access thread safe --- ct-app/core/core.py | 6 ++--- ct-app/core/node.py | 64 +++++++++++++++++++++++++++------------------ 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/ct-app/core/core.py b/ct-app/core/core.py index ce181bc3..46264639 100644 --- a/ct-app/core/core.py +++ b/ct-app/core/core.py @@ -73,8 +73,8 @@ def network_nodes(self) -> list[Node]: return self.nodes[:-1] @property - def network_nodes_addresses(self) -> list[Address]: - return [node.address for node in self.network_nodes] + async def network_nodes_addresses(self) -> list[Address]: + return [await node.address.get() for node in self.network_nodes] @property def safes_balance_subgraph_type(self) -> SubgraphType: @@ -262,7 +262,7 @@ async def apply_economic_model(self): excluded = Utils.excludeElements(eligibles, low_allowance_addresses) self.debug(f"Excluded nodes with low safe allowance ({len(excluded)} entries).") - excluded = Utils.excludeElements(eligibles, self.network_nodes_addresses) + excluded = Utils.excludeElements(eligibles, await self.network_nodes_addresses) self.debug(f"Excluded network nodes ({len(excluded)} entries).") self.debug(f"Eligible nodes ({len(eligibles)} entries).") diff --git a/ct-app/core/node.py b/ct-app/core/node.py index 62664606..0505f771 100644 --- a/ct-app/core/node.py +++ b/ct-app/core/node.py @@ -1,6 +1,5 @@ import asyncio from datetime import datetime -from typing import Optional from prometheus_client import Gauge @@ -63,8 +62,8 @@ def __init__(self, url: str, key: str): self.api: HoprdAPI = HoprdAPI(url, key) self.url = url - self.address: Optional[Address] = None + self.address = LockedVar("address", None, infer_type=False) self.peers = LockedVar("peers", set[Peer]()) self.outgoings = LockedVar("outgoings", []) self.incomings = LockedVar("incomings", []) @@ -80,7 +79,7 @@ def __init__(self, url: str, key: str): def print_prefix(self): return ".".join(self.url.split("//")[-1].split(".")[:2]) - async def _retrieve_address(self): + async def _retrieve_address(self) -> Address: address = await self.api.get_address("all") if not isinstance(address, dict): @@ -89,15 +88,17 @@ async def _retrieve_address(self): if "hopr" not in address or "native" not in address: return - self.address = Address(address["hopr"], address["native"]) + await self.address.set(Address(address["hopr"], address["native"])) + + return await self.address.get() @flagguard @formalin(None) async def healthcheck(self): - await self._retrieve_address() - await self.connected.set(self.address is not None) + node_address = await self._retrieve_address() + await self.connected.set(node_address is not None) - if address := self.address: + if address := node_address: self.debug(f"Connection state: {await self.connected.get()}") HEALTH.labels(address.id).set(int(await self.connected.get())) else: @@ -117,6 +118,8 @@ async def open_channels(self): """ Open channels to discovered_peers. """ + node_address = await self.address.get() + out_opens = [ c for c in await self.outgoings.get() @@ -137,12 +140,12 @@ async def open_channels(self): ) if ok: self.debug(f"Opened channel to {address}") - CHANNELS_OPENED.labels(self.address.id).inc() + CHANNELS_OPENED.labels(node_address.id).inc() else: self.warning(f"Failed to open channel to {address}") - OPEN_CHANNELS_CALLS.labels(self.address.id).inc() + OPEN_CHANNELS_CALLS.labels(node_address.id).inc() - ADDRESSES_WOUT_CHANNELS.labels(self.address.id).set( + ADDRESSES_WOUT_CHANNELS.labels(node_address.id).set( len(addresses_without_channels) ) @@ -153,6 +156,7 @@ async def close_incoming_channels(self): """ Close incoming channels """ + node_address = await self.address.get() in_opens = [ c for c in await self.incomings.get() if ChannelStatus.isOpen(c.status) @@ -163,10 +167,10 @@ async def close_incoming_channels(self): ok = await self.api.close_channel(channel.channel_id) if ok: self.debug(f"Closed channel {channel.channel_id}") - INCOMING_CHANNELS_CLOSED.labels(self.address.id).inc() + INCOMING_CHANNELS_CLOSED.labels(node_address.id).inc() else: self.warning(f"Failed to close channel {channel.channel_id}") - CLOSE_INCOMING_CHANNELS_CALLS.labels(self.address.id).inc() + CLOSE_INCOMING_CHANNELS_CALLS.labels(node_address.id).inc() @flagguard @formalin("Closing pending channels") @@ -175,6 +179,7 @@ async def close_pending_channels(self): """ Close channels in PendingToClose state. """ + node_address = await self.address.get() out_pendings = [ c for c in await self.outgoings.get() if ChannelStatus.isPending(c.status) @@ -187,10 +192,10 @@ async def close_pending_channels(self): ok = await self.api.close_channel(channel.channel_id) if ok: self.debug(f"Closed pending channel {channel.channel_id}") - PENDING_CHANNELS_CLOSED.labels(self.address.id).inc() + PENDING_CHANNELS_CLOSED.labels(node_address.id).inc() else: self.warning(f"Failed to close pending channel {channel.channel_id}") - CLOSE_PENDING_CHANNELS_CALLS.labels(self.address.id).inc() + CLOSE_PENDING_CHANNELS_CALLS.labels(node_address.id).inc() @flagguard @formalin("Closing old channels") @@ -199,6 +204,8 @@ async def close_old_channels(self): """ Close channels that have been open for too long. """ + node_address = await self.address.get() + outgoings = await self.outgoings.get() peer_history: dict[str, datetime] = await self.peer_history.get() to_peer_history = dict[str, datetime]() @@ -234,11 +241,11 @@ async def close_old_channels(self): if ok: self.debug(f"Channel {channel} closed") - OLD_CHANNELS_CLOSED.labels(self.address.id).inc() + OLD_CHANNELS_CLOSED.labels(node_address.id).inc() else: self.warning(f"Failed to close channel {channel_id}") - CLOSE_OLD_CHANNELS_CALLS.labels(self.address.id).inc() + CLOSE_OLD_CHANNELS_CALLS.labels(node_address.id).inc() @flagguard @formalin("Funding channels") @@ -247,6 +254,7 @@ async def fund_channels(self): """ Fund channels that are below minimum threshold. """ + node_address = await self.address.get() out_opens = [ c for c in await self.outgoings.get() if ChannelStatus.isOpen(c.status) @@ -270,10 +278,10 @@ async def fund_channels(self): ) if ok: self.debug(f"Funded channel {channel.channel_id}") - FUNDED_CHANNELS.labels(self.address.id).inc() + FUNDED_CHANNELS.labels(node_address.id).inc() else: self.warning(f"Failed to fund channel {channel.channel_id}") - FUND_CHANNELS_CALLS.labels(self.address.id).inc() + FUND_CHANNELS_CALLS.labels(node_address.id).inc() @flagguard @formalin("Retrieving peers") @@ -282,6 +290,7 @@ async def retrieve_peers(self): """ Retrieve real peers from the network. """ + node_address = await self.address.get() results = await self.api.peers( params=["peer_id", "peer_address", "reported_version"], quality=0.5 @@ -297,7 +306,7 @@ async def retrieve_peers(self): await self.peer_history.update(addresses_w_timestamp) self.debug(f"Peers: {len(peers)}") - PEERS_COUNT.labels(self.address.id).set(len(peers)) + PEERS_COUNT.labels(node_address.id).set(len(peers)) @flagguard @formalin("Retrieving outgoing channels") @@ -307,17 +316,18 @@ async def retrieve_outgoing_channels(self): Retrieve all outgoing channels. """ channels = await self.api.all_channels(False) + node_address = await self.address.get() outgoings = [ c for c in channels.all - if c.source_peer_id == self.address.id + if c.source_peer_id == node_address.id and not ChannelStatus.isClosed(c.status) ] await self.outgoings.set(outgoings) self.debug(f"Outgoing channels: {len(outgoings)}") - OUTGOING_CHANNELS.labels(self.address.id).set(len(outgoings)) + OUTGOING_CHANNELS.labels(node_address.id).set(len(outgoings)) @flagguard @formalin("Retrieving incoming channels") @@ -327,17 +337,18 @@ async def retrieve_incoming_channels(self): Retrieve all incoming channels. """ channels = await self.api.all_channels(False) + node_address = await self.address.get() incomings = [ c for c in channels.all - if c.destination_peer_id == self.address.id + if c.destination_peer_id == node_address.id and not ChannelStatus.isClosed(c.status) ] await self.incomings.set(incomings) self.debug(f"Incoming channels: {len(incomings)}") - INCOMING_CHANNELS.labels(self.address.id).set(len(incomings)) + INCOMING_CHANNELS.labels(node_address.id).set(len(incomings)) @flagguard @formalin("Retrieving total funds") @@ -347,17 +358,18 @@ async def get_total_channel_funds(self): Retrieve total funds. """ channels = await self.outgoings.get() + node_address = await self.address.get() results = await Utils.aggregatePeerBalanceInChannels(channels) - if self.address.id not in results: + if node_address.id not in results: self.warning("Funding info not found") return - entry = TopologyEntry.fromDict(self.address.id, results[self.address.id]) + entry = TopologyEntry.fromDict(node_address.id, results[node_address.id]) self.debug(f"Channels funds: { entry.channels_balance}") - TOTAL_CHANNEL_FUNDS.labels(self.address.id).set(entry.channels_balance) + TOTAL_CHANNEL_FUNDS.labels(node_address.id).set(entry.channels_balance) def tasks(self): self.info("Starting node") From b840e6894dccc6ad6f3573f30200da4dbeb54582 Mon Sep 17 00:00:00 2001 From: Jean Demeusy Date: Mon, 4 Mar 2024 17:19:16 +0100 Subject: [PATCH 7/8] ensure timeout it used on any http post request --- ct-app/core/components/utils.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ct-app/core/components/utils.py b/ct-app/core/components/utils.py index fc7ef45c..3ba6fdc8 100644 --- a/ct-app/core/components/utils.py +++ b/ct-app/core/components/utils.py @@ -68,16 +68,18 @@ def nodesAddresses( return list(addresses), list(keys) @classmethod - async def httpPOST(cls, url, data) -> tuple[int, dict]: - async def post(session: ClientSession, url: str, data: dict): - async with session.post(url, json=data) as response: + async def httpPOST( + cls, url: str, data: dict, timeout: int = 60 + ) -> tuple[int, dict]: + async def post(session: ClientSession, url: str, data: dict, timeout: int): + async with session.post(url, json=data, timeout=timeout) as response: status = response.status response = await response.json() return status, response async with aiohttp.ClientSession() as session: try: - status, response = await post(session, url, data) + status, response = await post(session, url, data, timeout) except Exception: return None, None else: From fa2b8f0d41f8db9adac01ec4eea859c4491b27fe Mon Sep 17 00:00:00 2001 From: Jean Demeusy <61140535+jeandemeusy@users.noreply.github.com> Date: Tue, 5 Mar 2024 12:39:12 +0100 Subject: [PATCH 8/8] Update ct-app/core/core.py Co-authored-by: Tibor <9529609+Teebor-Choka@users.noreply.github.com> --- ct-app/core/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct-app/core/core.py b/ct-app/core/core.py index 46264639..c9c84736 100644 --- a/ct-app/core/core.py +++ b/ct-app/core/core.py @@ -74,7 +74,7 @@ def network_nodes(self) -> list[Node]: @property async def network_nodes_addresses(self) -> list[Address]: - return [await node.address.get() for node in self.network_nodes] + return await asyncio.gather(*[node.address.get() for node in self.network_nodes]) @property def safes_balance_subgraph_type(self) -> SubgraphType: