From 2f2732479846f712dfc261a8f1c67e550f9de418 Mon Sep 17 00:00:00 2001 From: Jean Demeusy <61140535+jeandemeusy@users.noreply.github.com> Date: Tue, 21 Jan 2025 23:00:04 +0100 Subject: [PATCH 1/3] feature implementation --- ct-app/core/components/utils.py | 31 +++++++++++++++++++++++++++++++ ct-app/core/core.py | 32 +++++++------------------------- ct-app/core/node.py | 21 +++------------------ 3 files changed, 41 insertions(+), 43 deletions(-) diff --git a/ct-app/core/components/utils.py b/ct-app/core/components/utils.py index 4c8ca1bb..0bc4614b 100644 --- a/ct-app/core/components/utils.py +++ b/ct-app/core/components/utils.py @@ -1,3 +1,5 @@ +import ast + from core.baseclass import Base from core.subgraph.entries import Safe @@ -148,3 +150,32 @@ async def balanceInChannels(cls, channels: list) -> dict[str, dict]: c.balance) / 1e18 return results + + @classmethod + def decorated_methods(cls, file: str, target: str): + with open(file, "r") as f: + source_code = f.read() + + tree = ast.parse(source_code) + + keepalive_methods = [] + + for node in ast.walk(tree): + if not isinstance(node, ast.FunctionDef) and not isinstance(node, ast.AsyncFunctionDef): + continue + + for decorator in node.decorator_list: + if isinstance(decorator, ast.Call): + args_name = [arg.id for arg in decorator.args] + + if decorator.func.id != target and target not in args_name: + continue + + elif isinstance(decorator, ast.Name): + if decorator.id != target: + continue + + keepalive_methods.append(node.name) + break + + return keepalive_methods diff --git a/ct-app/core/core.py b/ct-app/core/core.py index 86b7b3f5..a4a9d7ca 100644 --- a/ct-app/core/core.py +++ b/ct-app/core/core.py @@ -7,6 +7,7 @@ from .baseclass import Base from .components import Address, AsyncLoop, LockedVar, Parameters, Peer, Utils from .components.decorators import flagguard, formalin, master +from .components.utils import Utils from .economic_model import EconomicModelTypes from .node import Node from .subgraph import URL, ProviderError, Type, entries @@ -59,7 +60,7 @@ def __init__(self, nodes: list[Node], params: Parameters): s: s.provider(URL(self.params.subgraph, s.value)) for s in Type } - self.running = False + self.running = True @property def api(self) -> HoprdAPI: @@ -402,32 +403,13 @@ async def start(self): """ self.info(f"CTCore started with {len(self.nodes)} nodes.") - for node in self.nodes: - node.running = True - await node._healthcheck() - AsyncLoop.update(await node.tasks()) - - self.running = True - + [await node._healthcheck() for node in self.nodes] + AsyncLoop.update(sum([node.tasks() for node in self.nodes], [])) AsyncLoop.update( - [ - self.rotate_subgraphs, - self.peers_rewards, - self.ticket_parameters, - self.connected_peers, - self.registered_nodes, - self.topology, - self.nft_holders, - self.allocations, - self.eoa_balances, - self.apply_economic_model, - self.safe_fundings, - ] + [getattr(self, method) + for method in Utils.decorated_methods(__file__, "formalin")] ) - for node in self.nodes: - AsyncLoop.add(node.observe_message_queue) - await AsyncLoop.gather() def stop(self): @@ -435,4 +417,4 @@ def stop(self): Stop the node. """ self.info("CTCore stopped.") - self.running = False + self.running = False \ No newline at end of file diff --git a/ct-app/core/node.py b/ct-app/core/node.py index 709b7938..75f496f7 100644 --- a/ct-app/core/node.py +++ b/ct-app/core/node.py @@ -50,7 +50,7 @@ def __init__(self, url: str, key: str): self.params = Parameters() self.connected = False - self.running = False + self.running = True @property async def safe_address(self): @@ -346,23 +346,8 @@ async def observe_message_queue(self): AsyncLoop.add(self.api.send_message, self.address.hopr, message.format(), [message.relayer]) MESSAGES_STATS.labels("sent", self.address.hopr, message.relayer).inc() - async def tasks(self): - callbacks = [ - self.healthcheck, - self.retrieve_peers, - self.retrieve_balances, - self.retrieve_channels, - self.open_channels, - self.fund_channels, - self.close_old_channels, - self.close_incoming_channels, - self.close_pending_channels, - self.get_total_channel_funds, - self.observe_message_queue, - self.observe_relayed_messages, - ] - - return callbacks + def tasks(self): + return [getattr(self, method) for method in Utils.decorated_methods(__file__, "formalin")] @classmethod def fromCredentials(cls, addresses: list[str], keys: list[str]): From 4ad64e03e8be2b013d1f3daf4238126e64c9480c Mon Sep 17 00:00:00 2001 From: Jean Demeusy <61140535+jeandemeusy@users.noreply.github.com> Date: Wed, 22 Jan 2025 09:36:32 +0100 Subject: [PATCH 2/3] cleanup unnecessary code --- ct-app/core/__main__.py | 8 ++------ ct-app/core/core.py | 12 ++++++------ ct-app/core/node.py | 7 ++----- ct-app/test/test_node.py | 12 +----------- 4 files changed, 11 insertions(+), 28 deletions(-) diff --git a/ct-app/core/__main__.py b/ct-app/core/__main__.py index eb112438..04e75c39 100644 --- a/ct-app/core/__main__.py +++ b/ct-app/core/__main__.py @@ -4,7 +4,6 @@ from .baseclass import Base from .components import AsyncLoop, Parameters, Utils -from .components.messages import MessageQueue from .core import Core from .node import Node @@ -17,11 +16,11 @@ def main(configfile: str): params = Parameters() params.parse(config, entrypoint=True) - params.from_env("SUBGRAPH", "PG") + params.from_env("SUBGRAPH") params.overrides("OVERRIDE") # create the core and nodes instances - nodes = Node.fromCredentials(*Utils.nodesCredentials("NODE_ADDRESS", "NODE_KEY")) + nodes = [Node(*pair) for pair in zip(*Utils.nodesCredentials("NODE_ADDRESS", "NODE_KEY"))] # start the prometheus client try: @@ -35,8 +34,5 @@ def main(configfile: str): AsyncLoop.run(core.start, core.stop) - MessageQueue.clear() - - if __name__ == "__main__": main() diff --git a/ct-app/core/core.py b/ct-app/core/core.py index a4a9d7ca..a39573bb 100644 --- a/ct-app/core/core.py +++ b/ct-app/core/core.py @@ -7,7 +7,6 @@ from .baseclass import Base from .components import Address, AsyncLoop, LockedVar, Parameters, Peer, Utils from .components.decorators import flagguard, formalin, master -from .components.utils import Utils from .economic_model import EconomicModelTypes from .node import Node from .subgraph import URL, ProviderError, Type, entries @@ -397,6 +396,10 @@ async def safe_fundings(self): f"Fetched safe fundings ({amount} + {self.params.fundings.constant})" ) + @property + async def tasks(self): + return [getattr(self, method) for method in Utils.decorated_methods(__file__, "formalin")] + async def start(self): """ Start the node. @@ -404,11 +407,8 @@ async def start(self): self.info(f"CTCore started with {len(self.nodes)} nodes.") [await node._healthcheck() for node in self.nodes] - AsyncLoop.update(sum([node.tasks() for node in self.nodes], [])) - AsyncLoop.update( - [getattr(self, method) - for method in Utils.decorated_methods(__file__, "formalin")] - ) + AsyncLoop.update(sum([node.tasks for node in self.nodes], [])) + AsyncLoop.update(self.tasks) await AsyncLoop.gather() diff --git a/ct-app/core/node.py b/ct-app/core/node.py index 75f496f7..f20ac253 100644 --- a/ct-app/core/node.py +++ b/ct-app/core/node.py @@ -346,9 +346,6 @@ async def observe_message_queue(self): AsyncLoop.add(self.api.send_message, self.address.hopr, message.format(), [message.relayer]) MESSAGES_STATS.labels("sent", self.address.hopr, message.relayer).inc() + @property def tasks(self): - return [getattr(self, method) for method in Utils.decorated_methods(__file__, "formalin")] - - @classmethod - def fromCredentials(cls, addresses: list[str], keys: list[str]): - return [cls(address, key) for address, key in zip(addresses, keys)] + return [getattr(self, method) for method in Utils.decorated_methods(__file__, "formalin")] \ No newline at end of file diff --git a/ct-app/test/test_node.py b/ct-app/test/test_node.py index 50fa2558..0c489728 100644 --- a/ct-app/test/test_node.py +++ b/ct-app/test/test_node.py @@ -88,14 +88,4 @@ async def test_get_total_channel_funds(node: Node, channels: Channels): @pytest.mark.asyncio async def test_check_inbox(node: Node): - pytest.skip(f"{inspect.stack()[0][3]} not implemented") - - -@pytest.mark.asyncio -async def test_fromAddressAndKeyLists(node: Node): - addresses = ["LOCALHOST:9091", "LOCALHOST:9092", "LOCALHOST:9093"] - keys = ["key1", "key2", "key3"] - - nodes = Node.fromCredentials(addresses, keys) - - assert len(nodes) == len(addresses) == len(keys) + pytest.skip(f"{inspect.stack()[0][3]} not implemented") \ No newline at end of file From 8fad349e11dc4d61b83705b1b44dc921bb67258c Mon Sep 17 00:00:00 2001 From: Jean Demeusy <61140535+jeandemeusy@users.noreply.github.com> Date: Wed, 22 Jan 2025 15:50:04 +0100 Subject: [PATCH 3/3] Update ct-app/core/components/utils.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- ct-app/core/components/utils.py | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/ct-app/core/components/utils.py b/ct-app/core/components/utils.py index 0bc4614b..edfd4c1d 100644 --- a/ct-app/core/components/utils.py +++ b/ct-app/core/components/utils.py @@ -153,10 +153,17 @@ async def balanceInChannels(cls, channels: list) -> dict[str, dict]: @classmethod def decorated_methods(cls, file: str, target: str): - with open(file, "r") as f: - source_code = f.read() - - tree = ast.parse(source_code) + try: + with open(file, "r") as f: + source_code = f.read() + + tree = ast.parse(source_code) + except FileNotFoundError as e: + cls().error(f"Could not find file {file}: {e}") + return [] + except SyntaxError as e: + cls().error(f"Could not parse {file}: {e}") + return [] keepalive_methods = [] @@ -165,15 +172,20 @@ def decorated_methods(cls, file: str, target: str): continue for decorator in node.decorator_list: - if isinstance(decorator, ast.Call): - args_name = [arg.id for arg in decorator.args] + try: + if isinstance(decorator, ast.Call): + args_name = [arg.id for arg in decorator.args if isinstance(arg, ast.Name)] - if decorator.func.id != target and target not in args_name: - continue + if not hasattr(decorator.func, 'id') or (decorator.func.id != target and target not in args_name): + continue - elif isinstance(decorator, ast.Name): - if decorator.id != target: + elif isinstance(decorator, ast.Name): + if not hasattr(decorator, 'id') or decorator.id != target: + continue + else: continue + except AttributeError: + continue keepalive_methods.append(node.name) break