From 28ed908c0f71ca7cdb149c3188ea61626fb92f6c Mon Sep 17 00:00:00 2001 From: Aya Hassan Date: Mon, 9 Mar 2026 18:48:25 +0100 Subject: [PATCH 01/15] Adding wrapper for new APIs --- waku/wrapper.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/waku/wrapper.py b/waku/wrapper.py index 55bf93e..45479fd 100644 --- a/waku/wrapper.py +++ b/waku/wrapper.py @@ -56,6 +56,13 @@ int logosdelivery_unsubscribe( void *userData, const char *contentTopic ); + +int logosdelivery_send( + void *ctx, + FFICallBack callback, + void *userData, + const char *messageJson +); """ ) @@ -242,4 +249,21 @@ class NodeWrapper: if rc != 0: return Err(f"unsubscribe_content_topic: immediate call failed (ret={rc})") - return _wait_cb(state, f"unsubscribe({content_topic})", timeout_s) \ No newline at end of file + return _wait_cb(state, f"unsubscribe({content_topic})", timeout_s) + + def send_message(self, message: dict, *, timeout_s: float = 20.0) -> Result[int, str]: + state = _new_cb_state() + cb = self._make_waiting_cb(state) + + message_json = json.dumps(message, separators=(",", ":"), ensure_ascii=False) + + rc = lib.logosdelivery_send( + self.ctx, + cb, + ffi.NULL, + message_json.encode("utf-8"), + ) + if rc != 0: + return Err(f"send_message: immediate call failed (ret={rc})") + + return _wait_cb(state, "send_message", timeout_s) \ No newline at end of file From 6777158dd351e35745fa488f35c34256b88f650a Mon Sep 17 00:00:00 2001 From: Aya Hassan Date: Tue, 10 Mar 2026 18:53:24 +0100 Subject: [PATCH 02/15] wrap the rest of APIs --- vendor/nwaku | 1 + 1 file changed, 1 insertion(+) create mode 160000 vendor/nwaku diff --git a/vendor/nwaku b/vendor/nwaku new file mode 160000 index 0000000..f1d14e9 --- /dev/null +++ b/vendor/nwaku @@ -0,0 +1 @@ +Subproject commit f1d14e9942fc44168d5755cd5a7f0123da8c5649 From 1a7f9a9d00efd4dded2a96821726b7ee7e3af11b Mon Sep 17 00:00:00 2001 From: Aya Hassan Date: Sun, 29 Mar 2026 15:50:12 +0200 Subject: [PATCH 03/15] Modify send API wrapper --- waku/wrapper.py | 61 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/waku/wrapper.py b/waku/wrapper.py index 45479fd..79ade59 100644 --- a/waku/wrapper.py +++ b/waku/wrapper.py @@ -1,10 +1,12 @@ import json import threading import logging +from time import sleep + from cffi import FFI from pathlib import Path from result import Result, Ok, Err - +import time logger = logging.getLogger(__name__) ffi = FFI() @@ -257,13 +259,62 @@ class NodeWrapper: message_json = json.dumps(message, separators=(",", ":"), ensure_ascii=False) - rc = lib.logosdelivery_send( + request_id = lib.logosdelivery_send( self.ctx, cb, ffi.NULL, message_json.encode("utf-8"), ) - if rc != 0: - return Err(f"send_message: immediate call failed (ret={rc})") - return _wait_cb(state, "send_message", timeout_s) \ No newline at end of file + if request_id < 0: + return Err(f"send_message: immediate call failed (ret={request_id})") + + wait_result = _wait_cb(state, "send_message", timeout_s) + if wait_result.is_err(): + return Err(wait_result.err()) + + return Ok(request_id) + +def main(): + config = { + "logLevel": "DEBUG", + "mode": "Core", + "protocolsConfig": { + "entryNodes": [ + "/dns4/node-01.do-ams3.misc.logos-chat.status.im/tcp/30303/p2p/16Uiu2HAkxoqUTud5LUPQBRmkeL2xP4iKx2kaABYXomQRgmLUgf78" + ], + "clusterId": 3, + "autoShardingConfig": {"numShardsInCluster": 8}, + }, + "networkingConfig": { + "listenIpv4": "0.0.0.0", + "p2pTcpPort": 60000, + "discv5UdpPort": 9000, + }, + } + + topic = "/test/1/chat/proto" + + message = { + "contentTopic": topic, + "payload": "SGVsbG8=", + "ephemeral": False, + } + + node_result = NodeWrapper.create_and_start(config) + sleep(20) + if node_result.is_err(): + print(node_result.err()) + return + + node = node_result.ok_value + + print(node.subscribe_content_topic(topic)) + #print(node.send_message(message)) + + print(node.stop_node()) + print(node.destroy()) + + +if __name__ == "__main__": + main() \ No newline at end of file From eb7ab05e8d17c2189f6db94f281d861a1c5757b6 Mon Sep 17 00:00:00 2001 From: Aya Hassan Date: Sun, 29 Mar 2026 23:55:40 +0200 Subject: [PATCH 04/15] Add wrappers for the rest of APIs --- waku/wrapper.py | 133 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 117 insertions(+), 16 deletions(-) diff --git a/waku/wrapper.py b/waku/wrapper.py index 79ade59..dd684d4 100644 --- a/waku/wrapper.py +++ b/waku/wrapper.py @@ -6,7 +6,7 @@ from time import sleep from cffi import FFI from pathlib import Path from result import Result, Ok, Err -import time + logger = logging.getLogger(__name__) ffi = FFI() @@ -65,6 +65,19 @@ int logosdelivery_send( void *userData, const char *messageJson ); + +int logosdelivery_get_available_node_info_ids( + void *ctx, + FFICallBack callback, + void *userData +); + +int logosdelivery_get_node_info( + void *ctx, + FFICallBack callback, + void *userData, + const char *nodeInfoId +); """ ) @@ -82,7 +95,7 @@ def _new_cb_state(): } -def _wait_cb(state, op_name: str, timeout_s: float = 20.0) -> Result[int, str]: +def _wait_cb_raw(state, op_name: str, timeout_s: float = 20.0): finished = state["done"].wait(timeout_s) if not finished: return Err(f"{op_name}: timeout waiting for callback after {timeout_s}s") @@ -93,6 +106,15 @@ def _wait_cb(state, op_name: str, timeout_s: float = 20.0) -> Result[int, str]: if ret is None: return Err(f"{op_name}: callback fired but ret is None") + return Ok((ret, msg)) + + +def _wait_cb_status(state, op_name: str, timeout_s: float = 20.0) -> Result[int, str]: + wait_result = _wait_cb_raw(state, op_name, timeout_s) + if wait_result.is_err(): + return Err(wait_result.err()) + + ret, msg = wait_result.ok_value if ret != 0: return Err(f"{op_name}: failed (ret={ret}) msg={msg!r}") @@ -148,7 +170,7 @@ class NodeWrapper: if ctx == ffi.NULL: return Err("create_node: ctx is NULL") - wait_result = _wait_cb(state, "create_node", timeout_s) + wait_result = _wait_cb_status(state, "create_node", timeout_s) if wait_result.is_err(): return Err(wait_result.err()) @@ -194,7 +216,7 @@ class NodeWrapper: if rc != 0: return Err(f"start_node: immediate call failed (ret={rc})") - return _wait_cb(state, "start_node", timeout_s) + return _wait_cb_status(state, "start_node", timeout_s) def stop_node(self, *, timeout_s: float = 20.0) -> Result[int, str]: state = _new_cb_state() @@ -204,7 +226,7 @@ class NodeWrapper: if rc != 0: return Err(f"stop_node: immediate call failed (ret={rc})") - return _wait_cb(state, "stop_node", timeout_s) + return _wait_cb_status(state, "stop_node", timeout_s) def destroy(self, *, timeout_s: float = 20.0) -> Result[int, str]: state = _new_cb_state() @@ -214,7 +236,7 @@ class NodeWrapper: if rc != 0: return Err(f"destroy: immediate call failed (ret={rc})") - return _wait_cb(state, "destroy", timeout_s) + return _wait_cb_status(state, "destroy", timeout_s) def stop_and_destroy(self, *, timeout_s: float = 20.0) -> Result[int, str]: stop_result = self.stop_node(timeout_s=timeout_s) @@ -236,7 +258,7 @@ class NodeWrapper: if rc != 0: return Err(f"subscribe_content_topic: immediate call failed (ret={rc})") - return _wait_cb(state, f"subscribe({content_topic})", timeout_s) + return _wait_cb_status(state, f"subscribe({content_topic})", timeout_s) def unsubscribe_content_topic(self, content_topic: str, *, timeout_s: float = 20.0) -> Result[int, str]: state = _new_cb_state() @@ -251,30 +273,108 @@ class NodeWrapper: if rc != 0: return Err(f"unsubscribe_content_topic: immediate call failed (ret={rc})") - return _wait_cb(state, f"unsubscribe({content_topic})", timeout_s) + return _wait_cb_status(state, f"unsubscribe({content_topic})", timeout_s) - def send_message(self, message: dict, *, timeout_s: float = 20.0) -> Result[int, str]: + def send_message(self, message: dict, *, timeout_s: float = 20.0) -> Result[str, str]: state = _new_cb_state() cb = self._make_waiting_cb(state) message_json = json.dumps(message, separators=(",", ":"), ensure_ascii=False) - request_id = lib.logosdelivery_send( + rc = lib.logosdelivery_send( self.ctx, cb, ffi.NULL, message_json.encode("utf-8"), ) - if request_id < 0: - return Err(f"send_message: immediate call failed (ret={request_id})") + if rc < 0: + return Err(f"send_message: immediate call failed (ret={rc})") - wait_result = _wait_cb(state, "send_message", timeout_s) + wait_result = _wait_cb_raw(state, "send_message", timeout_s) if wait_result.is_err(): return Err(wait_result.err()) + cb_ret, cb_msg = wait_result.ok_value + if cb_ret != 0: + return Err(f"send_message: failed (ret={cb_ret}) msg={cb_msg!r}") + + request_id = cb_msg.decode("utf-8") if cb_msg else "" return Ok(request_id) + def get_available_node_info_ids(self, *, timeout_s: float = 20.0) -> Result[int, str]: + state = _new_cb_state() + cb = self._make_waiting_cb(state) + + node_info_id = lib.logosdelivery_get_available_node_info_ids( + self.ctx, + cb, + ffi.NULL, + ) + + if node_info_id < 0: + return Err(f"get_available_node_info_ids: immediate call failed (ret={node_info_id})") + + wait_result = _wait_cb_raw(state, "get_available_node_info_ids", timeout_s) + if wait_result.is_err(): + return Err(wait_result.err()) + + return Ok(node_info_id) + + def get_node_info(self, node_info_id: str, *, timeout_s: float = 20.0) -> Result[dict, str]: + state = _new_cb_state() + cb = self._make_waiting_cb(state) + + rc = lib.logosdelivery_get_node_info( + self.ctx, + cb, + ffi.NULL, + node_info_id.encode("utf-8"), + ) + + if rc < 0: + return Err(f"get_node_info: immediate call failed (ret={rc})") + + wait_result = _wait_cb_raw(state, "get_node_info", timeout_s) + if wait_result.is_err(): + return Err(wait_result.err()) + + cb_ret, cb_msg = wait_result.ok_value + if cb_ret != 0: + return Err(f"get_node_info: failed (ret={cb_ret}) msg={cb_msg!r}") + + if not cb_msg: + return Err("get_node_info: empty response") + + try: + result = json.loads(cb_msg.decode("utf-8")) + except Exception as e: + return Err(f"get_node_info: failed to parse response: {e}") + + return Ok(result) + + def debug_get_node_info(self, node_info_id: str, *, timeout_s: float = 20.0) -> Result[tuple, str]: + state = _new_cb_state() + cb = self._make_waiting_cb(state) + + rc = lib.logosdelivery_get_node_info( + self.ctx, + cb, + ffi.NULL, + node_info_id.encode("utf-8"), + ) + + print(f"[DEBUG] get_node_info immediate rc={rc}") + + wait_result = _wait_cb_raw(state, "get_node_info", timeout_s) + if wait_result.is_err(): + return Err(wait_result.err()) + + cb_ret, cb_msg = wait_result.ok_value + print(f"[DEBUG] get_node_info callback ret={cb_ret}, msg={cb_msg}") + + return Ok((rc, cb_ret, cb_msg)) + def main(): config = { "logLevel": "DEBUG", @@ -310,11 +410,12 @@ def main(): node = node_result.ok_value print(node.subscribe_content_topic(topic)) - #print(node.send_message(message)) - + print(node.send_message(message)) + #print(node.get_available_node_info_ids()) print(node.stop_node()) print(node.destroy()) if __name__ == "__main__": - main() \ No newline at end of file + main() + From 17e526c4a1e051097ddbb35b4dcf03cb69bc1441 Mon Sep 17 00:00:00 2001 From: Aya Hassan Date: Mon, 30 Mar 2026 11:19:16 +0200 Subject: [PATCH 05/15] Adding the test version of the wrapper file --- waku/wrapper.py | 67 ++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/waku/wrapper.py b/waku/wrapper.py index dd684d4..9f9dd50 100644 --- a/waku/wrapper.py +++ b/waku/wrapper.py @@ -78,6 +78,12 @@ int logosdelivery_get_node_info( void *userData, const char *nodeInfoId ); + +int logosdelivery_get_available_configs( + void *ctx, + FFICallBack callback, + void *userData +); """ ) @@ -333,48 +339,40 @@ class NodeWrapper: ) if rc < 0: - return Err(f"get_node_info: immediate call failed (ret={rc})") + return Err(f"call failed rc={rc}") - wait_result = _wait_cb_raw(state, "get_node_info", timeout_s) + wait = _wait_cb_raw(state, "get_node_info", timeout_s) + if wait.is_err(): + return Err(wait.err()) + + cb_ret, cb_msg = wait.ok_value + if cb_ret != 0 or not cb_msg: + return Err(f"callback failed ret={cb_ret}") + + try: + return Ok(json.loads(cb_msg.decode())) + except Exception: + + return Err("invalid json") + + def get_available_configs(self, *, timeout_s: float = 20.0) -> Result[bytes, str]: + state = _new_cb_state() + cb = self._make_waiting_cb(state) + + rc = lib.logosdelivery_get_available_configs(self.ctx, cb, ffi.NULL) + if rc != 0: + return Err(f"get_available_configs failed: {rc}") + + wait_result = _wait_cb_raw(state, "get_available_configs", timeout_s) if wait_result.is_err(): return Err(wait_result.err()) cb_ret, cb_msg = wait_result.ok_value if cb_ret != 0: - return Err(f"get_node_info: failed (ret={cb_ret}) msg={cb_msg!r}") + return Err(f"get_available_configs failed: {cb_ret}") - if not cb_msg: - return Err("get_node_info: empty response") + return Ok(cb_msg) - try: - result = json.loads(cb_msg.decode("utf-8")) - except Exception as e: - return Err(f"get_node_info: failed to parse response: {e}") - - return Ok(result) - - def debug_get_node_info(self, node_info_id: str, *, timeout_s: float = 20.0) -> Result[tuple, str]: - state = _new_cb_state() - cb = self._make_waiting_cb(state) - - rc = lib.logosdelivery_get_node_info( - self.ctx, - cb, - ffi.NULL, - node_info_id.encode("utf-8"), - ) - - print(f"[DEBUG] get_node_info immediate rc={rc}") - - wait_result = _wait_cb_raw(state, "get_node_info", timeout_s) - if wait_result.is_err(): - return Err(wait_result.err()) - - cb_ret, cb_msg = wait_result.ok_value - print(f"[DEBUG] get_node_info callback ret={cb_ret}, msg={cb_msg}") - - return Ok((rc, cb_ret, cb_msg)) - def main(): config = { "logLevel": "DEBUG", @@ -412,6 +410,7 @@ def main(): print(node.subscribe_content_topic(topic)) print(node.send_message(message)) #print(node.get_available_node_info_ids()) + #print(node.debug_get_available_configs()) print(node.stop_node()) print(node.destroy()) From 2b448e2f0db7aa5d3f36d53db798331c505a9698 Mon Sep 17 00:00:00 2001 From: Aya Hassan Date: Mon, 30 Mar 2026 11:51:34 +0200 Subject: [PATCH 06/15] clean version of wrappers --- waku/wrapper.py | 154 +++++++++++++++++++----------------------------- 1 file changed, 60 insertions(+), 94 deletions(-) diff --git a/waku/wrapper.py b/waku/wrapper.py index 9f9dd50..b53fd17 100644 --- a/waku/wrapper.py +++ b/waku/wrapper.py @@ -1,14 +1,10 @@ import json import threading -import logging -from time import sleep from cffi import FFI from pathlib import Path from result import Result, Ok, Err -logger = logging.getLogger(__name__) - ffi = FFI() ffi.cdef( @@ -97,34 +93,31 @@ def _new_cb_state(): return { "done": threading.Event(), "ret": None, - "msg": None, + "msg": b"", } def _wait_cb_raw(state, op_name: str, timeout_s: float = 20.0): - finished = state["done"].wait(timeout_s) - if not finished: - return Err(f"{op_name}: timeout waiting for callback after {timeout_s}s") + ok = state["done"].wait(timeout_s) + if not ok: + return Err(f"{op_name}: timeout after {timeout_s}s") - ret = state["ret"] - msg = state["msg"] or b"" + if state["ret"] is None: + return Err(f"{op_name}: callback ret is None") - if ret is None: - return Err(f"{op_name}: callback fired but ret is None") - - return Ok((ret, msg)) + return Ok((state["ret"], state["msg"])) -def _wait_cb_status(state, op_name: str, timeout_s: float = 20.0) -> Result[int, str]: +def _wait_cb_ok(state, op_name: str, timeout_s: float = 20.0) -> Result[int, str]: wait_result = _wait_cb_raw(state, op_name, timeout_s) if wait_result.is_err(): return Err(wait_result.err()) - ret, msg = wait_result.ok_value - if ret != 0: - return Err(f"{op_name}: failed (ret={ret}) msg={msg!r}") + cb_ret, cb_msg = wait_result.ok_value + if cb_ret != 0: + return Err(f"{op_name}: callback failed (ret={cb_ret}) msg={cb_msg!r}") - return Ok(ret) + return Ok(cb_ret) class NodeWrapper: @@ -165,18 +158,18 @@ class NodeWrapper: config_buffer = ffi.new("char[]", config_json.encode("utf-8")) state = _new_cb_state() - create_cb = cls._make_waiting_cb(state) + cb = cls._make_waiting_cb(state) ctx = lib.logosdelivery_create_node( config_buffer, - create_cb, + cb, ffi.NULL, ) if ctx == ffi.NULL: return Err("create_node: ctx is NULL") - wait_result = _wait_cb_status(state, "create_node", timeout_s) + wait_result = _wait_cb_ok(state, "create_node", timeout_s) if wait_result.is_err(): return Err(wait_result.err()) @@ -208,6 +201,7 @@ class NodeWrapper: return Err(node_result.err()) node = node_result.ok_value + start_result = node.start_node(timeout_s=timeout_s) if start_result.is_err(): return Err(start_result.err()) @@ -222,7 +216,7 @@ class NodeWrapper: if rc != 0: return Err(f"start_node: immediate call failed (ret={rc})") - return _wait_cb_status(state, "start_node", timeout_s) + return _wait_cb_ok(state, "start_node", timeout_s) def stop_node(self, *, timeout_s: float = 20.0) -> Result[int, str]: state = _new_cb_state() @@ -232,7 +226,7 @@ class NodeWrapper: if rc != 0: return Err(f"stop_node: immediate call failed (ret={rc})") - return _wait_cb_status(state, "stop_node", timeout_s) + return _wait_cb_ok(state, "stop_node", timeout_s) def destroy(self, *, timeout_s: float = 20.0) -> Result[int, str]: state = _new_cb_state() @@ -242,7 +236,12 @@ class NodeWrapper: if rc != 0: return Err(f"destroy: immediate call failed (ret={rc})") - return _wait_cb_status(state, "destroy", timeout_s) + wait_result = _wait_cb_ok(state, "destroy", timeout_s) + if wait_result.is_err(): + return Err(wait_result.err()) + + self.ctx = ffi.NULL + return wait_result def stop_and_destroy(self, *, timeout_s: float = 20.0) -> Result[int, str]: stop_result = self.stop_node(timeout_s=timeout_s) @@ -264,7 +263,7 @@ class NodeWrapper: if rc != 0: return Err(f"subscribe_content_topic: immediate call failed (ret={rc})") - return _wait_cb_status(state, f"subscribe({content_topic})", timeout_s) + return _wait_cb_ok(state, f"subscribe({content_topic})", timeout_s) def unsubscribe_content_topic(self, content_topic: str, *, timeout_s: float = 20.0) -> Result[int, str]: state = _new_cb_state() @@ -279,7 +278,7 @@ class NodeWrapper: if rc != 0: return Err(f"unsubscribe_content_topic: immediate call failed (ret={rc})") - return _wait_cb_status(state, f"unsubscribe({content_topic})", timeout_s) + return _wait_cb_ok(state, f"unsubscribe({content_topic})", timeout_s) def send_message(self, message: dict, *, timeout_s: float = 20.0) -> Result[str, str]: state = _new_cb_state() @@ -293,8 +292,7 @@ class NodeWrapper: ffi.NULL, message_json.encode("utf-8"), ) - - if rc < 0: + if rc != 0: return Err(f"send_message: immediate call failed (ret={rc})") wait_result = _wait_cb_raw(state, "send_message", timeout_s) @@ -303,7 +301,7 @@ class NodeWrapper: cb_ret, cb_msg = wait_result.ok_value if cb_ret != 0: - return Err(f"send_message: failed (ret={cb_ret}) msg={cb_msg!r}") + return Err(f"send_message: callback failed (ret={cb_ret}) msg={cb_msg!r}") request_id = cb_msg.decode("utf-8") if cb_msg else "" return Ok(request_id) @@ -312,20 +310,23 @@ class NodeWrapper: state = _new_cb_state() cb = self._make_waiting_cb(state) - node_info_id = lib.logosdelivery_get_available_node_info_ids( + rc = lib.logosdelivery_get_available_node_info_ids( self.ctx, cb, ffi.NULL, ) - - if node_info_id < 0: - return Err(f"get_available_node_info_ids: immediate call failed (ret={node_info_id})") + if rc < 0: + return Err(f"get_available_node_info_ids: immediate call failed (ret={rc})") wait_result = _wait_cb_raw(state, "get_available_node_info_ids", timeout_s) if wait_result.is_err(): return Err(wait_result.err()) - return Ok(node_info_id) + cb_ret, cb_msg = wait_result.ok_value + if cb_ret != 0: + return Err(f"get_available_node_info_ids: callback failed (ret={cb_ret}) msg={cb_msg!r}") + + return Ok(rc) def get_node_info(self, node_info_id: str, *, timeout_s: float = 20.0) -> Result[dict, str]: state = _new_cb_state() @@ -337,31 +338,34 @@ class NodeWrapper: ffi.NULL, node_info_id.encode("utf-8"), ) + if rc != 0: + return Err(f"get_node_info: immediate call failed (ret={rc})") - if rc < 0: - return Err(f"call failed rc={rc}") + wait_result = _wait_cb_raw(state, "get_node_info", timeout_s) + if wait_result.is_err(): + return Err(wait_result.err()) - wait = _wait_cb_raw(state, "get_node_info", timeout_s) - if wait.is_err(): - return Err(wait.err()) + cb_ret, cb_msg = wait_result.ok_value + if cb_ret != 0: + return Err(f"get_node_info: callback failed (ret={cb_ret}) msg={cb_msg!r}") - cb_ret, cb_msg = wait.ok_value - if cb_ret != 0 or not cb_msg: - return Err(f"callback failed ret={cb_ret}") + if not cb_msg: + return Err("get_node_info: empty response") try: - return Ok(json.loads(cb_msg.decode())) - except Exception: + result = json.loads(cb_msg.decode("utf-8")) + except Exception as e: + return Err(f"get_node_info: invalid json: {e}") - return Err("invalid json") + return Ok(result) - def get_available_configs(self, *, timeout_s: float = 20.0) -> Result[bytes, str]: + def get_available_configs(self, *, timeout_s: float = 20.0) -> Result[dict, str]: state = _new_cb_state() cb = self._make_waiting_cb(state) rc = lib.logosdelivery_get_available_configs(self.ctx, cb, ffi.NULL) if rc != 0: - return Err(f"get_available_configs failed: {rc}") + return Err(f"get_available_configs: immediate call failed (ret={rc})") wait_result = _wait_cb_raw(state, "get_available_configs", timeout_s) if wait_result.is_err(): @@ -369,52 +373,14 @@ class NodeWrapper: cb_ret, cb_msg = wait_result.ok_value if cb_ret != 0: - return Err(f"get_available_configs failed: {cb_ret}") + return Err(f"get_available_configs: callback failed (ret={cb_ret}) msg={cb_msg!r}") - return Ok(cb_msg) + if not cb_msg: + return Err("get_available_configs: empty response") -def main(): - config = { - "logLevel": "DEBUG", - "mode": "Core", - "protocolsConfig": { - "entryNodes": [ - "/dns4/node-01.do-ams3.misc.logos-chat.status.im/tcp/30303/p2p/16Uiu2HAkxoqUTud5LUPQBRmkeL2xP4iKx2kaABYXomQRgmLUgf78" - ], - "clusterId": 3, - "autoShardingConfig": {"numShardsInCluster": 8}, - }, - "networkingConfig": { - "listenIpv4": "0.0.0.0", - "p2pTcpPort": 60000, - "discv5UdpPort": 9000, - }, - } - - topic = "/test/1/chat/proto" - - message = { - "contentTopic": topic, - "payload": "SGVsbG8=", - "ephemeral": False, - } - - node_result = NodeWrapper.create_and_start(config) - sleep(20) - if node_result.is_err(): - print(node_result.err()) - return - - node = node_result.ok_value - - print(node.subscribe_content_topic(topic)) - print(node.send_message(message)) - #print(node.get_available_node_info_ids()) - #print(node.debug_get_available_configs()) - print(node.stop_node()) - print(node.destroy()) - - -if __name__ == "__main__": - main() + try: + result = json.loads(cb_msg.decode("utf-8")) + except Exception as e: + return Err(f"get_available_configs: invalid json: {e}") + return Ok(result) \ No newline at end of file From 2a9a1f37bebaf441974cd99311508f2a9d988913 Mon Sep 17 00:00:00 2001 From: Ivan FB Date: Mon, 30 Mar 2026 13:30:04 +0200 Subject: [PATCH 07/15] Update logos-delivery submodule pointer --- vendor/logos-delivery | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/logos-delivery b/vendor/logos-delivery index ba85873..0623c10 160000 --- a/vendor/logos-delivery +++ b/vendor/logos-delivery @@ -1 +1 @@ -Subproject commit ba85873f03a1da6ab04287949849815fd97b7bfd +Subproject commit 0623c10635e5f00cd73db50e28aafc535b4b59ee From f4d79d13c65a8bad8d54a90ffb16a89ecd16a2b2 Mon Sep 17 00:00:00 2001 From: Aya Hassan Date: Wed, 1 Apr 2026 13:22:27 +0200 Subject: [PATCH 08/15] Ignore submodule --- vendor/logos-delivery | 1 - 1 file changed, 1 deletion(-) delete mode 160000 vendor/logos-delivery diff --git a/vendor/logos-delivery b/vendor/logos-delivery deleted file mode 160000 index 0623c10..0000000 --- a/vendor/logos-delivery +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0623c10635e5f00cd73db50e28aafc535b4b59ee From 392808f3d260e840520924d0af3678ddadccdb90 Mon Sep 17 00:00:00 2001 From: Aya Hassan Date: Wed, 1 Apr 2026 13:39:19 +0200 Subject: [PATCH 09/15] Ignore vendor --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index eac0912..3534fad 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ venv/ dist/ waku.egg-info/ waku/__pycache__/ + +vendor/logos-delivery From 4ed432de94a65d00536dda5eab04698b5d1606e0 Mon Sep 17 00:00:00 2001 From: Aya Hassan Date: Thu, 2 Apr 2026 15:20:07 +0200 Subject: [PATCH 10/15] revert change --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3534fad..cb7b31c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,3 @@ dist/ waku.egg-info/ waku/__pycache__/ -vendor/logos-delivery From 3ddad013f761ed9e1d28873adf700ca3daf2f853 Mon Sep 17 00:00:00 2001 From: Aya Hassan Date: Thu, 2 Apr 2026 16:58:51 +0200 Subject: [PATCH 11/15] Fix review comment --- waku/wrapper.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/waku/wrapper.py b/waku/wrapper.py index b53fd17..e094c6c 100644 --- a/waku/wrapper.py +++ b/waku/wrapper.py @@ -306,7 +306,7 @@ class NodeWrapper: request_id = cb_msg.decode("utf-8") if cb_msg else "" return Ok(request_id) - def get_available_node_info_ids(self, *, timeout_s: float = 20.0) -> Result[int, str]: + def get_available_node_info_ids(self, *, timeout_s: float = 20.0) -> Result[list[str], str]: state = _new_cb_state() cb = self._make_waiting_cb(state) @@ -315,8 +315,8 @@ class NodeWrapper: cb, ffi.NULL, ) - if rc < 0: - return Err(f"get_available_node_info_ids: immediate call failed (ret={rc})") + if rc != 0: + return Err("call failed") wait_result = _wait_cb_raw(state, "get_available_node_info_ids", timeout_s) if wait_result.is_err(): @@ -324,9 +324,22 @@ class NodeWrapper: cb_ret, cb_msg = wait_result.ok_value if cb_ret != 0: - return Err(f"get_available_node_info_ids: callback failed (ret={cb_ret}) msg={cb_msg!r}") + return Err("callback failed") - return Ok(rc) + if not cb_msg: + return Err("empty") + + text = cb_msg.decode("utf-8").strip() + + if not text.startswith("@[") or not text.endswith("]"): + return Err("bad format") + + inner = text[2:-1].strip() + if not inner: + return Ok([]) + + items = [item.strip() for item in inner.split(",")] + return Ok(items) def get_node_info(self, node_info_id: str, *, timeout_s: float = 20.0) -> Result[dict, str]: state = _new_cb_state() @@ -383,4 +396,5 @@ class NodeWrapper: except Exception as e: return Err(f"get_available_configs: invalid json: {e}") - return Ok(result) \ No newline at end of file + return Ok(result) + From 5aed7aca968ac19e8924e5bffb6a0e77437dca42 Mon Sep 17 00:00:00 2001 From: Aya Hassan Date: Thu, 2 Apr 2026 17:04:18 +0200 Subject: [PATCH 12/15] Fix review comment again --- waku/wrapper.py | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/waku/wrapper.py b/waku/wrapper.py index e094c6c..ea2f8dc 100644 --- a/waku/wrapper.py +++ b/waku/wrapper.py @@ -310,13 +310,9 @@ class NodeWrapper: state = _new_cb_state() cb = self._make_waiting_cb(state) - rc = lib.logosdelivery_get_available_node_info_ids( - self.ctx, - cb, - ffi.NULL, - ) + rc = lib.logosdelivery_get_available_node_info_ids(self.ctx, cb, ffi.NULL) if rc != 0: - return Err("call failed") + return Err("fail") wait_result = _wait_cb_raw(state, "get_available_node_info_ids", timeout_s) if wait_result.is_err(): @@ -324,23 +320,15 @@ class NodeWrapper: cb_ret, cb_msg = wait_result.ok_value if cb_ret != 0: - return Err("callback failed") - - if not cb_msg: - return Err("empty") + return Err("fail") text = cb_msg.decode("utf-8").strip() - if not text.startswith("@[") or not text.endswith("]"): - return Err("bad format") - - inner = text[2:-1].strip() - if not inner: - return Ok([]) - - items = [item.strip() for item in inner.split(",")] - return Ok(items) + # simple parse + inner = text.replace("@[", "").replace("]", "").strip() + return Ok([] if not inner else [x.strip() for x in inner.split(",")]) + def get_node_info(self, node_info_id: str, *, timeout_s: float = 20.0) -> Result[dict, str]: state = _new_cb_state() cb = self._make_waiting_cb(state) From 03a34e4fe543d8c18ec0a89ae4a9c533699ddbee Mon Sep 17 00:00:00 2001 From: Aya Hassan Date: Thu, 2 Apr 2026 17:08:47 +0200 Subject: [PATCH 13/15] change API to simpler version --- waku/wrapper.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/waku/wrapper.py b/waku/wrapper.py index ea2f8dc..d16937c 100644 --- a/waku/wrapper.py +++ b/waku/wrapper.py @@ -312,7 +312,7 @@ class NodeWrapper: rc = lib.logosdelivery_get_available_node_info_ids(self.ctx, cb, ffi.NULL) if rc != 0: - return Err("fail") + return Err(f"get_available_node_info_ids: immediate call failed (ret={rc})") wait_result = _wait_cb_raw(state, "get_available_node_info_ids", timeout_s) if wait_result.is_err(): @@ -320,15 +320,15 @@ class NodeWrapper: cb_ret, cb_msg = wait_result.ok_value if cb_ret != 0: - return Err("fail") + return Err(f"get_available_node_info_ids: callback failed (ret={cb_ret})") + if not cb_msg: + return Err("get_available_node_info_ids: empty response") - text = cb_msg.decode("utf-8").strip() + try: + return Ok(json.loads(cb_msg.decode("utf-8").strip().lstrip("@"))) + except Exception as e: + return Err(f"get_available_node_info_ids: invalid response: {e}") - # simple parse - inner = text.replace("@[", "").replace("]", "").strip() - - return Ok([] if not inner else [x.strip() for x in inner.split(",")]) - def get_node_info(self, node_info_id: str, *, timeout_s: float = 20.0) -> Result[dict, str]: state = _new_cb_state() cb = self._make_waiting_cb(state) From cc978c3190875681232228347f0729b4426442f0 Mon Sep 17 00:00:00 2001 From: Aya Hassan Date: Tue, 7 Apr 2026 17:03:35 +0200 Subject: [PATCH 14/15] Fix review comment --- waku/wrapper.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/waku/wrapper.py b/waku/wrapper.py index d16937c..e2edcc0 100644 --- a/waku/wrapper.py +++ b/waku/wrapper.py @@ -97,7 +97,11 @@ def _new_cb_state(): } -def _wait_cb_raw(state, op_name: str, timeout_s: float = 20.0): +def _wait_cb_raw( + state, + op_name: str, + timeout_s: float = 20.0, +) -> Result[tuple[int, bytes], str]: ok = state["done"].wait(timeout_s) if not ok: return Err(f"{op_name}: timeout after {timeout_s}s") From 44811368331fd24f22a5f2095b8247a32ea88b23 Mon Sep 17 00:00:00 2001 From: AYAHASSAN287 <49167455+AYAHASSAN287@users.noreply.github.com> Date: Tue, 7 Apr 2026 17:15:23 +0200 Subject: [PATCH 15/15] Update waku/wrapper.py Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com> --- waku/wrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/waku/wrapper.py b/waku/wrapper.py index e2edcc0..d232388 100644 --- a/waku/wrapper.py +++ b/waku/wrapper.py @@ -119,7 +119,7 @@ def _wait_cb_ok(state, op_name: str, timeout_s: float = 20.0) -> Result[int, str cb_ret, cb_msg = wait_result.ok_value if cb_ret != 0: - return Err(f"{op_name}: callback failed (ret={cb_ret}) msg={cb_msg!r}") + return Err(f"callback failed in _wait_cb_ok: {op_name} (ret={cb_ret}) msg={cb_msg!r}") return Ok(cb_ret)