mirror of
https://github.com/logos-messaging/logos-delivery.git
synced 2026-08-09 05:13:14 +00:00
feat(ffi)!: migrate liblogosdelivery to the nim-ffi 0.3.0 typed C ABI (#4082)
This commit is contained in:
parent
13d9b52f4a
commit
4a85db1b6a
3
.gitignore
vendored
3
.gitignore
vendored
@ -91,3 +91,6 @@ nimbledeps
|
|||||||
# Python bytecode from tests/simulator
|
# Python bytecode from tests/simulator
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
|
|
||||||
|
# Emitted by genBindings() during the liblogosdelivery build.
|
||||||
|
library/generated/
|
||||||
|
|||||||
@ -86,13 +86,17 @@ void event_callback(int ret, const char *msg, size_t len, void *userData) {
|
|||||||
### 2. Register the Callback
|
### 2. Register the Callback
|
||||||
|
|
||||||
Register the callback once per event name you want to receive. Each call returns a
|
Register the callback once per event name you want to receive. Each call returns a
|
||||||
listener id you can later pass to `logosdelivery_remove_event_listener(ctx, id)`.
|
listener id you can later pass to `logosdelivery_remove_event_listener(rawCtx, id)`.
|
||||||
|
|
||||||
|
The event API takes the raw context, which is the `ptr` field of the
|
||||||
|
`LogosDeliveryCtx` that `logosdelivery_ctx_create` hands to its callback.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void *ctx = logosdelivery_create_node(config, callback, userData);
|
// ctx comes from the logosdelivery_ctx_create callback; see the README.
|
||||||
logosdelivery_add_event_listener(ctx, "onMessageSent", event_callback, NULL);
|
void *rawCtx = ctx->ptr;
|
||||||
logosdelivery_add_event_listener(ctx, "onMessagePropagated", event_callback, NULL);
|
logosdelivery_add_event_listener(rawCtx, "onMessageSent", event_callback, NULL);
|
||||||
logosdelivery_add_event_listener(ctx, "onMessageError", event_callback, NULL);
|
logosdelivery_add_event_listener(rawCtx, "onMessagePropagated", event_callback, NULL);
|
||||||
|
logosdelivery_add_event_listener(rawCtx, "onMessageError", event_callback, NULL);
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Start the Node
|
### 3. Start the Node
|
||||||
@ -100,7 +104,7 @@ logosdelivery_add_event_listener(ctx, "onMessageError", event_callback, NULL);
|
|||||||
Once the node is started, events will be delivered to your callback:
|
Once the node is started, events will be delivered to your callback:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
logosdelivery_start_node(ctx, callback, userData);
|
logosdelivery_ctx_start_node(ctx, on_reply, userData);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Event Flow
|
## Event Flow
|
||||||
|
|||||||
@ -6,27 +6,68 @@ A C FFI library providing a simplified interface to Logos Messaging functionalit
|
|||||||
|
|
||||||
This library wraps the high-level API functions from `waku/api/api.nim` and exposes them via a C FFI interface, making them accessible from C, C++, and other languages that support C FFI.
|
This library wraps the high-level API functions from `waku/api/api.nim` and exposes them via a C FFI interface, making them accessible from C, C++, and other languages that support C FFI.
|
||||||
|
|
||||||
|
The call surface is generated by nim-ffi from the `{.ffi.}` annotations in
|
||||||
|
`library/*.nim`. `make liblogosdelivery` writes it to
|
||||||
|
`library/generated/logosdelivery.h` on every build, so it can never drift from
|
||||||
|
the Nim signatures. It is a build artifact and is not checked in: build the
|
||||||
|
library before you compile anything against it.
|
||||||
|
|
||||||
|
Include `library/liblogosdelivery.h`, which pulls in the generated header and
|
||||||
|
adds the event-listener ABI.
|
||||||
|
|
||||||
|
Every entry point takes the context handle (`void *ctx`) first, except the
|
||||||
|
constructor. The rest of the signature depends on the call:
|
||||||
|
|
||||||
|
- No-argument calls (`start_node`, `stop_node`, `get_available_configs`,
|
||||||
|
`get_available_node_info_ids`) take a raw `LogosDeliveryScalarRawFn`:
|
||||||
|
`(void *ctx, LogosDeliveryScalarRawFn cb, void *userData)`.
|
||||||
|
- Argument-taking calls (`subscribe`, `unsubscribe`, `send`, `get_node_info`)
|
||||||
|
take a per-call `LogosDelivery<Name>ReplyFn` and pass their arguments last, in
|
||||||
|
a request struct:
|
||||||
|
`(void *ctx, LogosDelivery<Name>ReplyFn onReply, void *userData, const <Name>Req *req)`.
|
||||||
|
|
||||||
|
The generator emits one reply typedef per call (e.g. `LogosDeliverySubscribeReplyFn`),
|
||||||
|
all with the same shape:
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef void (*LogosDeliveryScalarRawFn)(int callerRet, char *msg, size_t len, void *userData);
|
||||||
|
typedef void (*LogosDeliverySubscribeReplyFn)(int errCode, const char *reply, const char *errMsg, void *userData);
|
||||||
|
```
|
||||||
|
|
||||||
|
`reply`, `errMsg` and `msg` are borrowed: copy them if you need them after the
|
||||||
|
callback returns.
|
||||||
|
|
||||||
## API Functions
|
## API Functions
|
||||||
|
|
||||||
### Node Lifecycle
|
### Node Lifecycle
|
||||||
|
|
||||||
#### `logosdelivery_create_node`
|
#### `logosdelivery_create_node`
|
||||||
Creates a new instance of the node from the given configuration JSON.
|
Creates a node from the given configuration JSON.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
|
typedef struct { const char *configJson; } CreateNodeCtorReq;
|
||||||
|
|
||||||
|
typedef void (*LogosDeliveryCreateRawFn)(
|
||||||
|
int errCode,
|
||||||
|
const char *ctxAddr, // context address as decimal text, on success
|
||||||
|
const char *errMsg,
|
||||||
|
void *userData
|
||||||
|
);
|
||||||
|
|
||||||
void *logosdelivery_create_node(
|
void *logosdelivery_create_node(
|
||||||
const char *configJson,
|
const CreateNodeCtorReq *req,
|
||||||
FFICallBack callback,
|
LogosDeliveryCreateRawFn onCreated,
|
||||||
void *userData
|
void *userData
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
- `configJson`: JSON string containing node configuration
|
- `req->configJson`: JSON string containing node configuration
|
||||||
- `callback`: Callback function to receive the result
|
- `onCreated`: Callback that receives the terminal result
|
||||||
- `userData`: User data passed to the callback
|
- `userData`: User data passed to the callback
|
||||||
|
|
||||||
**Returns:** Pointer to the context needed by other API functions, or NULL on error.
|
**Returns:** the context handle, or `NULL` on failure. Creation is asynchronous:
|
||||||
|
wait for `onCreated` before you make any other call.
|
||||||
|
|
||||||
**Example configuration JSON:**
|
**Example configuration JSON:**
|
||||||
```json
|
```json
|
||||||
@ -63,33 +104,22 @@ Available presets:
|
|||||||
Starts the node.
|
Starts the node.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
int logosdelivery_start_node(
|
int logosdelivery_start_node(void *ctx, LogosDeliveryScalarRawFn callback, void *userData);
|
||||||
void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData
|
|
||||||
);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `logosdelivery_stop_node`
|
#### `logosdelivery_stop_node`
|
||||||
Stops the node.
|
Stops the node.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
int logosdelivery_stop_node(
|
int logosdelivery_stop_node(void *ctx, LogosDeliveryScalarRawFn callback, void *userData);
|
||||||
void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData
|
|
||||||
);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `logosdelivery_destroy`
|
#### `logosdelivery_destroy`
|
||||||
Destroys a node instance and frees resources.
|
Destroys a node instance and frees resources. This call is synchronous; do not
|
||||||
|
use `ctx` afterwards.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
int logosdelivery_destroy(
|
int logosdelivery_destroy(void *ctx);
|
||||||
void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData
|
|
||||||
);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Messaging
|
### Messaging
|
||||||
@ -98,29 +128,33 @@ int logosdelivery_destroy(
|
|||||||
Subscribe to a content topic to receive messages.
|
Subscribe to a content topic to receive messages.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
|
typedef struct { const char *contentTopicStr; } SubscribeReq;
|
||||||
|
|
||||||
int logosdelivery_subscribe(
|
int logosdelivery_subscribe(
|
||||||
void *ctx,
|
void *ctx,
|
||||||
FFICallBack callback,
|
LogosDeliverySubscribeReplyFn onReply,
|
||||||
void *userData,
|
void *userData,
|
||||||
const char *contentTopic
|
const SubscribeReq *req
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
- `ctx`: Context pointer from `logosdelivery_create_node`
|
- `ctx`: Context handle returned by `logosdelivery_create_node`
|
||||||
- `callback`: Callback function to receive the result
|
- `req->contentTopicStr`: Content topic string (e.g., "/myapp/1/chat/proto")
|
||||||
|
- `onReply`: Callback function to receive the result
|
||||||
- `userData`: User data passed to the callback
|
- `userData`: User data passed to the callback
|
||||||
- `contentTopic`: Content topic string (e.g., "/myapp/1/chat/proto")
|
|
||||||
|
|
||||||
#### `logosdelivery_unsubscribe`
|
#### `logosdelivery_unsubscribe`
|
||||||
Unsubscribe from a content topic.
|
Unsubscribe from a content topic.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
|
typedef struct { const char *contentTopicStr; } UnsubscribeReq;
|
||||||
|
|
||||||
int logosdelivery_unsubscribe(
|
int logosdelivery_unsubscribe(
|
||||||
void *ctx,
|
void *ctx,
|
||||||
FFICallBack callback,
|
LogosDeliveryUnsubscribeReplyFn onReply,
|
||||||
void *userData,
|
void *userData,
|
||||||
const char *contentTopic
|
const UnsubscribeReq *req
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -128,16 +162,18 @@ int logosdelivery_unsubscribe(
|
|||||||
Send a message.
|
Send a message.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
|
typedef struct { const char *messageJson; } SendReq;
|
||||||
|
|
||||||
int logosdelivery_send(
|
int logosdelivery_send(
|
||||||
void *ctx,
|
void *ctx,
|
||||||
FFICallBack callback,
|
LogosDeliverySendReplyFn onReply,
|
||||||
void *userData,
|
void *userData,
|
||||||
const char *messageJson
|
const SendReq *req
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
- `messageJson`: JSON string containing the message
|
- `req->messageJson`: JSON string containing the message
|
||||||
|
|
||||||
**Example message JSON:**
|
**Example message JSON:**
|
||||||
```json
|
```json
|
||||||
@ -206,15 +242,43 @@ make liblogosdeliveryDynamic # Build dynamic library
|
|||||||
|
|
||||||
All functions that return `int` use the following return codes:
|
All functions that return `int` use the following return codes:
|
||||||
|
|
||||||
- `RET_OK` (0): Success
|
- `NIMFFI_RET_OK` / `RET_OK` (0): Success
|
||||||
- `RET_ERR` (1): Error
|
- `NIMFFI_RET_ERR` / `RET_ERR` (1): Error
|
||||||
- `RET_MISSING_CALLBACK` (2): Missing callback function
|
- `NIMFFI_RET_MISSING_CALLBACK` / `RET_MISSING_CALLBACK` (2): Missing callback function
|
||||||
|
- `NIMFFI_RET_STALE_WARN` (3): Non-terminal progress tick, always followed by a
|
||||||
|
terminal code. Ignore it unless you want progress.
|
||||||
|
|
||||||
## Callback Function
|
## Callback Functions
|
||||||
|
|
||||||
All API functions use the following callback signature:
|
Results come back through one of four callback shapes. The generated names carry
|
||||||
|
the library prefix (`LogosDelivery`); the reply typedef is emitted once per call.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
|
// Argument-taking calls: one typedef per call, all this shape.
|
||||||
|
typedef void (*LogosDeliverySubscribeReplyFn)(
|
||||||
|
int errCode,
|
||||||
|
const char *reply,
|
||||||
|
const char *errMsg,
|
||||||
|
void *userData
|
||||||
|
);
|
||||||
|
|
||||||
|
// No-argument calls (start/stop/get_available_*).
|
||||||
|
typedef void (*LogosDeliveryScalarRawFn)(
|
||||||
|
int callerRet,
|
||||||
|
char *msg,
|
||||||
|
size_t len,
|
||||||
|
void *userData
|
||||||
|
);
|
||||||
|
|
||||||
|
// Constructor.
|
||||||
|
typedef void (*LogosDeliveryCreateRawFn)(
|
||||||
|
int errCode,
|
||||||
|
const char *ctxAddr,
|
||||||
|
const char *errMsg,
|
||||||
|
void *userData
|
||||||
|
);
|
||||||
|
|
||||||
|
// Event listeners (declared by liblogosdelivery.h, not the generated header).
|
||||||
typedef void (*FFICallBack)(
|
typedef void (*FFICallBack)(
|
||||||
int callerRet,
|
int callerRet,
|
||||||
const char *msg,
|
const char *msg,
|
||||||
@ -223,44 +287,68 @@ typedef void (*FFICallBack)(
|
|||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
**Parameters:**
|
- Reply typedefs (`LogosDelivery<Name>ReplyFn`): `reply` is the result on success
|
||||||
- `callerRet`: Return code (RET_OK, RET_ERR, etc.)
|
(NUL-terminated, may be empty); `errMsg` is the message on failure.
|
||||||
- `msg`: Response message (may be empty for success)
|
- `LogosDeliveryScalarRawFn` and `FFICallBack`: `msg` holds `len` bytes and is
|
||||||
- `len`: Length of the message
|
not NUL-terminated.
|
||||||
- `userData`: User data passed in the original call
|
- `LogosDeliveryCreateRawFn`: `ctxAddr` is the context address as decimal text on
|
||||||
|
success.
|
||||||
|
|
||||||
|
All of these strings are borrowed and valid only for the duration of the call.
|
||||||
|
Copy them if you need them afterwards.
|
||||||
|
|
||||||
## Example Usage
|
## Example Usage
|
||||||
|
|
||||||
```c
|
```c
|
||||||
#include "liblogosdelivery.h"
|
#include "liblogosdelivery.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
void callback(int ret, const char *msg, size_t len, void *userData) {
|
static volatile int created = -1;
|
||||||
|
static void *node = NULL;
|
||||||
|
|
||||||
|
// The argument-taking calls share this reply shape.
|
||||||
|
void on_reply(int ret, const char *reply, const char *errMsg, void *userData) {
|
||||||
if (ret == RET_OK) {
|
if (ret == RET_OK) {
|
||||||
printf("Success: %.*s\n", (int)len, msg);
|
printf("Success: %s\n", reply ? reply : "");
|
||||||
} else {
|
} else {
|
||||||
printf("Error: %.*s\n", (int)len, msg);
|
printf("Error: %s\n", errMsg ? errMsg : "unknown error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The no-argument calls (start/stop) take the raw callback.
|
||||||
|
void on_scalar(int ret, char *msg, size_t len, void *userData) {
|
||||||
|
if (ret == RET_STALE_WARN) return; // progress tick, ignore
|
||||||
|
printf("%.*s\n", (int)len, msg ? msg : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_created(int ret, const char *ctxAddr, const char *errMsg, void *userData) {
|
||||||
|
created = (ret == RET_OK);
|
||||||
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
const char *config = "{"
|
const char *config = "{"
|
||||||
"\"logLevel\": \"INFO\","
|
|
||||||
"\"mode\": \"Core\","
|
"\"mode\": \"Core\","
|
||||||
"\"preset\": \"logos.dev\""
|
"\"preset\": \"logos.dev\""
|
||||||
"}";
|
"}";
|
||||||
|
|
||||||
// Create node
|
// Create the node. The return value is the context handle; wait for
|
||||||
void *ctx = logosdelivery_create_node(config, callback, NULL);
|
// on_created before making any other call.
|
||||||
if (ctx == NULL) {
|
CreateNodeCtorReq createReq = { .configJson = config };
|
||||||
|
node = logosdelivery_create_node(&createReq, on_created, NULL);
|
||||||
|
for (int i = 0; i < 100 && created == -1; i++) {
|
||||||
|
usleep(100000);
|
||||||
|
}
|
||||||
|
if (created != 1 || node == NULL) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start node
|
// Start node
|
||||||
logosdelivery_start_node(ctx, callback, NULL);
|
logosdelivery_start_node(node, on_scalar, NULL);
|
||||||
|
|
||||||
// Subscribe to a topic
|
// Subscribe to a topic
|
||||||
logosdelivery_subscribe(ctx, callback, NULL, "/myapp/1/chat/proto");
|
SubscribeReq subReq = { .contentTopicStr = "/myapp/1/chat/proto" };
|
||||||
|
logosdelivery_subscribe(node, on_reply, NULL, &subReq);
|
||||||
|
|
||||||
// Send a message
|
// Send a message
|
||||||
const char *msg = "{"
|
const char *msg = "{"
|
||||||
@ -268,11 +356,12 @@ int main() {
|
|||||||
"\"payload\": \"SGVsbG8gV29ybGQ=\","
|
"\"payload\": \"SGVsbG8gV29ybGQ=\","
|
||||||
"\"ephemeral\": false"
|
"\"ephemeral\": false"
|
||||||
"}";
|
"}";
|
||||||
logosdelivery_send(ctx, callback, NULL, msg);
|
SendReq sendReq = { .messageJson = msg };
|
||||||
|
logosdelivery_send(node, on_reply, NULL, &sendReq);
|
||||||
|
|
||||||
// Clean up
|
// Clean up. logosdelivery_destroy is synchronous.
|
||||||
logosdelivery_stop_node(ctx, callback, NULL);
|
logosdelivery_stop_node(node, on_scalar, NULL);
|
||||||
logosdelivery_destroy(ctx, callback, NULL);
|
logosdelivery_destroy(node);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -282,11 +371,12 @@ int main() {
|
|||||||
|
|
||||||
The library is structured as follows:
|
The library is structured as follows:
|
||||||
|
|
||||||
- `liblogosdelivery.h`: C header file with function declarations
|
- `liblogosdelivery.h`: Public C header; includes the generated header and adds the event ABI
|
||||||
|
- `generated/logosdelivery.h`: Generated call surface, emitted by `make liblogosdelivery` (not checked in)
|
||||||
- `liblogosdelivery.nim`: Main library entry point
|
- `liblogosdelivery.nim`: Main library entry point
|
||||||
- `declare_lib.nim`: Library declaration and initialization
|
- `declare_lib.nim`: Library declaration and initialization
|
||||||
- `lmapi/node_api.nim`: Node lifecycle API implementation
|
- `logos_delivery_api/node_api.nim`: Node lifecycle API implementation
|
||||||
- `lmapi/messaging_api.nim`: Subscribe/send API implementation
|
- `logos_delivery_api/messaging_api.nim`: Subscribe/send API implementation
|
||||||
|
|
||||||
The library uses the nim-ffi framework for FFI infrastructure, which handles:
|
The library uses the nim-ffi framework for FFI infrastructure, which handles:
|
||||||
- Thread-safe request processing
|
- Thread-safe request processing
|
||||||
|
|||||||
@ -8,60 +8,42 @@ import
|
|||||||
../declare_lib
|
../declare_lib
|
||||||
|
|
||||||
proc logosdelivery_channel_create(
|
proc logosdelivery_channel_create(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery,
|
||||||
callback: FFICallBack,
|
channelIdStr: string,
|
||||||
userData: pointer,
|
contentTopicStr: string,
|
||||||
channelIdStr: cstring,
|
senderIdStr: string,
|
||||||
contentTopicStr: cstring,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
senderIdStr: cstring,
|
requireChannels(self, "ChannelCreate"):
|
||||||
) {.ffiRaw.} =
|
|
||||||
requireInitializedNode(ctx, "ChannelCreate"):
|
|
||||||
return err(errMsg)
|
return err(errMsg)
|
||||||
|
|
||||||
requireChannels(ctx, "ChannelCreate"):
|
let id = self.reliableChannelManager.createReliableChannel(
|
||||||
return err(errMsg)
|
ChannelId(channelIdStr),
|
||||||
|
ContentTopic(contentTopicStr),
|
||||||
let id = ctx.myLib[].reliableChannelManager.createReliableChannel(
|
SdsParticipantID(senderIdStr),
|
||||||
ChannelId($channelIdStr),
|
|
||||||
ContentTopic($contentTopicStr),
|
|
||||||
SdsParticipantID($senderIdStr),
|
|
||||||
).valueOr:
|
).valueOr:
|
||||||
return err("ChannelCreate failed: " & $error)
|
return err("ChannelCreate failed: " & $error)
|
||||||
|
|
||||||
return ok(string(id))
|
return ok(string(id))
|
||||||
|
|
||||||
proc logosdelivery_channel_exists(
|
proc logosdelivery_channel_exists(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, channelIdStr: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
|
||||||
channelIdStr: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
## Returns `"true"` or `"false"`; a missing channel is not an error.
|
## Returns `"true"` or `"false"`; a missing channel is not an error.
|
||||||
requireInitializedNode(ctx, "ChannelExists"):
|
requireChannels(self, "ChannelExists"):
|
||||||
return err(errMsg)
|
return err(errMsg)
|
||||||
|
|
||||||
requireChannels(ctx, "ChannelExists"):
|
return ok($self.reliableChannelManager.channelExists(ChannelId(channelIdStr)))
|
||||||
return err(errMsg)
|
|
||||||
|
|
||||||
return ok($ctx.myLib[].reliableChannelManager.channelExists(ChannelId($channelIdStr)))
|
|
||||||
|
|
||||||
proc logosdelivery_channel_send(
|
proc logosdelivery_channel_send(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, channelIdStr: string, messageJson: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
|
||||||
channelIdStr: cstring,
|
|
||||||
messageJson: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
## `messageJson` carries `{ "payload": <base64>, "ephemeral": <bool> }`.
|
## `messageJson` carries `{ "payload": <base64>, "ephemeral": <bool> }`.
|
||||||
requireInitializedNode(ctx, "ChannelSend"):
|
requireChannels(self, "ChannelSend"):
|
||||||
return err(errMsg)
|
|
||||||
|
|
||||||
requireChannels(ctx, "ChannelSend"):
|
|
||||||
return err(errMsg)
|
return err(errMsg)
|
||||||
|
|
||||||
var jsonNode: JsonNode
|
var jsonNode: JsonNode
|
||||||
try:
|
try:
|
||||||
jsonNode = parseJson($messageJson)
|
jsonNode = parseJson(messageJson)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return err("Failed to parse channel message JSON: " & e.msg)
|
return err("Failed to parse channel message JSON: " & e.msg)
|
||||||
|
|
||||||
@ -74,27 +56,19 @@ proc logosdelivery_channel_send(
|
|||||||
let ephemeral = jsonNode.getOrDefault("ephemeral").getBool(false)
|
let ephemeral = jsonNode.getOrDefault("ephemeral").getBool(false)
|
||||||
|
|
||||||
let requestId = (
|
let requestId = (
|
||||||
await ctx.myLib[].reliableChannelManager.send(
|
await self.reliableChannelManager.send(ChannelId(channelIdStr), payload, ephemeral)
|
||||||
ChannelId($channelIdStr), payload, ephemeral
|
|
||||||
)
|
|
||||||
).valueOr:
|
).valueOr:
|
||||||
return err("ChannelSend failed: " & $error)
|
return err("ChannelSend failed: " & $error)
|
||||||
|
|
||||||
return ok($requestId)
|
return ok($requestId)
|
||||||
|
|
||||||
proc logosdelivery_channel_close(
|
proc logosdelivery_channel_close(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, channelIdStr: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
requireChannels(self, "ChannelClose"):
|
||||||
channelIdStr: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
requireInitializedNode(ctx, "ChannelClose"):
|
|
||||||
return err(errMsg)
|
return err(errMsg)
|
||||||
|
|
||||||
requireChannels(ctx, "ChannelClose"):
|
(await self.reliableChannelManager.closeChannel(ChannelId(channelIdStr))).isOkOr:
|
||||||
return err(errMsg)
|
|
||||||
|
|
||||||
(await ctx.myLib[].reliableChannelManager.closeChannel(ChannelId($channelIdStr))).isOkOr:
|
|
||||||
return err("ChannelClose failed: " & $error)
|
return err("ChannelClose failed: " & $error)
|
||||||
|
|
||||||
return ok("")
|
return ok("")
|
||||||
|
|||||||
@ -2,16 +2,7 @@ import ffi
|
|||||||
import results
|
import results
|
||||||
import logos_delivery
|
import logos_delivery
|
||||||
|
|
||||||
declareLibrary("logosdelivery", LogosDelivery)
|
declareLibrary("logosdelivery", LogosDelivery, defaultABIFormat = "c")
|
||||||
|
|
||||||
template checkParams*(
|
|
||||||
ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer
|
|
||||||
) =
|
|
||||||
## Re-implements the `checkParams` helper dropped from nim-ffi in 0.3.0.
|
|
||||||
if not ctx.isNil():
|
|
||||||
ctx[].userData = userData
|
|
||||||
if callback.isNil():
|
|
||||||
return RET_MISSING_CALLBACK
|
|
||||||
|
|
||||||
template emitEvent*(eventName: string, body: untyped) =
|
template emitEvent*(eventName: string, body: untyped) =
|
||||||
## Enqueues `body`'s payload for nim-ffi's event thread to fan out to listeners.
|
## Enqueues `body`'s payload for nim-ffi's event thread to fan out to listeners.
|
||||||
@ -26,30 +17,14 @@ template emitEvent*(eventName: string, body: untyped) =
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
chronicles.error "failed to emit FFI event", event = eventName, err = e.msg
|
chronicles.error "failed to emit FFI event", event = eventName, err = e.msg
|
||||||
|
|
||||||
template requireInitializedNode*(
|
template requireMessaging*(self: LogosDelivery, opName: string, onError: untyped) =
|
||||||
ctx: ptr FFIContext[LogosDelivery], opName: string, onError: untyped
|
## Fails if the node has no messaging client (a kernel-only / fleet node).
|
||||||
) =
|
self.ensureMessaging().isOkOr:
|
||||||
if isNil(ctx):
|
|
||||||
let errMsg {.inject.} = opName & " failed: invalid context"
|
|
||||||
onError
|
|
||||||
elif isNil(ctx.myLib) or isNil(ctx.myLib[]):
|
|
||||||
let errMsg {.inject.} = opName & " failed: node is not initialized"
|
|
||||||
onError
|
|
||||||
|
|
||||||
template requireMessaging*(
|
|
||||||
ctx: ptr FFIContext[LogosDelivery], opName: string, onError: untyped
|
|
||||||
) =
|
|
||||||
## Use after `requireInitializedNode`. Fails if the node has no messaging client
|
|
||||||
## (a kernel-only / fleet node).
|
|
||||||
ctx.myLib[].ensureMessaging().isOkOr:
|
|
||||||
let errMsg {.inject.} = opName & " failed: " & error
|
let errMsg {.inject.} = opName & " failed: " & error
|
||||||
onError
|
onError
|
||||||
|
|
||||||
template requireChannels*(
|
template requireChannels*(self: LogosDelivery, opName: string, onError: untyped) =
|
||||||
ctx: ptr FFIContext[LogosDelivery], opName: string, onError: untyped
|
## Fails if the node has no reliable channel manager (a kernel-only / fleet node).
|
||||||
) =
|
self.ensureChannels().isOkOr:
|
||||||
## Use after `requireInitializedNode`. Fails if the node has no reliable channel
|
|
||||||
## manager (a kernel-only / fleet node).
|
|
||||||
ctx.myLib[].ensureChannels().isOkOr:
|
|
||||||
let errMsg {.inject.} = opName & " failed: " & error
|
let errMsg {.inject.} = opName & " failed: " & error
|
||||||
onError
|
onError
|
||||||
|
|||||||
@ -107,20 +107,35 @@ void event_callback(int ret, const char *msg, size_t len, void *userData) {
|
|||||||
free(eventJson);
|
free(eventJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple callback that prints results
|
// Constructor callback (LogosDeliveryCreateRawFn): reports the terminal result
|
||||||
void simple_callback(int ret, const char *msg, size_t len, void *userData) {
|
// of create_node. `ctxAddr` is the context address as text on success.
|
||||||
const char *operation = (const char *)userData;
|
void on_created(int ret, const char *ctxAddr, const char *errMsg, void *userData) {
|
||||||
|
create_node_ok = (ret == RET_OK) ? 1 : 0;
|
||||||
if (operation != NULL && strcmp(operation, "create_node") == 0) {
|
if (ret != RET_OK) {
|
||||||
create_node_ok = (ret == RET_OK) ? 1 : 0;
|
printf("[create_node] Error: %s\n", errMsg ? errMsg : "unknown error");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply callback for the argument-taking calls (subscribe, unsubscribe, send,
|
||||||
|
// get_node_info). `reply` is the result on success, `errMsg` on failure.
|
||||||
|
void on_reply(int ret, const char *reply, const char *errMsg, void *userData) {
|
||||||
|
const char *operation = (const char *)userData;
|
||||||
if (ret == RET_OK) {
|
if (ret == RET_OK) {
|
||||||
if (len > 0) {
|
printf("[%s] Success: %s\n", operation, reply ? reply : "");
|
||||||
printf("[%s] Success: %.*s\n", operation, (int)len, msg);
|
} else {
|
||||||
} else {
|
printf("[%s] Error: %s\n", operation, errMsg ? errMsg : "unknown error");
|
||||||
printf("[%s] Success\n", operation);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Raw callback for the no-argument calls (start_node, stop_node,
|
||||||
|
// get_available_*). `msg` is `len` bytes and not NUL-terminated.
|
||||||
|
void on_scalar(int ret, char *msg, size_t len, void *userData) {
|
||||||
|
const char *operation = (const char *)userData;
|
||||||
|
if (ret == RET_STALE_WARN) {
|
||||||
|
return; // non-terminal progress tick
|
||||||
|
}
|
||||||
|
if (ret == RET_OK) {
|
||||||
|
printf("[%s] Success: %.*s\n", operation, (int)len, msg);
|
||||||
} else {
|
} else {
|
||||||
printf("[%s] Error: %.*s\n", operation, (int)len, msg);
|
printf("[%s] Error: %.*s\n", operation, (int)len, msg);
|
||||||
}
|
}
|
||||||
@ -140,7 +155,8 @@ int main() {
|
|||||||
"}";
|
"}";
|
||||||
|
|
||||||
printf("1. Creating node...\n");
|
printf("1. Creating node...\n");
|
||||||
void *ctx = logosdelivery_create_node(config, simple_callback, (void *)"create_node");
|
CreateNodeCtorReq createReq = { .configJson = config };
|
||||||
|
void *ctx = logosdelivery_create_node(&createReq, on_created, NULL);
|
||||||
if (ctx == NULL) {
|
if (ctx == NULL) {
|
||||||
printf("Failed to create node\n");
|
printf("Failed to create node\n");
|
||||||
return 1;
|
return 1;
|
||||||
@ -151,7 +167,7 @@ int main() {
|
|||||||
|
|
||||||
if (create_node_ok != 1) {
|
if (create_node_ok != 1) {
|
||||||
printf("Create node failed, stopping example early.\n");
|
printf("Create node failed, stopping example early.\n");
|
||||||
logosdelivery_destroy(ctx, simple_callback, (void *)"destroy");
|
logosdelivery_destroy(ctx);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,33 +178,35 @@ int main() {
|
|||||||
printf("Event listeners registered for message events\n");
|
printf("Event listeners registered for message events\n");
|
||||||
|
|
||||||
printf("\n3. Starting node...\n");
|
printf("\n3. Starting node...\n");
|
||||||
logosdelivery_start_node(ctx, simple_callback, (void *)"start_node");
|
logosdelivery_start_node(ctx, on_scalar, (void *)"start_node");
|
||||||
|
|
||||||
// Wait for node to start
|
// Wait for node to start
|
||||||
sleep(5);
|
sleep(5);
|
||||||
|
|
||||||
printf("\n4. Subscribing to content topic...\n");
|
printf("\n4. Subscribing to content topic...\n");
|
||||||
const char *contentTopic = "/example/1/chat/proto";
|
const char *contentTopic = "/example/1/chat/proto";
|
||||||
logosdelivery_subscribe(ctx, simple_callback, (void *)"subscribe", contentTopic);
|
SubscribeReq subscribeReq = { .contentTopicStr = contentTopic };
|
||||||
|
logosdelivery_subscribe(ctx, on_reply, (void *)"subscribe", &subscribeReq);
|
||||||
|
|
||||||
// Wait for subscription
|
// Wait for subscription
|
||||||
sleep(1);
|
sleep(1);
|
||||||
|
|
||||||
printf("\n5. Retrieving all possibl node info ids...\n");
|
printf("\n5. Retrieving all possible node info ids...\n");
|
||||||
logosdelivery_get_available_node_info_ids(ctx, simple_callback, (void *)"get_available_node_info_ids");
|
logosdelivery_get_available_node_info_ids(ctx, on_scalar, (void *)"get_available_node_info_ids");
|
||||||
|
|
||||||
printf("\nRetrieving node info for a specific invalid ID...\n");
|
printf("\nRetrieving node info for a specific invalid ID...\n");
|
||||||
logosdelivery_get_node_info(ctx, simple_callback, (void *)"get_node_info", "WrongNodeInfoId");
|
GetNodeInfoReq nodeInfoReq = { .nodeInfoId = "WrongNodeInfoId" };
|
||||||
|
logosdelivery_get_node_info(ctx, on_reply, (void *)"get_node_info", &nodeInfoReq);
|
||||||
|
|
||||||
printf("\nRetrieving several node info for specific correct IDs...\n");
|
printf("\nRetrieving several node info for specific correct IDs...\n");
|
||||||
logosdelivery_get_node_info(ctx, simple_callback, (void *)"get_node_info", "Version");
|
const char *nodeInfoIds[] = {"Version", "MyMultiaddresses", "MyENR", "MyPeerId"};
|
||||||
// logosdelivery_get_node_info(ctx, simple_callback, (void *)"get_node_info", "Metrics");
|
for (size_t i = 0; i < sizeof(nodeInfoIds) / sizeof(nodeInfoIds[0]); i++) {
|
||||||
logosdelivery_get_node_info(ctx, simple_callback, (void *)"get_node_info", "MyMultiaddresses");
|
GetNodeInfoReq req = { .nodeInfoId = nodeInfoIds[i] };
|
||||||
logosdelivery_get_node_info(ctx, simple_callback, (void *)"get_node_info", "MyENR");
|
logosdelivery_get_node_info(ctx, on_reply, (void *)"get_node_info", &req);
|
||||||
logosdelivery_get_node_info(ctx, simple_callback, (void *)"get_node_info", "MyPeerId");
|
}
|
||||||
|
|
||||||
printf("\nRetrieving available configs...\n");
|
printf("\nRetrieving available configs...\n");
|
||||||
logosdelivery_get_available_configs(ctx, simple_callback, (void *)"get_available_configs");
|
logosdelivery_get_available_configs(ctx, on_scalar, (void *)"get_available_configs");
|
||||||
|
|
||||||
printf("\n6. Sending a message...\n");
|
printf("\n6. Sending a message...\n");
|
||||||
printf("Watch for message events (sent, propagated, or error):\n");
|
printf("Watch for message events (sent, propagated, or error):\n");
|
||||||
@ -198,7 +216,8 @@ int main() {
|
|||||||
"\"payload\": \"SGVsbG8sIExvZ29zIE1lc3NhZ2luZyE=\","
|
"\"payload\": \"SGVsbG8sIExvZ29zIE1lc3NhZ2luZyE=\","
|
||||||
"\"ephemeral\": false"
|
"\"ephemeral\": false"
|
||||||
"}";
|
"}";
|
||||||
logosdelivery_send(ctx, simple_callback, (void *)"send", message);
|
SendReq sendReq = { .messageJson = message };
|
||||||
|
logosdelivery_send(ctx, on_reply, (void *)"send", &sendReq);
|
||||||
|
|
||||||
// Poll for terminal message events (sent, error, or received) with timeout
|
// Poll for terminal message events (sent, error, or received) with timeout
|
||||||
printf("Waiting for message delivery events...\n");
|
printf("Waiting for message delivery events...\n");
|
||||||
@ -214,17 +233,18 @@ int main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
printf("\n7. Unsubscribing from content topic...\n");
|
printf("\n7. Unsubscribing from content topic...\n");
|
||||||
logosdelivery_unsubscribe(ctx, simple_callback, (void *)"unsubscribe", contentTopic);
|
UnsubscribeReq unsubscribeReq = { .contentTopicStr = contentTopic };
|
||||||
|
logosdelivery_unsubscribe(ctx, on_reply, (void *)"unsubscribe", &unsubscribeReq);
|
||||||
|
|
||||||
sleep(1);
|
sleep(1);
|
||||||
|
|
||||||
printf("\n8. Stopping node...\n");
|
printf("\n8. Stopping node...\n");
|
||||||
logosdelivery_stop_node(ctx, simple_callback, (void *)"stop_node");
|
logosdelivery_stop_node(ctx, on_scalar, (void *)"stop_node");
|
||||||
|
|
||||||
sleep(1);
|
sleep(1);
|
||||||
|
|
||||||
printf("\n9. Destroying context...\n");
|
printf("\n9. Destroying context...\n");
|
||||||
logosdelivery_destroy(ctx, simple_callback, (void *)"destroy");
|
logosdelivery_destroy(ctx);
|
||||||
|
|
||||||
printf("\n=== Example completed ===\n");
|
printf("\n=== Example completed ===\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -2,45 +2,35 @@ import std/strutils
|
|||||||
import chronos, results, ffi
|
import chronos, results, ffi
|
||||||
import logos_delivery, library/declare_lib
|
import logos_delivery, library/declare_lib
|
||||||
|
|
||||||
proc waku_version(
|
proc waku_version(self: LogosDelivery): Future[Result[string, string]] {.ffi.} =
|
||||||
ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer
|
let v = (await self.waku.version()).valueOr:
|
||||||
) {.ffiRaw.} =
|
|
||||||
let v = (await ctx.myLib[].waku.version()).valueOr:
|
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok(v)
|
return ok(v)
|
||||||
|
|
||||||
proc waku_listen_addresses(
|
proc waku_listen_addresses(
|
||||||
ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer
|
self: LogosDelivery
|
||||||
) {.ffiRaw.} =
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
## returns a comma-separated string of the listen addresses
|
## returns a comma-separated string of the listen addresses
|
||||||
let addrs = (await ctx.myLib[].waku.listenAddresses()).valueOr:
|
let addrs = (await self.waku.listenAddresses()).valueOr:
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok(addrs.join(","))
|
return ok(addrs.join(","))
|
||||||
|
|
||||||
proc waku_get_my_enr(
|
proc waku_get_my_enr(self: LogosDelivery): Future[Result[string, string]] {.ffi.} =
|
||||||
ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer
|
let enrUri = (await self.waku.myEnr()).valueOr:
|
||||||
) {.ffiRaw.} =
|
|
||||||
let enrUri = (await ctx.myLib[].waku.myEnr()).valueOr:
|
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok(enrUri)
|
return ok(enrUri)
|
||||||
|
|
||||||
proc waku_get_my_peerid(
|
proc waku_get_my_peerid(self: LogosDelivery): Future[Result[string, string]] {.ffi.} =
|
||||||
ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer
|
let peerId = (await self.waku.myPeerId()).valueOr:
|
||||||
) {.ffiRaw.} =
|
|
||||||
let peerId = (await ctx.myLib[].waku.myPeerId()).valueOr:
|
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok(peerId)
|
return ok(peerId)
|
||||||
|
|
||||||
proc waku_get_metrics(
|
proc waku_get_metrics(self: LogosDelivery): Future[Result[string, string]] {.ffi.} =
|
||||||
ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer
|
let m = (await self.waku.metrics()).valueOr:
|
||||||
) {.ffiRaw.} =
|
|
||||||
let m = (await ctx.myLib[].waku.metrics()).valueOr:
|
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok(m)
|
return ok(m)
|
||||||
|
|
||||||
proc waku_is_online(
|
proc waku_is_online(self: LogosDelivery): Future[Result[string, string]] {.ffi.} =
|
||||||
ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer
|
let online = (await self.waku.isOnline()).valueOr:
|
||||||
) {.ffiRaw.} =
|
|
||||||
let online = (await ctx.myLib[].waku.isOnline()).valueOr:
|
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok($online)
|
return ok($online)
|
||||||
|
|||||||
@ -3,57 +3,40 @@ import chronos, chronicles, results, ffi
|
|||||||
import logos_delivery, library/declare_lib
|
import logos_delivery, library/declare_lib
|
||||||
|
|
||||||
proc waku_discv5_update_bootnodes(
|
proc waku_discv5_update_bootnodes(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, bootnodes: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
|
||||||
bootnodes: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
## Updates the bootnode list used for discovering new peers via DiscoveryV5
|
## Updates the bootnode list used for discovering new peers via DiscoveryV5
|
||||||
## bootnodes - JSON array containing the bootnode ENRs i.e. `["enr:...", "enr:..."]`
|
## bootnodes - JSON array containing the bootnode ENRs i.e. `["enr:...", "enr:..."]`
|
||||||
(await ctx.myLib[].waku.discv5UpdateBootnodes($bootnodes)).isOkOr:
|
(await self.waku.discv5UpdateBootnodes(bootnodes)).isOkOr:
|
||||||
error "UPDATE_DISCV5_BOOTSTRAP_NODES failed", error = error
|
error "UPDATE_DISCV5_BOOTSTRAP_NODES failed", error = error
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok("discovery request processed correctly")
|
return ok("discovery request processed correctly")
|
||||||
|
|
||||||
proc waku_dns_discovery(
|
proc waku_dns_discovery(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, enrTreeUrl: string, nameDnsServer: string, timeoutMs: int32
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
## returns a comma-separated string of bootstrap nodes' multiaddresses
|
||||||
enrTreeUrl: cstring,
|
let nodes = (await self.waku.dnsDiscovery(enrTreeUrl, nameDnsServer, int(timeoutMs))).valueOr:
|
||||||
nameDnsServer: cstring,
|
|
||||||
timeoutMs: cint,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
let nodes = (
|
|
||||||
await ctx.myLib[].waku.dnsDiscovery($enrTreeUrl, $nameDnsServer, int(timeoutMs))
|
|
||||||
).valueOr:
|
|
||||||
error "GET_BOOTSTRAP_NODES failed", error = error
|
error "GET_BOOTSTRAP_NODES failed", error = error
|
||||||
return err(error)
|
return err(error)
|
||||||
## returns a comma-separated string of bootstrap nodes' multiaddresses
|
|
||||||
return ok(nodes.join(","))
|
return ok(nodes.join(","))
|
||||||
|
|
||||||
proc waku_start_discv5(
|
proc waku_start_discv5(self: LogosDelivery): Future[Result[string, string]] {.ffi.} =
|
||||||
ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer
|
(await self.waku.startDiscv5()).isOkOr:
|
||||||
) {.ffiRaw.} =
|
|
||||||
(await ctx.myLib[].waku.startDiscv5()).isOkOr:
|
|
||||||
error "START_DISCV5 failed", error = error
|
error "START_DISCV5 failed", error = error
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok("discv5 started correctly")
|
return ok("discv5 started correctly")
|
||||||
|
|
||||||
proc waku_stop_discv5(
|
proc waku_stop_discv5(self: LogosDelivery): Future[Result[string, string]] {.ffi.} =
|
||||||
ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer
|
(await self.waku.stopDiscv5()).isOkOr:
|
||||||
) {.ffiRaw.} =
|
|
||||||
(await ctx.myLib[].waku.stopDiscv5()).isOkOr:
|
|
||||||
error "STOP_DISCV5 failed", error = error
|
error "STOP_DISCV5 failed", error = error
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok("discv5 stopped correctly")
|
return ok("discv5 stopped correctly")
|
||||||
|
|
||||||
proc waku_peer_exchange_request(
|
proc waku_peer_exchange_request(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, numPeers: uint64
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
let numValidPeers = (await self.waku.peerExchangeRequest(numPeers)).valueOr:
|
||||||
numPeers: uint64,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
let numValidPeers = (await ctx.myLib[].waku.peerExchangeRequest(numPeers)).valueOr:
|
|
||||||
error "waku_peer_exchange_request failed", error = error
|
error "waku_peer_exchange_request failed", error = error
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok($numValidPeers)
|
return ok($numValidPeers)
|
||||||
|
|||||||
@ -7,74 +7,57 @@ type PeerInfo = object
|
|||||||
addresses: seq[string]
|
addresses: seq[string]
|
||||||
|
|
||||||
proc waku_get_peerids_from_peerstore(
|
proc waku_get_peerids_from_peerstore(
|
||||||
ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer
|
self: LogosDelivery
|
||||||
) {.ffiRaw.} =
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
## returns a comma-separated string of peerIDs
|
## returns a comma-separated string of peerIDs
|
||||||
let peerIds = (await ctx.myLib[].waku.peerIdsFromPeerstore()).valueOr:
|
let peerIds = (await self.waku.peerIdsFromPeerstore()).valueOr:
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok(peerIds.join(","))
|
return ok(peerIds.join(","))
|
||||||
|
|
||||||
proc waku_connect(
|
proc waku_connect(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, peerMultiAddr: string, timeoutMs: uint32
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
let peers = peerMultiAddr.split(",")
|
||||||
peerMultiAddr: cstring,
|
(await self.waku.connect(peers, timeoutMs)).isOkOr:
|
||||||
timeoutMs: cuint,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
let peers = ($peerMultiAddr).split(",")
|
|
||||||
(await ctx.myLib[].waku.connect(peers, uint32(timeoutMs))).isOkOr:
|
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok("")
|
return ok("")
|
||||||
|
|
||||||
proc waku_disconnect_peer_by_id(
|
proc waku_disconnect_peer_by_id(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, peerId: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
(await self.waku.disconnectPeerById(peerId)).isOkOr:
|
||||||
peerId: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
(await ctx.myLib[].waku.disconnectPeerById($peerId)).isOkOr:
|
|
||||||
error "DISCONNECT_PEER_BY_ID failed", error = error
|
error "DISCONNECT_PEER_BY_ID failed", error = error
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok("")
|
return ok("")
|
||||||
|
|
||||||
proc waku_disconnect_all_peers(
|
proc waku_disconnect_all_peers(
|
||||||
ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer
|
self: LogosDelivery
|
||||||
) {.ffiRaw.} =
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
(await ctx.myLib[].waku.disconnectAllPeers()).isOkOr:
|
(await self.waku.disconnectAllPeers()).isOkOr:
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok("")
|
return ok("")
|
||||||
|
|
||||||
proc waku_dial_peer(
|
proc waku_dial_peer(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, peerMultiAddr: string, protocol: string, timeoutMs: uint32
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
(await self.waku.dialPeer(peerMultiAddr, protocol, int(timeoutMs))).isOkOr:
|
||||||
peerMultiAddr: cstring,
|
|
||||||
protocol: cstring,
|
|
||||||
timeoutMs: cuint,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
(await ctx.myLib[].waku.dialPeer($peerMultiAddr, $protocol, int(timeoutMs))).isOkOr:
|
|
||||||
error "DIAL_PEER failed", error = error
|
error "DIAL_PEER failed", error = error
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok("")
|
return ok("")
|
||||||
|
|
||||||
proc waku_dial_peer_by_id(
|
proc waku_dial_peer_by_id(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, peerId: string, protocol: string, timeoutMs: uint32
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
(await self.waku.dialPeerById(peerId, protocol, int(timeoutMs))).isOkOr:
|
||||||
peerId: cstring,
|
|
||||||
protocol: cstring,
|
|
||||||
timeoutMs: cuint,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
(await ctx.myLib[].waku.dialPeerById($peerId, $protocol, int(timeoutMs))).isOkOr:
|
|
||||||
error "DIAL_PEER_BY_ID failed", error = error
|
error "DIAL_PEER_BY_ID failed", error = error
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok("")
|
return ok("")
|
||||||
|
|
||||||
proc waku_get_connected_peers_info(
|
proc waku_get_connected_peers_info(
|
||||||
ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer
|
self: LogosDelivery
|
||||||
) {.ffiRaw.} =
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
## returns a JSON string mapping peerIDs to objects with protocols and addresses
|
## returns a JSON string mapping peerIDs to objects with protocols and addresses
|
||||||
let peers = (await ctx.myLib[].waku.connectedPeersInfo()).valueOr:
|
let peers = (await self.waku.connectedPeersInfo()).valueOr:
|
||||||
return err(error)
|
return err(error)
|
||||||
|
|
||||||
var peersMap = initTable[string, PeerInfo]()
|
var peersMap = initTable[string, PeerInfo]()
|
||||||
@ -85,20 +68,17 @@ proc waku_get_connected_peers_info(
|
|||||||
return ok($(%*peersMap))
|
return ok($(%*peersMap))
|
||||||
|
|
||||||
proc waku_get_connected_peers(
|
proc waku_get_connected_peers(
|
||||||
ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer
|
self: LogosDelivery
|
||||||
) {.ffiRaw.} =
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
## returns a comma-separated string of peerIDs
|
## returns a comma-separated string of peerIDs
|
||||||
let peerIds = (await ctx.myLib[].waku.connectedPeers()).valueOr:
|
let peerIds = (await self.waku.connectedPeers()).valueOr:
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok(peerIds.join(","))
|
return ok(peerIds.join(","))
|
||||||
|
|
||||||
proc waku_get_peerids_by_protocol(
|
proc waku_get_peerids_by_protocol(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, protocol: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
|
||||||
protocol: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
## returns a comma-separated string of peerIDs that mount the given protocol
|
## returns a comma-separated string of peerIDs that mount the given protocol
|
||||||
let peerIds = (await ctx.myLib[].waku.peerIdsByProtocol($protocol)).valueOr:
|
let peerIds = (await self.waku.peerIdsByProtocol(protocol)).valueOr:
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok(peerIds.join(","))
|
return ok(peerIds.join(","))
|
||||||
|
|||||||
@ -2,12 +2,8 @@ import chronos, results, ffi
|
|||||||
import logos_delivery, library/declare_lib
|
import logos_delivery, library/declare_lib
|
||||||
|
|
||||||
proc waku_ping_peer(
|
proc waku_ping_peer(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, peerAddr: string, timeoutMs: uint32
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
let rttNanos = (await self.waku.pingPeer(peerAddr, int(timeoutMs))).valueOr:
|
||||||
peerAddr: cstring,
|
|
||||||
timeoutMs: cuint,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
let rttNanos = (await ctx.myLib[].waku.pingPeer($peerAddr, int(timeoutMs))).valueOr:
|
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok($rttNanos)
|
return ok($rttNanos)
|
||||||
|
|||||||
@ -10,22 +10,18 @@ import
|
|||||||
library/declare_lib
|
library/declare_lib
|
||||||
|
|
||||||
proc waku_filter_subscribe(
|
proc waku_filter_subscribe(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, pubSubTopic: string, contentTopics: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
proc onReceivedMessage(): FilterPushHandler =
|
||||||
pubSubTopic: cstring,
|
|
||||||
contentTopics: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
proc onReceivedMessage(ctx: ptr FFIContext[LogosDelivery]): FilterPushHandler =
|
|
||||||
return proc(pubsubTopic: PubsubTopic, msg: WakuMessage) {.async.} =
|
return proc(pubsubTopic: PubsubTopic, msg: WakuMessage) {.async.} =
|
||||||
emitEvent("onReceivedMessage"):
|
emitEvent("onReceivedMessage"):
|
||||||
$JsonMessageEvent.new(pubsubTopic, msg)
|
$JsonMessageEvent.new(pubsubTopic, msg)
|
||||||
|
|
||||||
(
|
(
|
||||||
await ctx.myLib[].waku.filterSubscribe(
|
await self.waku.filterSubscribe(
|
||||||
PubsubTopic($pubSubTopic),
|
PubsubTopic(pubSubTopic),
|
||||||
($contentTopics).split(",").mapIt(ContentTopic(it)),
|
contentTopics.split(",").mapIt(ContentTopic(it)),
|
||||||
FilterPushHandler(onReceivedMessage(ctx)),
|
FilterPushHandler(onReceivedMessage()),
|
||||||
)
|
)
|
||||||
).isOkOr:
|
).isOkOr:
|
||||||
error "fail filter subscribe", error = error
|
error "fail filter subscribe", error = error
|
||||||
@ -33,15 +29,11 @@ proc waku_filter_subscribe(
|
|||||||
return ok("")
|
return ok("")
|
||||||
|
|
||||||
proc waku_filter_unsubscribe(
|
proc waku_filter_unsubscribe(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, pubSubTopic: string, contentTopics: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
|
||||||
pubSubTopic: cstring,
|
|
||||||
contentTopics: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
(
|
(
|
||||||
await ctx.myLib[].waku.filterUnsubscribe(
|
await self.waku.filterUnsubscribe(
|
||||||
PubsubTopic($pubSubTopic), ($contentTopics).split(",").mapIt(ContentTopic(it))
|
PubsubTopic(pubSubTopic), contentTopics.split(",").mapIt(ContentTopic(it))
|
||||||
)
|
)
|
||||||
).isOkOr:
|
).isOkOr:
|
||||||
error "fail filter unsubscribe", error = error
|
error "fail filter unsubscribe", error = error
|
||||||
@ -49,9 +41,9 @@ proc waku_filter_unsubscribe(
|
|||||||
return ok("")
|
return ok("")
|
||||||
|
|
||||||
proc waku_filter_unsubscribe_all(
|
proc waku_filter_unsubscribe_all(
|
||||||
ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer
|
self: LogosDelivery
|
||||||
) {.ffiRaw.} =
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
(await ctx.myLib[].waku.filterUnsubscribeAll()).isOkOr:
|
(await self.waku.filterUnsubscribeAll()).isOkOr:
|
||||||
error "fail filter unsubscribe all", error = error
|
error "fail filter unsubscribe all", error = error
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok("")
|
return ok("")
|
||||||
|
|||||||
@ -8,26 +8,20 @@ import
|
|||||||
library/declare_lib
|
library/declare_lib
|
||||||
|
|
||||||
proc waku_lightpush_publish(
|
proc waku_lightpush_publish(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, pubSubTopic: string, jsonWakuMessage: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
|
||||||
pubSubTopic: cstring,
|
|
||||||
jsonWakuMessage: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
var jsonMessage: JsonMessage
|
var jsonMessage: JsonMessage
|
||||||
try:
|
try:
|
||||||
let jsonContent = parseJson($jsonWakuMessage)
|
let jsonContent = parseJson(jsonWakuMessage)
|
||||||
jsonMessage = JsonMessage.fromJsonNode(jsonContent).valueOr:
|
jsonMessage = JsonMessage.fromJsonNode(jsonContent).valueOr:
|
||||||
raise newException(JsonParsingError, $error)
|
raise newException(JsonParsingError, $error)
|
||||||
except JsonParsingError as exc:
|
except JsonParsingError as e:
|
||||||
return err(fmt"Error parsing json message: {exc.msg}")
|
return err(fmt"Error parsing json message: {e.msg}")
|
||||||
|
|
||||||
let msg = json_message_event.toWakuMessage(jsonMessage).valueOr:
|
let msg = json_message_event.toWakuMessage(jsonMessage).valueOr:
|
||||||
return err("Problem building the WakuMessage: " & $error)
|
return err("Problem building the WakuMessage: " & $error)
|
||||||
|
|
||||||
let msgHashHex = (
|
let msgHashHex = (await self.waku.lightpushPublish(PubsubTopic(pubSubTopic), msg)).valueOr:
|
||||||
await ctx.myLib[].waku.lightpushPublish(PubsubTopic($pubSubTopic), msg)
|
|
||||||
).valueOr:
|
|
||||||
error "PUBLISH failed", error = error
|
error "PUBLISH failed", error = error
|
||||||
return err(error)
|
return err(error)
|
||||||
|
|
||||||
|
|||||||
@ -9,82 +9,58 @@ import
|
|||||||
library/declare_lib
|
library/declare_lib
|
||||||
|
|
||||||
proc waku_relay_get_peers_in_mesh(
|
proc waku_relay_get_peers_in_mesh(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, pubSubTopic: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
## returns a comma-separated string of peerIDs
|
||||||
pubSubTopic: cstring,
|
let peers = (await self.waku.relayPeersInMesh(PubsubTopic(pubSubTopic))).valueOr:
|
||||||
) {.ffiRaw.} =
|
|
||||||
let peers = (await ctx.myLib[].waku.relayPeersInMesh(PubsubTopic($pubSubTopic))).valueOr:
|
|
||||||
error "LIST_MESH_PEERS failed", error = error
|
error "LIST_MESH_PEERS failed", error = error
|
||||||
return err(error)
|
return err(error)
|
||||||
## returns a comma-separated string of peerIDs
|
|
||||||
return ok(peers.join(","))
|
return ok(peers.join(","))
|
||||||
|
|
||||||
proc waku_relay_get_num_peers_in_mesh(
|
proc waku_relay_get_num_peers_in_mesh(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, pubSubTopic: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
let n = (await self.waku.relayNumPeersInMesh(PubsubTopic(pubSubTopic))).valueOr:
|
||||||
pubSubTopic: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
let n = (await ctx.myLib[].waku.relayNumPeersInMesh(PubsubTopic($pubSubTopic))).valueOr:
|
|
||||||
error "NUM_MESH_PEERS failed", error = error
|
error "NUM_MESH_PEERS failed", error = error
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok($n)
|
return ok($n)
|
||||||
|
|
||||||
proc waku_relay_get_connected_peers(
|
proc waku_relay_get_connected_peers(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, pubSubTopic: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
|
||||||
pubSubTopic: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
## Returns the list of all connected peers to an specific pubsub topic
|
## Returns the list of all connected peers to an specific pubsub topic
|
||||||
let peers = (await ctx.myLib[].waku.relayConnectedPeers(PubsubTopic($pubSubTopic))).valueOr:
|
let peers = (await self.waku.relayConnectedPeers(PubsubTopic(pubSubTopic))).valueOr:
|
||||||
error "LIST_CONNECTED_PEERS failed", error = error
|
error "LIST_CONNECTED_PEERS failed", error = error
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok(peers.join(","))
|
return ok(peers.join(","))
|
||||||
|
|
||||||
proc waku_relay_get_num_connected_peers(
|
proc waku_relay_get_num_connected_peers(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, pubSubTopic: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
let n = (await self.waku.relayNumConnectedPeers(PubsubTopic(pubSubTopic))).valueOr:
|
||||||
pubSubTopic: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
let n = (await ctx.myLib[].waku.relayNumConnectedPeers(PubsubTopic($pubSubTopic))).valueOr:
|
|
||||||
error "NUM_CONNECTED_PEERS failed", error = error
|
error "NUM_CONNECTED_PEERS failed", error = error
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok($n)
|
return ok($n)
|
||||||
|
|
||||||
proc waku_relay_add_protected_shard(
|
proc waku_relay_add_protected_shard(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, clusterId: uint16, shardId: uint16, publicKey: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
|
||||||
clusterId: cint,
|
|
||||||
shardId: cint,
|
|
||||||
publicKey: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
## Protects a shard with a public key
|
## Protects a shard with a public key
|
||||||
(
|
(await self.waku.relayAddProtectedShard(clusterId, shardId, publicKey)).isOkOr:
|
||||||
await ctx.myLib[].waku.relayAddProtectedShard(
|
|
||||||
uint16(clusterId), uint16(shardId), $publicKey
|
|
||||||
)
|
|
||||||
).isOkOr:
|
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok("")
|
return ok("")
|
||||||
|
|
||||||
proc waku_relay_subscribe(
|
proc waku_relay_subscribe(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, pubSubTopic: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
proc onReceivedMessage(): WakuRelayHandler =
|
||||||
pubSubTopic: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
proc onReceivedMessage(ctx: ptr FFIContext[LogosDelivery]): WakuRelayHandler =
|
|
||||||
return proc(pubsubTopic: PubsubTopic, msg: WakuMessage) {.async.} =
|
return proc(pubsubTopic: PubsubTopic, msg: WakuMessage) {.async.} =
|
||||||
emitEvent("onReceivedMessage"):
|
emitEvent("onReceivedMessage"):
|
||||||
$JsonMessageEvent.new(pubsubTopic, msg)
|
$JsonMessageEvent.new(pubsubTopic, msg)
|
||||||
|
|
||||||
(
|
(
|
||||||
await ctx.myLib[].waku.relaySubscribe(
|
await self.waku.relaySubscribe(
|
||||||
PubsubTopic($pubSubTopic), WakuRelayHandler(onReceivedMessage(ctx))
|
PubsubTopic(pubSubTopic), WakuRelayHandler(onReceivedMessage())
|
||||||
)
|
)
|
||||||
).isOkOr:
|
).isOkOr:
|
||||||
error "SUBSCRIBE failed", error = error
|
error "SUBSCRIBE failed", error = error
|
||||||
@ -92,74 +68,55 @@ proc waku_relay_subscribe(
|
|||||||
return ok("")
|
return ok("")
|
||||||
|
|
||||||
proc waku_relay_unsubscribe(
|
proc waku_relay_unsubscribe(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, pubSubTopic: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
(await self.waku.relayUnsubscribe(PubsubTopic(pubSubTopic))).isOkOr:
|
||||||
pubSubTopic: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
(await ctx.myLib[].waku.relayUnsubscribe(PubsubTopic($pubSubTopic))).isOkOr:
|
|
||||||
error "UNSUBSCRIBE failed", error = error
|
error "UNSUBSCRIBE failed", error = error
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok("")
|
return ok("")
|
||||||
|
|
||||||
proc waku_relay_publish(
|
proc waku_relay_publish(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, pubSubTopic: string, jsonWakuMessage: string, timeoutMs: uint32
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
|
||||||
pubSubTopic: cstring,
|
|
||||||
jsonWakuMessage: cstring,
|
|
||||||
timeoutMs: cuint,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
var jsonMessage: JsonMessage
|
var jsonMessage: JsonMessage
|
||||||
try:
|
try:
|
||||||
let jsonContent = parseJson($jsonWakuMessage)
|
let jsonContent = parseJson(jsonWakuMessage)
|
||||||
jsonMessage = JsonMessage.fromJsonNode(jsonContent).valueOr:
|
jsonMessage = JsonMessage.fromJsonNode(jsonContent).valueOr:
|
||||||
raise newException(JsonParsingError, $error)
|
raise newException(JsonParsingError, $error)
|
||||||
except JsonParsingError as exc:
|
except JsonParsingError as e:
|
||||||
return err("Error parsing json message: " & exc.msg)
|
return err("Error parsing json message: " & e.msg)
|
||||||
|
|
||||||
let msg = json_message_event.toWakuMessage(jsonMessage).valueOr:
|
let msg = json_message_event.toWakuMessage(jsonMessage).valueOr:
|
||||||
return err("Problem building the WakuMessage: " & $error)
|
return err("Problem building the WakuMessage: " & $error)
|
||||||
|
|
||||||
let msgHash = (
|
let msgHash = (await self.waku.relayPublish(PubsubTopic(pubSubTopic), msg, timeoutMs)).valueOr:
|
||||||
await ctx.myLib[].waku.relayPublish(
|
|
||||||
PubsubTopic($pubSubTopic), msg, uint32(timeoutMs)
|
|
||||||
)
|
|
||||||
).valueOr:
|
|
||||||
error "PUBLISH failed", error = error
|
error "PUBLISH failed", error = error
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok(msgHash)
|
return ok(msgHash)
|
||||||
|
|
||||||
proc waku_default_pubsub_topic(
|
proc waku_default_pubsub_topic(
|
||||||
ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer
|
self: LogosDelivery
|
||||||
) {.ffiRaw.} =
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
let topic = (await ctx.myLib[].waku.defaultPubsubTopic()).valueOr:
|
let topic = (await self.waku.defaultPubsubTopic()).valueOr:
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok(string(topic))
|
return ok(string(topic))
|
||||||
|
|
||||||
proc waku_content_topic(
|
proc waku_content_topic(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery,
|
||||||
callback: FFICallBack,
|
appName: string,
|
||||||
userData: pointer,
|
appVersion: uint32,
|
||||||
appName: cstring,
|
contentTopicName: string,
|
||||||
appVersion: cuint,
|
encoding: string,
|
||||||
contentTopicName: cstring,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
encoding: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
let topic = (
|
let topic = (
|
||||||
await ctx.myLib[].waku.buildContentTopic(
|
await self.waku.buildContentTopic(appName, appVersion, contentTopicName, encoding)
|
||||||
$appName, uint32(appVersion), $contentTopicName, $encoding
|
|
||||||
)
|
|
||||||
).valueOr:
|
).valueOr:
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok(string(topic))
|
return ok(string(topic))
|
||||||
|
|
||||||
proc waku_pubsub_topic(
|
proc waku_pubsub_topic(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, topicName: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
let topic = (await self.waku.buildPubsubTopic(topicName)).valueOr:
|
||||||
topicName: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
let topic = (await ctx.myLib[].waku.buildPubsubTopic($topicName)).valueOr:
|
|
||||||
return err(error)
|
return err(error)
|
||||||
return ok(string(topic))
|
return ok(string(topic))
|
||||||
|
|||||||
@ -65,15 +65,10 @@ func fromJsonNode(jsonContent: JsonNode): Result[StoreQueryRequest, string] =
|
|||||||
)
|
)
|
||||||
|
|
||||||
proc waku_store_query(
|
proc waku_store_query(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, jsonQuery: string, peerAddr: string, timeoutMs: int32
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
|
||||||
jsonQuery: cstring,
|
|
||||||
peerAddr: cstring,
|
|
||||||
timeoutMs: cint,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
let jsonContentRes = catch:
|
let jsonContentRes = catch:
|
||||||
parseJson($jsonQuery)
|
parseJson(jsonQuery)
|
||||||
|
|
||||||
if jsonContentRes.isErr():
|
if jsonContentRes.isErr():
|
||||||
return err("StoreRequest failed parsing store request: " & jsonContentRes.error.msg)
|
return err("StoreRequest failed parsing store request: " & jsonContentRes.error.msg)
|
||||||
@ -81,7 +76,7 @@ proc waku_store_query(
|
|||||||
let storeQueryRequest = ?fromJsonNode(jsonContentRes.get())
|
let storeQueryRequest = ?fromJsonNode(jsonContentRes.get())
|
||||||
|
|
||||||
let queryResponse = (
|
let queryResponse = (
|
||||||
await ctx.myLib[].waku.storeQuery(storeQueryRequest, $peerAddr, int(timeoutMs))
|
await self.waku.storeQuery(storeQueryRequest, peerAddr, int(timeoutMs))
|
||||||
).valueOr:
|
).valueOr:
|
||||||
return err("StoreRequest failed store query: " & error)
|
return err("StoreRequest failed store query: " & error)
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
|
// Public C header for the Logos Messaging API (LMAPI) library.
|
||||||
// Generated manually and inspired by libwaku.h
|
//
|
||||||
// Header file for Logos Messaging API (LMAPI) library
|
// The call surface is generated from the {.ffi.} annotations in library/*.nim
|
||||||
|
// and written to generated/logosdelivery.h by `make liblogosdelivery`. That file
|
||||||
|
// is a build artifact, not checked in, so build the library before you compile
|
||||||
|
// against this header. This file adds the event-listener ABI, which nim-ffi
|
||||||
|
// exports from declareLibrary but does not emit into the `abi = c` header.
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef __liblogosdelivery__
|
#ifndef __liblogosdelivery__
|
||||||
#define __liblogosdelivery__
|
#define __liblogosdelivery__
|
||||||
@ -8,145 +12,50 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
// The possible returned values for the functions that return int
|
#include "generated/logosdelivery.h"
|
||||||
#define RET_OK 0
|
|
||||||
#define RET_ERR 1
|
// Kept as aliases of the generated NIMFFI_RET_* codes so existing callers that
|
||||||
#define RET_MISSING_CALLBACK 2
|
// use the short names keep compiling. Guarded because the legacy libwaku header
|
||||||
|
// defines the same names with the same values.
|
||||||
|
#ifndef RET_OK
|
||||||
|
#define RET_OK NIMFFI_RET_OK
|
||||||
|
#endif
|
||||||
|
#ifndef RET_ERR
|
||||||
|
#define RET_ERR NIMFFI_RET_ERR
|
||||||
|
#endif
|
||||||
|
#ifndef RET_MISSING_CALLBACK
|
||||||
|
#define RET_MISSING_CALLBACK NIMFFI_RET_MISSING_CALLBACK
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Raw result-delivery callback used by the event API. `msg` is a byte run of
|
||||||
|
// `len` bytes, not NUL-terminated, and is valid only for the duration of the
|
||||||
|
// call.
|
||||||
typedef void (*FFICallBack)(int callerRet, const char *msg, size_t len, void *userData);
|
typedef void (*FFICallBack)(int callerRet, const char *msg, size_t len, void *userData);
|
||||||
|
|
||||||
// Creates a new instance of the node from the given configuration JSON.
|
// Events are delivered through a per-event listener registry. Register one
|
||||||
// Returns a pointer to the Context needed by the rest of the API functions.
|
// callback per event name of interest; see the README for the full list.
|
||||||
// The configuration is a JSON object with these optional keys:
|
// Channel lifecycle events are "onChannelMessageReceived" (payload
|
||||||
// "mode": "Core" | "Edge" (messaging role; defaults to "Core")
|
// base64-encoded), "onChannelMessageSent" and "onChannelMessageError".
|
||||||
// "preset": "<network preset>" (e.g. "twn")
|
|
||||||
// "messagingOverrides": { ... } (per-field messaging config overrides)
|
|
||||||
// "channelsOverrides": { ... } (per-field reliable-channel overrides)
|
|
||||||
// Override keys accept the config field name or its CLI switch name (e.g.
|
|
||||||
// "clusterId" or "cluster-id"). Unknown keys are rejected.
|
|
||||||
// Example: {"mode":"Core","messagingOverrides":{"cluster-id":42,"log-level":"INFO"}}
|
|
||||||
void *logosdelivery_create_node(
|
|
||||||
const char *configJson,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
// Starts the node.
|
|
||||||
int logosdelivery_start_node(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
// Stops the node.
|
|
||||||
int logosdelivery_stop_node(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
// Destroys an instance of a node created with logosdelivery_create_node
|
|
||||||
int logosdelivery_destroy(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
// Subscribe to a content topic.
|
|
||||||
// contentTopic: string representing the content topic (e.g., "/myapp/1/chat/proto")
|
|
||||||
int logosdelivery_subscribe(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *contentTopic);
|
|
||||||
|
|
||||||
// Unsubscribe from a content topic.
|
|
||||||
int logosdelivery_unsubscribe(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *contentTopic);
|
|
||||||
|
|
||||||
// Send a message.
|
|
||||||
// messageJson: JSON string with the following structure:
|
|
||||||
// {
|
|
||||||
// "contentTopic": "/myapp/1/chat/proto",
|
|
||||||
// "payload": "base64-encoded-payload",
|
|
||||||
// "ephemeral": false
|
|
||||||
// }
|
|
||||||
// Returns a request ID that can be used to track the message delivery.
|
|
||||||
int logosdelivery_send(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *messageJson);
|
|
||||||
|
|
||||||
// --- Reliable Channels API (stable surface) ---
|
|
||||||
|
|
||||||
// Create a reliable channel. Returns the channel id.
|
|
||||||
int logosdelivery_channel_create(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *channelId,
|
|
||||||
const char *contentTopic,
|
|
||||||
const char *senderId);
|
|
||||||
|
|
||||||
// Check whether a reliable channel is currently open. Returns "true" or
|
|
||||||
// "false"; an unknown channel id is not an error.
|
|
||||||
int logosdelivery_channel_exists(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *channelId);
|
|
||||||
|
|
||||||
// Send a message on a reliable channel.
|
|
||||||
// messageJson: { "payload": "base64-encoded-payload", "ephemeral": false }
|
|
||||||
// Returns a request ID that can be used to track delivery.
|
|
||||||
int logosdelivery_channel_send(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *channelId,
|
|
||||||
const char *messageJson);
|
|
||||||
|
|
||||||
// Close a reliable channel: stops its SDS loops; persisted state survives, so
|
|
||||||
// re-creating the channel restores it.
|
|
||||||
int logosdelivery_channel_close(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *channelId);
|
|
||||||
|
|
||||||
// Channel lifecycle events are delivered through a per-event listener,
|
|
||||||
// registered by name: "onChannelMessageReceived" (payload base64-encoded),
|
|
||||||
// "onChannelMessageSent", "onChannelMessageError".
|
|
||||||
|
|
||||||
// Registers a callback for the named event and returns a non-zero listener id
|
// Registers a callback for the named event and returns a non-zero listener id
|
||||||
// (0 on an invalid context). Register one listener per event name of interest;
|
// (0 on an invalid context). `ctx` is the context handle returned by
|
||||||
// see the README for the full list of event names.
|
// logosdelivery_create_node.
|
||||||
// The callback runs on a dedicated event thread and must be fast,
|
// The callback runs on a dedicated event thread and must be fast,
|
||||||
// non-blocking and thread-safe.
|
// non-blocking and thread-safe.
|
||||||
uint64_t logosdelivery_add_event_listener(void *ctx,
|
uint64_t logosdelivery_add_event_listener(void *ctx,
|
||||||
const char *eventName,
|
const char *eventName,
|
||||||
FFICallBack callback,
|
FFICallBack callback,
|
||||||
void *userData);
|
void *userData);
|
||||||
|
|
||||||
// Removes a previously registered listener. Returns 0 on success, 1 if the
|
// Removes a previously registered listener. Returns 0 on success, 1 if the
|
||||||
// listener id was not found or the context is invalid.
|
// listener id was not found or the context is invalid.
|
||||||
int logosdelivery_remove_event_listener(void *ctx,
|
int logosdelivery_remove_event_listener(void *ctx,
|
||||||
uint64_t listenerId);
|
uint64_t listenerId);
|
||||||
|
|
||||||
// Retrieves the list of available node info IDs.
|
|
||||||
int logosdelivery_get_available_node_info_ids(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
// Given a node info ID, retrieves the corresponding info.
|
|
||||||
int logosdelivery_get_node_info(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *nodeInfoId);
|
|
||||||
|
|
||||||
// Retrieves the list of available configurations.
|
|
||||||
int logosdelivery_get_available_configs(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
// NOTE: the low-level kernel API (waku_*) lives in the separate, advanced
|
|
||||||
// header liblogosdelivery_kernel.h. It is intentionally not declared here so
|
|
||||||
// this header only promises the stable Messaging / Reliable Channels surface.
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,3 +32,6 @@ include
|
|||||||
# logosdelivery_* surface in ./logos_delivery_api/node_api. The former
|
# logosdelivery_* surface in ./logos_delivery_api/node_api. The former
|
||||||
# waku_new / waku_start / waku_stop / waku_destroy entry points were removed to
|
# waku_new / waku_start / waku_stop / waku_destroy entry points were removed to
|
||||||
# avoid maintaining two parallel node-lifecycle APIs.
|
# avoid maintaining two parallel node-lifecycle APIs.
|
||||||
|
|
||||||
|
# Emits the `abi = c` dispatch wrappers, so it must stay the last FFI call here.
|
||||||
|
genBindings()
|
||||||
|
|||||||
@ -1,16 +1,17 @@
|
|||||||
|
// liblogosdelivery_kernel.h — compatibility alias for liblogosdelivery.h.
|
||||||
// liblogosdelivery_kernel.h — Kernel / advanced API (low-level, per-protocol).
|
|
||||||
//
|
//
|
||||||
// ⚠️ USE AT YOUR OWN RISK — UNSUPPORTED, UNSTABLE SURFACE.
|
// This header used to declare the low-level `waku_*` kernel API separately from
|
||||||
|
// the stable messaging surface, so that including it was a deliberate opt-in.
|
||||||
|
// That split no longer exists: the call surface is generated as one header, and
|
||||||
|
// liblogosdelivery.h declares every entry point. This file is kept only so
|
||||||
|
// existing includes keep resolving.
|
||||||
//
|
//
|
||||||
// These `waku_*` functions are the low-level kernel API. They are NOT part of
|
// The tiering still holds as a support promise, even though the compiler no
|
||||||
// the stable, supported Messaging / Reliable Channels surface declared in
|
// longer enforces it. The `waku_*` functions expose per-protocol internals
|
||||||
// liblogosdelivery.h. They expose per-protocol internals (relay, filter,
|
// (relay, filter, lightpush, store, discovery, peer management) and may change
|
||||||
// lightpush, store, discovery, peer management) and may change or be removed
|
// or be removed at ANY time, without notice or a deprecation cycle. Only the
|
||||||
// at ANY time, without notice or a deprecation cycle.
|
// messaging and reliable-channel entry points are
|
||||||
//
|
// supported.
|
||||||
// Including this header is a deliberate opt-in into the advanced tier. If you
|
|
||||||
// only need messaging, include liblogosdelivery.h and nothing here.
|
|
||||||
//
|
//
|
||||||
// See https://github.com/logos-messaging/logos-delivery/issues/3851 for the
|
// See https://github.com/logos-messaging/logos-delivery/issues/3851 for the
|
||||||
// tiering rationale.
|
// tiering rationale.
|
||||||
@ -18,224 +19,6 @@
|
|||||||
#ifndef __liblogosdelivery_kernel__
|
#ifndef __liblogosdelivery_kernel__
|
||||||
#define __liblogosdelivery_kernel__
|
#define __liblogosdelivery_kernel__
|
||||||
|
|
||||||
// Shared FFICallBack typedef and RET_* return codes live in the stable header.
|
|
||||||
#include "liblogosdelivery.h"
|
#include "liblogosdelivery.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// NOTE: node lifecycle (create / start / stop / destroy) is unified and lives
|
|
||||||
// only in the stable header. Use logosdelivery_create_node,
|
|
||||||
// logosdelivery_start_node, logosdelivery_stop_node and logosdelivery_destroy
|
|
||||||
// (declared in liblogosdelivery.h, included above) regardless of whether you
|
|
||||||
// drive the node through the messaging surface or this kernel API.
|
|
||||||
|
|
||||||
int waku_version(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
// NOTE: event callbacks are registered via logosdelivery_add_event_listener
|
|
||||||
// (declared above) which the waku_* API shares.
|
|
||||||
|
|
||||||
int waku_content_topic(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *appName,
|
|
||||||
unsigned int appVersion,
|
|
||||||
const char *contentTopicName,
|
|
||||||
const char *encoding);
|
|
||||||
|
|
||||||
int waku_pubsub_topic(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *topicName);
|
|
||||||
|
|
||||||
int waku_default_pubsub_topic(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
int waku_relay_publish(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *pubSubTopic,
|
|
||||||
const char *jsonWakuMessage,
|
|
||||||
unsigned int timeoutMs);
|
|
||||||
|
|
||||||
int waku_lightpush_publish(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *pubSubTopic,
|
|
||||||
const char *jsonWakuMessage);
|
|
||||||
|
|
||||||
int waku_relay_subscribe(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *pubSubTopic);
|
|
||||||
|
|
||||||
int waku_relay_add_protected_shard(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
int clusterId,
|
|
||||||
int shardId,
|
|
||||||
char *publicKey);
|
|
||||||
|
|
||||||
int waku_relay_unsubscribe(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *pubSubTopic);
|
|
||||||
|
|
||||||
int waku_filter_subscribe(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *pubSubTopic,
|
|
||||||
const char *contentTopics);
|
|
||||||
|
|
||||||
int waku_filter_unsubscribe(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *pubSubTopic,
|
|
||||||
const char *contentTopics);
|
|
||||||
|
|
||||||
int waku_filter_unsubscribe_all(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
int waku_relay_get_num_connected_peers(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *pubSubTopic);
|
|
||||||
|
|
||||||
int waku_relay_get_connected_peers(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *pubSubTopic);
|
|
||||||
|
|
||||||
int waku_relay_get_num_peers_in_mesh(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *pubSubTopic);
|
|
||||||
|
|
||||||
int waku_relay_get_peers_in_mesh(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *pubSubTopic);
|
|
||||||
|
|
||||||
int waku_store_query(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *jsonQuery,
|
|
||||||
const char *peerAddr,
|
|
||||||
int timeoutMs);
|
|
||||||
|
|
||||||
int waku_connect(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *peerMultiAddr,
|
|
||||||
unsigned int timeoutMs);
|
|
||||||
|
|
||||||
int waku_disconnect_peer_by_id(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *peerId);
|
|
||||||
|
|
||||||
int waku_disconnect_all_peers(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
int waku_dial_peer(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *peerMultiAddr,
|
|
||||||
const char *protocol,
|
|
||||||
int timeoutMs);
|
|
||||||
|
|
||||||
int waku_dial_peer_by_id(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *peerId,
|
|
||||||
const char *protocol,
|
|
||||||
int timeoutMs);
|
|
||||||
|
|
||||||
int waku_get_peerids_from_peerstore(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
int waku_get_connected_peers_info(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
int waku_get_peerids_by_protocol(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *protocol);
|
|
||||||
|
|
||||||
int waku_listen_addresses(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
int waku_get_connected_peers(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
// Returns a list of multiaddress given a url to a DNS discoverable ENR tree
|
|
||||||
// Parameters
|
|
||||||
// char* entTreeUrl: URL containing a discoverable ENR tree
|
|
||||||
// char* nameDnsServer: The nameserver to resolve the ENR tree url.
|
|
||||||
// int timeoutMs: Timeout value in milliseconds to execute the call.
|
|
||||||
int waku_dns_discovery(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *entTreeUrl,
|
|
||||||
const char *nameDnsServer,
|
|
||||||
int timeoutMs);
|
|
||||||
|
|
||||||
// Updates the bootnode list used for discovering new peers via DiscoveryV5
|
|
||||||
// bootnodes - JSON array containing the bootnode ENRs i.e. `["enr:...", "enr:..."]`
|
|
||||||
int waku_discv5_update_bootnodes(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
char *bootnodes);
|
|
||||||
|
|
||||||
int waku_start_discv5(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
int waku_stop_discv5(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
// Retrieves the ENR information
|
|
||||||
int waku_get_my_enr(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
int waku_get_my_peerid(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
int waku_get_metrics(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
int waku_peer_exchange_request(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
int numPeers);
|
|
||||||
|
|
||||||
int waku_ping_peer(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *peerAddr,
|
|
||||||
int timeoutMs);
|
|
||||||
|
|
||||||
int waku_is_online(void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* __liblogosdelivery_kernel__ */
|
#endif /* __liblogosdelivery_kernel__ */
|
||||||
|
|||||||
@ -3,46 +3,37 @@ import logos_delivery/waku/factory/waku_state_info
|
|||||||
import tools/confutils/[cli_args, config_option_meta]
|
import tools/confutils/[cli_args, config_option_meta]
|
||||||
|
|
||||||
proc logosdelivery_get_available_node_info_ids(
|
proc logosdelivery_get_available_node_info_ids(
|
||||||
ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer
|
self: LogosDelivery
|
||||||
) {.ffiRaw.} =
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
## Returns the list of all available node info item ids that
|
## Returns, as a JSON array of strings, all available node info item ids that
|
||||||
## can be queried with `get_node_info_item`.
|
## can be queried with `get_node_info`.
|
||||||
requireInitializedNode(ctx, "GetNodeInfoIds"):
|
var ids = newJArray()
|
||||||
return err(errMsg)
|
for id in self.waku.stateInfo.getAllPossibleInfoItemIds():
|
||||||
|
ids.add(%($id))
|
||||||
|
|
||||||
return ok($ctx.myLib[].waku.stateInfo.getAllPossibleInfoItemIds())
|
return ok($ids)
|
||||||
|
|
||||||
proc logosdelivery_get_node_info(
|
proc logosdelivery_get_node_info(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, nodeInfoId: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
|
||||||
nodeInfoId: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
## Returns the content of the node info item with the given id if it exists.
|
## Returns the content of the node info item with the given id if it exists.
|
||||||
requireInitializedNode(ctx, "GetNodeInfoItem"):
|
## The content is a plain string, not JSON: a peer id, an ENR URI, a
|
||||||
return err(errMsg)
|
## comma-separated multiaddress list or the Prometheus metrics text.
|
||||||
|
|
||||||
let infoItemIdEnum =
|
let infoItemIdEnum =
|
||||||
try:
|
try:
|
||||||
parseEnum[NodeInfoId]($nodeInfoId)
|
parseEnum[NodeInfoId](nodeInfoId)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return err("Invalid node info id: " & $nodeInfoId)
|
return err("Invalid node info id: " & nodeInfoId)
|
||||||
|
|
||||||
return ok(ctx.myLib[].waku.stateInfo.getNodeInfoItem(infoItemIdEnum))
|
return ok(self.waku.stateInfo.getNodeInfoItem(infoItemIdEnum))
|
||||||
|
|
||||||
proc logosdelivery_get_available_configs(
|
proc logosdelivery_get_available_configs(
|
||||||
ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer
|
self: LogosDelivery
|
||||||
) {.ffiRaw.} =
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
## Returns information about the accepted config items.
|
## Returns information about the accepted config items.
|
||||||
requireInitializedNode(ctx, "GetAvailableConfigs"):
|
|
||||||
return err(errMsg)
|
|
||||||
|
|
||||||
let optionMetas: seq[ConfigOptionMeta] = extractConfigOptionMeta(WakuNodeConf)
|
let optionMetas: seq[ConfigOptionMeta] = extractConfigOptionMeta(WakuNodeConf)
|
||||||
var configOptionDetails = newJArray()
|
var configOptionDetails = newJArray()
|
||||||
|
|
||||||
# for confField, confValue in fieldPairs(conf):
|
|
||||||
# defaultConfig[confField] = $confValue
|
|
||||||
|
|
||||||
for meta in optionMetas:
|
for meta in optionMetas:
|
||||||
configOptionDetails.add(
|
configOptionDetails.add(
|
||||||
%*{
|
%*{
|
||||||
@ -52,5 +43,4 @@ proc logosdelivery_get_available_configs(
|
|||||||
|
|
||||||
var jsonNode = newJObject()
|
var jsonNode = newJObject()
|
||||||
jsonNode["configOptions"] = configOptionDetails
|
jsonNode["configOptions"] = configOptionDetails
|
||||||
let asString = pretty(jsonNode)
|
|
||||||
return ok(pretty(jsonNode))
|
return ok(pretty(jsonNode))
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import std/[json]
|
import std/[json]
|
||||||
import chronos, results, ffi
|
import chronos, results, ffi
|
||||||
import stew/byteutils
|
|
||||||
import
|
import
|
||||||
logos_delivery/waku/common/base64,
|
logos_delivery/waku/common/base64,
|
||||||
logos_delivery/waku/waku,
|
logos_delivery/waku/waku,
|
||||||
@ -9,63 +8,45 @@ import
|
|||||||
../declare_lib
|
../declare_lib
|
||||||
|
|
||||||
proc logosdelivery_subscribe(
|
proc logosdelivery_subscribe(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, contentTopicStr: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
requireMessaging(self, "Subscribe"):
|
||||||
contentTopicStr: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
requireInitializedNode(ctx, "Subscribe"):
|
|
||||||
return err(errMsg)
|
|
||||||
|
|
||||||
requireMessaging(ctx, "Subscribe"):
|
|
||||||
return err(errMsg)
|
return err(errMsg)
|
||||||
|
|
||||||
# ContentTopic is just a string type alias
|
# ContentTopic is just a string type alias
|
||||||
let contentTopic = ContentTopic($contentTopicStr)
|
let contentTopic = ContentTopic(contentTopicStr)
|
||||||
|
|
||||||
(await ctx.myLib[].messagingClient.subscribe(contentTopic)).isOkOr:
|
(await self.messagingClient.subscribe(contentTopic)).isOkOr:
|
||||||
let errMsg = $error
|
let errMsg = $error
|
||||||
return err("Subscribe failed: " & errMsg)
|
return err("Subscribe failed: " & errMsg)
|
||||||
|
|
||||||
return ok("")
|
return ok("")
|
||||||
|
|
||||||
proc logosdelivery_unsubscribe(
|
proc logosdelivery_unsubscribe(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, contentTopicStr: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
requireMessaging(self, "Unsubscribe"):
|
||||||
contentTopicStr: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
requireInitializedNode(ctx, "Unsubscribe"):
|
|
||||||
return err(errMsg)
|
|
||||||
|
|
||||||
requireMessaging(ctx, "Unsubscribe"):
|
|
||||||
return err(errMsg)
|
return err(errMsg)
|
||||||
|
|
||||||
# ContentTopic is just a string type alias
|
# ContentTopic is just a string type alias
|
||||||
let contentTopic = ContentTopic($contentTopicStr)
|
let contentTopic = ContentTopic(contentTopicStr)
|
||||||
|
|
||||||
ctx.myLib[].messagingClient.unsubscribe(contentTopic).isOkOr:
|
self.messagingClient.unsubscribe(contentTopic).isOkOr:
|
||||||
let errMsg = $error
|
let errMsg = $error
|
||||||
return err("Unsubscribe failed: " & errMsg)
|
return err("Unsubscribe failed: " & errMsg)
|
||||||
|
|
||||||
return ok("")
|
return ok("")
|
||||||
|
|
||||||
proc logosdelivery_send(
|
proc logosdelivery_send(
|
||||||
ctx: ptr FFIContext[LogosDelivery],
|
self: LogosDelivery, messageJson: string
|
||||||
callback: FFICallBack,
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
userData: pointer,
|
requireMessaging(self, "Send"):
|
||||||
messageJson: cstring,
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
requireInitializedNode(ctx, "Send"):
|
|
||||||
return err(errMsg)
|
|
||||||
|
|
||||||
requireMessaging(ctx, "Send"):
|
|
||||||
return err(errMsg)
|
return err(errMsg)
|
||||||
|
|
||||||
## Parse the message JSON and send the message
|
## Parse the message JSON and send the message
|
||||||
var jsonNode: JsonNode
|
var jsonNode: JsonNode
|
||||||
try:
|
try:
|
||||||
jsonNode = parseJson($messageJson)
|
jsonNode = parseJson(messageJson)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return err("Failed to parse message JSON: " & e.msg)
|
return err("Failed to parse message JSON: " & e.msg)
|
||||||
|
|
||||||
@ -93,7 +74,7 @@ proc logosdelivery_send(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Send the message via the messaging layer's own API.
|
# Send the message via the messaging layer's own API.
|
||||||
let requestId = (await ctx.myLib[].messagingClient.send(envelope)).valueOr:
|
let requestId = (await self.messagingClient.send(envelope)).valueOr:
|
||||||
let errMsg = $error
|
let errMsg = $error
|
||||||
return err("Send failed: " & errMsg)
|
return err("Send failed: " & errMsg)
|
||||||
|
|
||||||
|
|||||||
@ -16,123 +16,74 @@ import
|
|||||||
proc `%`*(id: RequestId): JsonNode =
|
proc `%`*(id: RequestId): JsonNode =
|
||||||
%($id)
|
%($id)
|
||||||
|
|
||||||
registerReqFFI(CreateNodeRequest, ctx: ptr FFIContext[LogosDelivery]):
|
|
||||||
proc(configJson: cstring): Future[Result[string, string]] {.async.} =
|
|
||||||
let conf = parseLogosDeliveryConf($configJson).valueOr:
|
|
||||||
error "Failed to parse Logos Delivery configuration JSON",
|
|
||||||
error = error, configJson = $configJson
|
|
||||||
return err("failed parseLogosDeliveryConf " & error)
|
|
||||||
|
|
||||||
ctx.myLib[] = (await LogosDelivery.new(conf)).valueOr:
|
|
||||||
let errMsg = $error
|
|
||||||
chronicles.error "CreateNodeRequest failed", err = errMsg
|
|
||||||
return err(errMsg)
|
|
||||||
|
|
||||||
return ok("")
|
|
||||||
|
|
||||||
proc logosdelivery_destroy(
|
|
||||||
ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer
|
|
||||||
): cint {.dynlib, exportc, cdecl.} =
|
|
||||||
initializeLibrary()
|
|
||||||
if not LogosDeliveryFFIPool.isValidCtx(cast[pointer](ctx)):
|
|
||||||
return RET_ERR
|
|
||||||
checkParams(ctx, callback, userData)
|
|
||||||
|
|
||||||
# Recycle instead of destroy: under refc a full teardown cannot close the
|
|
||||||
# context signal fds, so every create/destroy cycle would leak them.
|
|
||||||
ffi.recycleFFIContext(LogosDeliveryFFIPool, ctx).isOkOr:
|
|
||||||
let msg = "liblogosdelivery error: " & $error
|
|
||||||
callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData)
|
|
||||||
return RET_ERR
|
|
||||||
|
|
||||||
## always need to invoke the callback although we don't retrieve value to the caller
|
|
||||||
callback(RET_OK, nil, 0, userData)
|
|
||||||
|
|
||||||
return RET_OK
|
|
||||||
|
|
||||||
proc logosdelivery_create_node(
|
proc logosdelivery_create_node(
|
||||||
configJson: cstring, callback: FFICallback, userData: pointer
|
configJson: string
|
||||||
): pointer {.dynlib, exportc, cdecl.} =
|
): Future[Result[LogosDelivery, string]] {.ffiCtor.} =
|
||||||
initializeLibrary()
|
let conf = parseLogosDeliveryConf(configJson).valueOr:
|
||||||
|
error "Failed to parse Logos Delivery configuration JSON",
|
||||||
|
error = error, configJson = configJson
|
||||||
|
return err("failed parseLogosDeliveryConf " & error)
|
||||||
|
|
||||||
if callback.isNil():
|
let lib = (await LogosDelivery.new(conf)).valueOr:
|
||||||
echo "error: missing callback in logosdelivery_create_node"
|
let errMsg = $error
|
||||||
return nil
|
chronicles.error "CreateNodeRequest failed", err = errMsg
|
||||||
|
|
||||||
var ctx = ffi.createFFIContext(LogosDeliveryFFIPool).valueOr:
|
|
||||||
let msg = "Error in createFFIContext: " & $error
|
|
||||||
callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData)
|
|
||||||
return nil
|
|
||||||
|
|
||||||
ctx.userData = userData
|
|
||||||
|
|
||||||
ffi.sendRequestToFFIThread(
|
|
||||||
ctx, CreateNodeRequest.ffiNewReq(callback, userData, configJson)
|
|
||||||
).isOkOr:
|
|
||||||
let msg = "error in sendRequestToFFIThread: " & $error
|
|
||||||
callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData)
|
|
||||||
# free allocated resources as they won't be available
|
|
||||||
ffi.recycleFFIContext(LogosDeliveryFFIPool, ctx).isOkOr:
|
|
||||||
chronicles.error "Error in recycleFFIContext after sendRequestToFFIThread during creation",
|
|
||||||
err = $error
|
|
||||||
return nil
|
|
||||||
|
|
||||||
return ctx
|
|
||||||
|
|
||||||
proc logosdelivery_start_node(
|
|
||||||
ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer
|
|
||||||
) {.ffiRaw.} =
|
|
||||||
requireInitializedNode(ctx, "START_NODE"):
|
|
||||||
return err(errMsg)
|
return err(errMsg)
|
||||||
|
|
||||||
# setting up outgoing event listeners
|
return ok(lib)
|
||||||
let sentListener = MessageSentEvent.listen(
|
|
||||||
ctx.myLib[].waku.brokerCtx,
|
proc logosdelivery_destroy(self: LogosDelivery) {.ffiDtor.} =
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc registerFFIEventListeners(self: LogosDelivery): Result[void, string] =
|
||||||
|
## Bridges every broker event the library re-publishes onto the FFI event
|
||||||
|
## registry. Keep in step with `dropFFIEventListeners`.
|
||||||
|
MessageSentEvent.listen(
|
||||||
|
self.waku.brokerCtx,
|
||||||
proc(event: MessageSentEvent) {.async: (raises: []).} =
|
proc(event: MessageSentEvent) {.async: (raises: []).} =
|
||||||
emitEvent("onMessageSent"):
|
emitEvent("onMessageSent"):
|
||||||
$newJsonEvent("message_sent", event),
|
$newJsonEvent("message_sent", event),
|
||||||
).valueOr:
|
).isOkOr:
|
||||||
chronicles.error "MessageSentEvent.listen failed", err = $error
|
chronicles.error "MessageSentEvent.listen failed", err = $error
|
||||||
return err("MessageSentEvent.listen failed: " & $error)
|
return err("MessageSentEvent.listen failed: " & $error)
|
||||||
|
|
||||||
let errorListener = MessageErrorEvent.listen(
|
MessageErrorEvent.listen(
|
||||||
ctx.myLib[].waku.brokerCtx,
|
self.waku.brokerCtx,
|
||||||
proc(event: MessageErrorEvent) {.async: (raises: []).} =
|
proc(event: MessageErrorEvent) {.async: (raises: []).} =
|
||||||
emitEvent("onMessageError"):
|
emitEvent("onMessageError"):
|
||||||
$newJsonEvent("message_error", event),
|
$newJsonEvent("message_error", event),
|
||||||
).valueOr:
|
).isOkOr:
|
||||||
chronicles.error "MessageErrorEvent.listen failed", err = $error
|
chronicles.error "MessageErrorEvent.listen failed", err = $error
|
||||||
return err("MessageErrorEvent.listen failed: " & $error)
|
return err("MessageErrorEvent.listen failed: " & $error)
|
||||||
|
|
||||||
let propagatedListener = MessagePropagatedEvent.listen(
|
MessagePropagatedEvent.listen(
|
||||||
ctx.myLib[].waku.brokerCtx,
|
self.waku.brokerCtx,
|
||||||
proc(event: MessagePropagatedEvent) {.async: (raises: []).} =
|
proc(event: MessagePropagatedEvent) {.async: (raises: []).} =
|
||||||
emitEvent("onMessagePropagated"):
|
emitEvent("onMessagePropagated"):
|
||||||
$newJsonEvent("message_propagated", event),
|
$newJsonEvent("message_propagated", event),
|
||||||
).valueOr:
|
).isOkOr:
|
||||||
chronicles.error "MessagePropagatedEvent.listen failed", err = $error
|
chronicles.error "MessagePropagatedEvent.listen failed", err = $error
|
||||||
return err("MessagePropagatedEvent.listen failed: " & $error)
|
return err("MessagePropagatedEvent.listen failed: " & $error)
|
||||||
|
|
||||||
let receivedListener = MessageReceivedEvent.listen(
|
MessageReceivedEvent.listen(
|
||||||
ctx.myLib[].waku.brokerCtx,
|
self.waku.brokerCtx,
|
||||||
proc(event: MessageReceivedEvent) {.async: (raises: []).} =
|
proc(event: MessageReceivedEvent) {.async: (raises: []).} =
|
||||||
emitEvent("onMessageReceived"):
|
emitEvent("onMessageReceived"):
|
||||||
$newJsonEvent("message_received", event),
|
$newJsonEvent("message_received", event),
|
||||||
).valueOr:
|
).isOkOr:
|
||||||
chronicles.error "MessageReceivedEvent.listen failed", err = $error
|
chronicles.error "MessageReceivedEvent.listen failed", err = $error
|
||||||
return err("MessageReceivedEvent.listen failed: " & $error)
|
return err("MessageReceivedEvent.listen failed: " & $error)
|
||||||
|
|
||||||
let ConnectionStatusChangeListener = EventConnectionStatusChange.listen(
|
EventConnectionStatusChange.listen(
|
||||||
ctx.myLib[].waku.brokerCtx,
|
self.waku.brokerCtx,
|
||||||
proc(event: EventConnectionStatusChange) {.async: (raises: []).} =
|
proc(event: EventConnectionStatusChange) {.async: (raises: []).} =
|
||||||
emitEvent("onConnectionStatusChange"):
|
emitEvent("onConnectionStatusChange"):
|
||||||
$newJsonEvent("connection_status_change", event),
|
$newJsonEvent("connection_status_change", event),
|
||||||
).valueOr:
|
).isOkOr:
|
||||||
chronicles.error "ConnectionStatusChange.listen failed", err = $error
|
chronicles.error "ConnectionStatusChange.listen failed", err = $error
|
||||||
return err("ConnectionStatusChange.listen failed: " & $error)
|
return err("ConnectionStatusChange.listen failed: " & $error)
|
||||||
|
|
||||||
let shardTopicHealthListener = EventShardTopicHealthChange.listen(
|
EventShardTopicHealthChange.listen(
|
||||||
ctx.myLib[].waku.brokerCtx,
|
self.waku.brokerCtx,
|
||||||
proc(event: EventShardTopicHealthChange) {.async: (raises: []).} =
|
proc(event: EventShardTopicHealthChange) {.async: (raises: []).} =
|
||||||
emitEvent("onTopicHealthChange"):
|
emitEvent("onTopicHealthChange"):
|
||||||
$(
|
$(
|
||||||
@ -142,12 +93,12 @@ proc logosdelivery_start_node(
|
|||||||
"topicHealth": $event.health,
|
"topicHealth": $event.health,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
).valueOr:
|
).isOkOr:
|
||||||
chronicles.error "EventShardTopicHealthChange.listen failed", err = $error
|
chronicles.error "EventShardTopicHealthChange.listen failed", err = $error
|
||||||
return err("EventShardTopicHealthChange.listen failed: " & $error)
|
return err("EventShardTopicHealthChange.listen failed: " & $error)
|
||||||
|
|
||||||
let peerEventListener = WakuPeerEvent.listen(
|
WakuPeerEvent.listen(
|
||||||
ctx.myLib[].waku.brokerCtx,
|
self.waku.brokerCtx,
|
||||||
proc(event: WakuPeerEvent) {.async: (raises: []).} =
|
proc(event: WakuPeerEvent) {.async: (raises: []).} =
|
||||||
emitEvent("onConnectionChange"):
|
emitEvent("onConnectionChange"):
|
||||||
$(
|
$(
|
||||||
@ -157,12 +108,12 @@ proc logosdelivery_start_node(
|
|||||||
"peerEvent": $event.kind,
|
"peerEvent": $event.kind,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
).valueOr:
|
).isOkOr:
|
||||||
chronicles.error "WakuPeerEvent.listen failed", err = $error
|
chronicles.error "WakuPeerEvent.listen failed", err = $error
|
||||||
return err("WakuPeerEvent.listen failed: " & $error)
|
return err("WakuPeerEvent.listen failed: " & $error)
|
||||||
|
|
||||||
let channelReceivedListener = ChannelMessageReceivedEvent.listen(
|
ChannelMessageReceivedEvent.listen(
|
||||||
ctx.myLib[].waku.brokerCtx,
|
self.waku.brokerCtx,
|
||||||
proc(event: ChannelMessageReceivedEvent) {.async: (raises: []).} =
|
proc(event: ChannelMessageReceivedEvent) {.async: (raises: []).} =
|
||||||
emitEvent("onChannelMessageReceived"):
|
emitEvent("onChannelMessageReceived"):
|
||||||
$(
|
$(
|
||||||
@ -173,52 +124,61 @@ proc logosdelivery_start_node(
|
|||||||
"payload": string(base64.encode(event.payload)),
|
"payload": string(base64.encode(event.payload)),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
).valueOr:
|
).isOkOr:
|
||||||
chronicles.error "ChannelMessageReceivedEvent.listen failed", err = $error
|
chronicles.error "ChannelMessageReceivedEvent.listen failed", err = $error
|
||||||
return err("ChannelMessageReceivedEvent.listen failed: " & $error)
|
return err("ChannelMessageReceivedEvent.listen failed: " & $error)
|
||||||
|
|
||||||
let channelSentListener = ChannelMessageSentEvent.listen(
|
ChannelMessageSentEvent.listen(
|
||||||
ctx.myLib[].waku.brokerCtx,
|
self.waku.brokerCtx,
|
||||||
proc(event: ChannelMessageSentEvent) {.async: (raises: []).} =
|
proc(event: ChannelMessageSentEvent) {.async: (raises: []).} =
|
||||||
emitEvent("onChannelMessageSent"):
|
emitEvent("onChannelMessageSent"):
|
||||||
$newJsonEvent("channel_message_sent", event),
|
$newJsonEvent("channel_message_sent", event),
|
||||||
).valueOr:
|
).isOkOr:
|
||||||
chronicles.error "ChannelMessageSentEvent.listen failed", err = $error
|
chronicles.error "ChannelMessageSentEvent.listen failed", err = $error
|
||||||
return err("ChannelMessageSentEvent.listen failed: " & $error)
|
return err("ChannelMessageSentEvent.listen failed: " & $error)
|
||||||
|
|
||||||
let channelErrorListener = ChannelMessageErrorEvent.listen(
|
ChannelMessageErrorEvent.listen(
|
||||||
ctx.myLib[].waku.brokerCtx,
|
self.waku.brokerCtx,
|
||||||
proc(event: ChannelMessageErrorEvent) {.async: (raises: []).} =
|
proc(event: ChannelMessageErrorEvent) {.async: (raises: []).} =
|
||||||
emitEvent("onChannelMessageError"):
|
emitEvent("onChannelMessageError"):
|
||||||
$newJsonEvent("channel_message_error", event),
|
$newJsonEvent("channel_message_error", event),
|
||||||
).valueOr:
|
).isOkOr:
|
||||||
chronicles.error "ChannelMessageErrorEvent.listen failed", err = $error
|
chronicles.error "ChannelMessageErrorEvent.listen failed", err = $error
|
||||||
return err("ChannelMessageErrorEvent.listen failed: " & $error)
|
return err("ChannelMessageErrorEvent.listen failed: " & $error)
|
||||||
|
|
||||||
(await ctx.myLib[].start()).isOkOr:
|
return ok()
|
||||||
|
|
||||||
|
proc dropFFIEventListeners(self: LogosDelivery) {.async.} =
|
||||||
|
## Reverse of `registerFFIEventListeners`.
|
||||||
|
await MessageErrorEvent.dropAllListeners(self.waku.brokerCtx)
|
||||||
|
await MessageSentEvent.dropAllListeners(self.waku.brokerCtx)
|
||||||
|
await MessagePropagatedEvent.dropAllListeners(self.waku.brokerCtx)
|
||||||
|
await MessageReceivedEvent.dropAllListeners(self.waku.brokerCtx)
|
||||||
|
await EventConnectionStatusChange.dropAllListeners(self.waku.brokerCtx)
|
||||||
|
await EventShardTopicHealthChange.dropAllListeners(self.waku.brokerCtx)
|
||||||
|
await WakuPeerEvent.dropAllListeners(self.waku.brokerCtx)
|
||||||
|
await ChannelMessageReceivedEvent.dropAllListeners(self.waku.brokerCtx)
|
||||||
|
await ChannelMessageSentEvent.dropAllListeners(self.waku.brokerCtx)
|
||||||
|
await ChannelMessageErrorEvent.dropAllListeners(self.waku.brokerCtx)
|
||||||
|
|
||||||
|
proc logosdelivery_start_node(
|
||||||
|
self: LogosDelivery
|
||||||
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
|
self.registerFFIEventListeners().isOkOr:
|
||||||
|
return err(error)
|
||||||
|
|
||||||
|
(await self.start()).isOkOr:
|
||||||
let errMsg = $error
|
let errMsg = $error
|
||||||
chronicles.error "START_NODE failed", err = errMsg
|
chronicles.error "START_NODE failed", err = errMsg
|
||||||
return err("failed to start: " & errMsg)
|
return err("failed to start: " & errMsg)
|
||||||
return ok("")
|
return ok("")
|
||||||
|
|
||||||
proc logosdelivery_stop_node(
|
proc logosdelivery_stop_node(
|
||||||
ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer
|
self: LogosDelivery
|
||||||
) {.ffiRaw.} =
|
): Future[Result[string, string]] {.ffi.} =
|
||||||
requireInitializedNode(ctx, "STOP_NODE"):
|
await self.dropFFIEventListeners()
|
||||||
return err(errMsg)
|
|
||||||
|
|
||||||
await MessageErrorEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx)
|
(await self.stop()).isOkOr:
|
||||||
await MessageSentEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx)
|
|
||||||
await MessagePropagatedEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx)
|
|
||||||
await MessageReceivedEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx)
|
|
||||||
await EventConnectionStatusChange.dropAllListeners(ctx.myLib[].waku.brokerCtx)
|
|
||||||
await EventShardTopicHealthChange.dropAllListeners(ctx.myLib[].waku.brokerCtx)
|
|
||||||
await WakuPeerEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx)
|
|
||||||
await ChannelMessageReceivedEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx)
|
|
||||||
await ChannelMessageSentEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx)
|
|
||||||
await ChannelMessageErrorEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx)
|
|
||||||
|
|
||||||
(await ctx.myLib[].stop()).isOkOr:
|
|
||||||
let errMsg = $error
|
let errMsg = $error
|
||||||
chronicles.error "STOP_NODE failed", err = errMsg
|
chronicles.error "STOP_NODE failed", err = errMsg
|
||||||
return err("failed to stop: " & errMsg)
|
return err("failed to stop: " & errMsg)
|
||||||
|
|||||||
@ -61,7 +61,7 @@ requires "nim >= 2.2.4",
|
|||||||
|
|
||||||
# Packages not on nimble (use git URLs)
|
# Packages not on nimble (use git URLs)
|
||||||
|
|
||||||
requires "https://github.com/logos-messaging/nim-ffi#aad9374354a5e3d98964a9adf80766a12f8f200d" # v0.3.0-rc.1
|
requires "https://github.com/logos-messaging/nim-ffi#53515de17af0ef3e88b2aec9675b8163dddc14ae" # v0.3.0-rc.2
|
||||||
|
|
||||||
requires "https://github.com/logos-messaging/nim-sds.git#b12f5ee07c5b764303b51fb948b32a4ade1de3b5"
|
requires "https://github.com/logos-messaging/nim-sds.git#b12f5ee07c5b764303b51fb948b32a4ade1de3b5"
|
||||||
|
|
||||||
@ -106,14 +106,25 @@ proc buildBinary(name: string, srcDir = "./", params = "") =
|
|||||||
exec "nim c --out:build/" & name & " --mm:refc " & getMyCPU() & getNimParams() & " " & params & " " &
|
exec "nim c --out:build/" & name & " --mm:refc " & getMyCPU() & getNimParams() & " " & params & " " &
|
||||||
srcDir & name & ".nim"
|
srcDir & name & ".nim"
|
||||||
|
|
||||||
|
## Emitted by `genBindings()` during the library build, so the header can never
|
||||||
|
## drift from the Nim signatures. Not checked in: it is a build artifact.
|
||||||
|
const cBindingsDir = "library/generated"
|
||||||
|
|
||||||
|
## `-d:ffiSrcPath` is required: without it nim-ffi derives the path with
|
||||||
|
## `relativePath`, which needs `getcwd` at compile time and fails to build.
|
||||||
|
const cBindingsFlags =
|
||||||
|
" -d:ffiGenBindings -d:targetLang=c -d:ffiOutputDir=" & cBindingsDir &
|
||||||
|
" -d:ffiSrcPath=../liblogosdelivery.nim "
|
||||||
|
|
||||||
proc buildLibrary(lib_name: string, srcDir = "./", params = "", `type` = "static", srcFile = "liblogosdelivery.nim", mainPrefix = "liblogosdelivery") =
|
proc buildLibrary(lib_name: string, srcDir = "./", params = "", `type` = "static", srcFile = "liblogosdelivery.nim", mainPrefix = "liblogosdelivery") =
|
||||||
if not dirExists "build":
|
if not dirExists "build":
|
||||||
mkDir "build"
|
mkDir "build"
|
||||||
|
mkDir cBindingsDir
|
||||||
|
|
||||||
if `type` == "static":
|
if `type` == "static":
|
||||||
exec "nim c" & " --out:build/" & lib_name &
|
exec "nim c" & " --out:build/" & lib_name &
|
||||||
" --threads:on --app:staticlib --opt:speed --noMain --mm:refc --header -d:metrics --nimMainPrefix:" & mainPrefix & " --skipParentCfg:off -d:discv5_protocol_id=d5waku " &
|
" --threads:on --app:staticlib --opt:speed --noMain --mm:refc --header -d:metrics --nimMainPrefix:" & mainPrefix & " --skipParentCfg:off -d:discv5_protocol_id=d5waku " &
|
||||||
getMyCPU() & getNimParams() & srcDir & "/" & srcFile
|
cBindingsFlags & getMyCPU() & getNimParams() & srcDir & "/" & srcFile
|
||||||
else:
|
else:
|
||||||
# -Bsymbolic binds the library's references to its own symbols at link
|
# -Bsymbolic binds the library's references to its own symbols at link
|
||||||
# time. Without it, a host process that already loads OpenSSL (e.g.
|
# time. Without it, a host process that already loads OpenSSL (e.g.
|
||||||
@ -123,7 +134,7 @@ proc buildLibrary(lib_name: string, srcDir = "./", params = "", `type` = "static
|
|||||||
let elfFlags = when defined(linux): "--passL:-Wl,-Bsymbolic " else: ""
|
let elfFlags = when defined(linux): "--passL:-Wl,-Bsymbolic " else: ""
|
||||||
exec "nim c" & " --out:build/" & lib_name &
|
exec "nim c" & " --out:build/" & lib_name &
|
||||||
" --threads:on --app:lib --opt:speed --noMain --mm:refc --header -d:metrics --nimMainPrefix:" & mainPrefix & " --skipParentCfg:off -d:discv5_protocol_id=d5waku " &
|
" --threads:on --app:lib --opt:speed --noMain --mm:refc --header -d:metrics --nimMainPrefix:" & mainPrefix & " --skipParentCfg:off -d:discv5_protocol_id=d5waku " &
|
||||||
elfFlags & getMyCPU() & getNimParams() & " " & srcDir & "/" & srcFile
|
elfFlags & cBindingsFlags & getMyCPU() & getNimParams() & " " & srcDir & "/" & srcFile
|
||||||
|
|
||||||
proc buildLibDynamicWindows(libName: string, folderName: string) =
|
proc buildLibDynamicWindows(libName: string, folderName: string) =
|
||||||
buildLibrary libName & ".dll", folderName,
|
buildLibrary libName & ".dll", folderName,
|
||||||
|
|||||||
@ -644,7 +644,7 @@
|
|||||||
},
|
},
|
||||||
"ffi": {
|
"ffi": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"vcsRevision": "aad9374354a5e3d98964a9adf80766a12f8f200d",
|
"vcsRevision": "53515de17af0ef3e88b2aec9675b8163dddc14ae",
|
||||||
"url": "https://github.com/logos-messaging/nim-ffi",
|
"url": "https://github.com/logos-messaging/nim-ffi",
|
||||||
"downloadMethod": "git",
|
"downloadMethod": "git",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@ -655,7 +655,7 @@
|
|||||||
"cbor_serialization"
|
"cbor_serialization"
|
||||||
],
|
],
|
||||||
"checksums": {
|
"checksums": {
|
||||||
"sha1": "db5fc50aa4717418e481cb0b1f7ca36f2d76586c"
|
"sha1": "1d84ceaf8594f4970c5a37f916003ffc0531dc4e"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"boringssl": {
|
"boringssl": {
|
||||||
|
|||||||
@ -285,8 +285,8 @@
|
|||||||
|
|
||||||
ffi = pkgs.fetchgit {
|
ffi = pkgs.fetchgit {
|
||||||
url = "https://github.com/logos-messaging/nim-ffi";
|
url = "https://github.com/logos-messaging/nim-ffi";
|
||||||
rev = "aad9374354a5e3d98964a9adf80766a12f8f200d";
|
rev = "53515de17af0ef3e88b2aec9675b8163dddc14ae";
|
||||||
sha256 = "075ax4spvzr7idd5b5sncpkr7b3163qncr8fsxv9d02dix4ailqc";
|
sha256 = "0ncf9j7fhgd3nswr4rh19jx77dl974sajphdl04cb602hshgj5ij";
|
||||||
fetchSubmodules = true;
|
fetchSubmodules = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -56,7 +56,7 @@
|
|||||||
{
|
{
|
||||||
"path": "vendor/nim-ffi",
|
"path": "vendor/nim-ffi",
|
||||||
"url": "https://github.com/logos-messaging/nim-ffi",
|
"url": "https://github.com/logos-messaging/nim-ffi",
|
||||||
"rev": "aad9374354a5e3d98964a9adf80766a12f8f200d"
|
"rev": "53515de17af0ef3e88b2aec9675b8163dddc14ae"
|
||||||
}
|
}
|
||||||
,
|
,
|
||||||
{
|
{
|
||||||
|
|||||||
@ -181,9 +181,9 @@ def get_node_multiaddr(node) -> str:
|
|||||||
list), this fails loudly instead of silently passing a malformed string
|
list), this fails loudly instead of silently passing a malformed string
|
||||||
downstream to staticnodes / add_peers.
|
downstream to staticnodes / add_peers.
|
||||||
"""
|
"""
|
||||||
result = node.get_node_info_raw("MyMultiaddresses")
|
result = node.get_node_info("MyMultiaddresses")
|
||||||
if result.is_err():
|
if result.is_err():
|
||||||
raise RuntimeError(f"get_node_info_raw failed: {result.err()}")
|
raise RuntimeError(f"get_node_info failed: {result.err()}")
|
||||||
|
|
||||||
addr = result.ok_value.strip()
|
addr = result.ok_value.strip()
|
||||||
if not addr or not addr.startswith("/"):
|
if not addr or not addr.startswith("/"):
|
||||||
|
|||||||
@ -74,25 +74,8 @@ class WrapperManager:
|
|||||||
def get_available_node_info_ids(self, *, timeout_s: float = 20.0) -> Result[list[str], str]:
|
def get_available_node_info_ids(self, *, timeout_s: float = 20.0) -> Result[list[str], str]:
|
||||||
return self._node.get_available_node_info_ids(timeout_s=timeout_s)
|
return self._node.get_available_node_info_ids(timeout_s=timeout_s)
|
||||||
|
|
||||||
def get_node_info(self, node_info_id: str, *, timeout_s: float = 20.0) -> Result[dict, str]:
|
def get_node_info(self, node_info_id: str, *, timeout_s: float = 20.0) -> Result[str, str]:
|
||||||
return self._node.get_node_info(node_info_id, timeout_s=timeout_s)
|
return self._node.get_node_info(node_info_id, timeout_s=timeout_s)
|
||||||
|
|
||||||
def get_node_info_raw(self, node_info_id: str, *, timeout_s: float = 20.0) -> Result[str, str]:
|
|
||||||
"""Like get_node_info but returns the raw string without JSON parsing."""
|
|
||||||
from wrapper import lib, ffi, _new_cb_state, _wait_cb_raw # type: ignore[import]
|
|
||||||
|
|
||||||
state = _new_cb_state()
|
|
||||||
cb = self._node._make_waiting_cb(state)
|
|
||||||
rc = lib.logosdelivery_get_node_info(self._node.ctx, cb, ffi.NULL, node_info_id.encode("utf-8"))
|
|
||||||
if rc != 0:
|
|
||||||
return Err(f"get_node_info_raw: immediate call failed (ret={rc})")
|
|
||||||
wait_result = _wait_cb_raw(state, "get_node_info_raw", 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_raw: callback failed (ret={cb_ret})")
|
|
||||||
return Ok(cb_msg.decode("utf-8") if cb_msg else "")
|
|
||||||
|
|
||||||
def get_available_configs(self, *, timeout_s: float = 20.0) -> Result[dict, str]:
|
def get_available_configs(self, *, timeout_s: float = 20.0) -> Result[dict, str]:
|
||||||
return self._node.get_available_configs(timeout_s=timeout_s)
|
return self._node.get_available_configs(timeout_s=timeout_s)
|
||||||
|
|||||||
@ -9,25 +9,40 @@ ffi = FFI()
|
|||||||
|
|
||||||
ffi.cdef(
|
ffi.cdef(
|
||||||
"""
|
"""
|
||||||
|
// Raw FFICallBack, used by the event listener registry and by the
|
||||||
|
// scalar-fast-path exports (no string arguments).
|
||||||
typedef void (*FFICallBack)(int callerRet, const char *msg, size_t len, void *userData);
|
typedef void (*FFICallBack)(int callerRet, const char *msg, size_t len, void *userData);
|
||||||
|
|
||||||
|
// Reply callback of the `abi = c` exports that take arguments. `reply` and
|
||||||
|
// `errMsg` are NUL-terminated and valid only for the duration of the call.
|
||||||
|
typedef void (*ReplyFn)(int errCode, const char *reply, const char *errMsg, void *userData);
|
||||||
|
|
||||||
|
// The constructor reports the context address as decimal text.
|
||||||
|
typedef void (*CreateRawFn)(int errCode, const char *ctxAddr, const char *errMsg, void *userData);
|
||||||
|
|
||||||
|
typedef struct { const char *configJson; } CreateNodeCtorReq;
|
||||||
|
typedef struct { const char *contentTopicStr; } SubscribeReq;
|
||||||
|
typedef struct { const char *contentTopicStr; } UnsubscribeReq;
|
||||||
|
typedef struct { const char *messageJson; } SendReq;
|
||||||
|
typedef struct { const char *nodeInfoId; } GetNodeInfoReq;
|
||||||
|
|
||||||
void *logosdelivery_create_node(
|
void *logosdelivery_create_node(
|
||||||
const char *configJson,
|
const CreateNodeCtorReq *req,
|
||||||
FFICallBack callback,
|
CreateRawFn onCreated,
|
||||||
void *userData
|
void *userData
|
||||||
);
|
);
|
||||||
|
|
||||||
int logosdelivery_start_node(
|
int logosdelivery_destroy(void *ctx);
|
||||||
void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData
|
|
||||||
);
|
|
||||||
|
|
||||||
int logosdelivery_stop_node(
|
int logosdelivery_start_node(void *ctx, FFICallBack callback, void *userData);
|
||||||
void *ctx,
|
int logosdelivery_stop_node(void *ctx, FFICallBack callback, void *userData);
|
||||||
FFICallBack callback,
|
int logosdelivery_get_available_node_info_ids(void *ctx, FFICallBack callback, void *userData);
|
||||||
void *userData
|
int logosdelivery_get_available_configs(void *ctx, FFICallBack callback, void *userData);
|
||||||
);
|
|
||||||
|
int logosdelivery_subscribe(void *ctx, ReplyFn onReply, void *userData, const SubscribeReq *req);
|
||||||
|
int logosdelivery_unsubscribe(void *ctx, ReplyFn onReply, void *userData, const UnsubscribeReq *req);
|
||||||
|
int logosdelivery_send(void *ctx, ReplyFn onReply, void *userData, const SendReq *req);
|
||||||
|
int logosdelivery_get_node_info(void *ctx, ReplyFn onReply, void *userData, const GetNodeInfoReq *req);
|
||||||
|
|
||||||
uint64_t logosdelivery_add_event_listener(
|
uint64_t logosdelivery_add_event_listener(
|
||||||
void *ctx,
|
void *ctx,
|
||||||
@ -40,52 +55,6 @@ int logosdelivery_remove_event_listener(
|
|||||||
void *ctx,
|
void *ctx,
|
||||||
uint64_t listenerId
|
uint64_t listenerId
|
||||||
);
|
);
|
||||||
|
|
||||||
int logosdelivery_destroy(
|
|
||||||
void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData
|
|
||||||
);
|
|
||||||
|
|
||||||
int logosdelivery_subscribe(
|
|
||||||
void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *contentTopic
|
|
||||||
);
|
|
||||||
|
|
||||||
int logosdelivery_unsubscribe(
|
|
||||||
void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData,
|
|
||||||
const char *contentTopic
|
|
||||||
);
|
|
||||||
|
|
||||||
int logosdelivery_send(
|
|
||||||
void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
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
|
|
||||||
);
|
|
||||||
|
|
||||||
int logosdelivery_get_available_configs(
|
|
||||||
void *ctx,
|
|
||||||
FFICallBack callback,
|
|
||||||
void *userData
|
|
||||||
);
|
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -93,17 +62,15 @@ _repo_root = Path(__file__).resolve().parents[1]
|
|||||||
lib = ffi.dlopen(str(_repo_root / "lib" / "liblogosdelivery.so"))
|
lib = ffi.dlopen(str(_repo_root / "lib" / "liblogosdelivery.so"))
|
||||||
|
|
||||||
CallbackType = ffi.callback("void(int, const char*, size_t, void*)")
|
CallbackType = ffi.callback("void(int, const char*, size_t, void*)")
|
||||||
|
ReplyCallbackType = ffi.callback("void(int, const char*, const char*, void*)")
|
||||||
|
|
||||||
RET_OK = 0
|
# Non-terminal progress tick (~every 5s while a request is in flight), always
|
||||||
|
# followed by a terminal RET_OK/RET_ERR. The waiting callbacks drop it so a slow
|
||||||
# Non-terminal progress tick. It fires every ~5s while a request is still in
|
# call (start_node most of all) is not latched as a result.
|
||||||
# flight and is always followed by a terminal RET_OK/RET_ERR, so a caller that
|
|
||||||
# latched it would fail every call slower than five seconds -- start_node most
|
|
||||||
# of all, since it boots the node and joins the network.
|
|
||||||
RET_STALE_WARN = 3
|
RET_STALE_WARN = 3
|
||||||
|
|
||||||
# Since 0.3.0 a listener is registered per event name, so a caller that wants
|
# Every event the library emits. Since 0.3.0 a listener is registered per event
|
||||||
# every event registers once per name.
|
# name, so an `event_cb` that wants them all registers once per name.
|
||||||
EVENT_NAMES = (
|
EVENT_NAMES = (
|
||||||
"onMessageSent",
|
"onMessageSent",
|
||||||
"onMessageError",
|
"onMessageError",
|
||||||
@ -118,43 +85,6 @@ EVENT_NAMES = (
|
|||||||
"onChannelMessageError",
|
"onChannelMessageError",
|
||||||
)
|
)
|
||||||
|
|
||||||
_CBOR_MAJOR_BYTES = 2
|
|
||||||
_CBOR_MAJOR_TEXT = 3
|
|
||||||
|
|
||||||
# The event thread calls these from outside Python. cffi frees the trampoline
|
|
||||||
# when the object dies, so a late event would land on freed memory; keep every
|
|
||||||
# event callback alive for the whole process.
|
|
||||||
_PINNED_EVENT_CALLBACKS = []
|
|
||||||
|
|
||||||
|
|
||||||
def _decode_cbor_string(raw: bytes) -> bytes:
|
|
||||||
"""Unwrap a CBOR definite-length text/byte string.
|
|
||||||
|
|
||||||
Since 0.3.0 the library encodes every RET_OK reply payload with CBOR. Error
|
|
||||||
payloads and the RET_STALE_WARN tick stay plain, and so do event payloads.
|
|
||||||
"""
|
|
||||||
if not raw:
|
|
||||||
return b""
|
|
||||||
|
|
||||||
header = raw[0]
|
|
||||||
if header >> 5 not in (_CBOR_MAJOR_BYTES, _CBOR_MAJOR_TEXT):
|
|
||||||
raise ValueError(f"reply is not a CBOR string: header {header:#04x}")
|
|
||||||
|
|
||||||
info = header & 0x1F
|
|
||||||
if info < 24:
|
|
||||||
length, offset = info, 1
|
|
||||||
elif info <= 27:
|
|
||||||
size = 1 << (info - 24)
|
|
||||||
length, offset = int.from_bytes(raw[1 : 1 + size], "big"), 1 + size
|
|
||||||
else:
|
|
||||||
raise ValueError(f"unsupported CBOR string header: {header:#04x}")
|
|
||||||
|
|
||||||
payload = raw[offset : offset + length]
|
|
||||||
if len(payload) != length:
|
|
||||||
raise ValueError(f"truncated CBOR string: want {length} bytes, got {len(payload)}")
|
|
||||||
|
|
||||||
return payload
|
|
||||||
|
|
||||||
|
|
||||||
def _new_cb_state():
|
def _new_cb_state():
|
||||||
return {
|
return {
|
||||||
@ -173,17 +103,10 @@ def _wait_cb_raw(
|
|||||||
if not ok:
|
if not ok:
|
||||||
return Err(f"{op_name}: timeout after {timeout_s}s")
|
return Err(f"{op_name}: timeout after {timeout_s}s")
|
||||||
|
|
||||||
cb_ret = state["ret"]
|
if state["ret"] is None:
|
||||||
if cb_ret is None:
|
|
||||||
return Err(f"{op_name}: callback ret is None")
|
return Err(f"{op_name}: callback ret is None")
|
||||||
|
|
||||||
if cb_ret != RET_OK:
|
return Ok((state["ret"], state["msg"]))
|
||||||
return Ok((cb_ret, state["msg"]))
|
|
||||||
|
|
||||||
try:
|
|
||||||
return Ok((cb_ret, _decode_cbor_string(state["msg"])))
|
|
||||||
except ValueError as e:
|
|
||||||
return Err(f"{op_name}: {e}")
|
|
||||||
|
|
||||||
|
|
||||||
def _wait_cb_ok(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]:
|
||||||
@ -220,15 +143,29 @@ class NodeWrapper:
|
|||||||
|
|
||||||
return CallbackType(c_cb)
|
return CallbackType(c_cb)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _make_waiting_reply_cb(state):
|
||||||
|
def c_cb(err_code, reply_p, err_p, userData):
|
||||||
|
if int(err_code) == RET_STALE_WARN:
|
||||||
|
return
|
||||||
|
|
||||||
|
text_p = reply_p if int(err_code) == 0 else err_p
|
||||||
|
msg = ffi.string(text_p) if text_p != ffi.NULL else b""
|
||||||
|
|
||||||
|
if not state["done"].is_set():
|
||||||
|
state["ret"] = int(err_code)
|
||||||
|
state["msg"] = msg
|
||||||
|
state["done"].set()
|
||||||
|
|
||||||
|
return ReplyCallbackType(c_cb)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _make_event_cb(py_callback):
|
def _make_event_cb(py_callback):
|
||||||
def c_cb(ret, char_p, length, userData):
|
def c_cb(ret, char_p, length, userData):
|
||||||
msg = ffi.buffer(char_p, length)[:] if char_p != ffi.NULL else b""
|
msg = ffi.buffer(char_p, length)[:] if char_p != ffi.NULL else b""
|
||||||
py_callback(int(ret), msg)
|
py_callback(int(ret), msg)
|
||||||
|
|
||||||
handler = CallbackType(c_cb)
|
return CallbackType(c_cb)
|
||||||
_PINNED_EVENT_CALLBACKS.append(handler)
|
|
||||||
return handler
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_node(
|
def create_node(
|
||||||
@ -242,41 +179,40 @@ class NodeWrapper:
|
|||||||
config_buffer = ffi.new("char[]", config_json.encode("utf-8"))
|
config_buffer = ffi.new("char[]", config_json.encode("utf-8"))
|
||||||
|
|
||||||
state = _new_cb_state()
|
state = _new_cb_state()
|
||||||
cb = cls._make_waiting_cb(state)
|
cb = cls._make_waiting_reply_cb(state)
|
||||||
|
|
||||||
ctx = lib.logosdelivery_create_node(
|
req = ffi.new("CreateNodeCtorReq *", {"configJson": config_buffer})
|
||||||
config_buffer,
|
lib.logosdelivery_create_node(req, cb, ffi.NULL)
|
||||||
cb,
|
|
||||||
ffi.NULL,
|
wait_result = _wait_cb_ok(state, "create_node", timeout_s)
|
||||||
)
|
if wait_result.is_err():
|
||||||
|
return Err(wait_result.err())
|
||||||
|
|
||||||
|
# The constructor reports the context address as decimal text.
|
||||||
|
try:
|
||||||
|
ctx = ffi.cast("void *", int(state["msg"].decode("utf-8")))
|
||||||
|
except Exception as e:
|
||||||
|
return Err(f"create_node: invalid context address: {e}")
|
||||||
|
|
||||||
if ctx == ffi.NULL:
|
if ctx == ffi.NULL:
|
||||||
return Err("create_node: ctx is NULL")
|
return Err("create_node: ctx is NULL")
|
||||||
|
|
||||||
node = cls(ctx, config_buffer, None)
|
event_cb_handler = None
|
||||||
|
listener_ids = []
|
||||||
|
if event_cb is not None:
|
||||||
|
event_cb_handler = cls._make_event_cb(event_cb)
|
||||||
|
for event_name in EVENT_NAMES:
|
||||||
|
listener_id = lib.logosdelivery_add_event_listener(
|
||||||
|
ctx,
|
||||||
|
event_name.encode("utf-8"),
|
||||||
|
event_cb_handler,
|
||||||
|
ffi.NULL,
|
||||||
|
)
|
||||||
|
if listener_id == 0:
|
||||||
|
return Err(f"create_node: add_event_listener({event_name}) failed")
|
||||||
|
listener_ids.append(listener_id)
|
||||||
|
|
||||||
wait_result = _wait_cb_ok(state, "create_node", timeout_s)
|
return Ok(cls(ctx, config_buffer, event_cb_handler, listener_ids))
|
||||||
if wait_result.is_err():
|
|
||||||
node.destroy()
|
|
||||||
return Err(wait_result.err())
|
|
||||||
|
|
||||||
if event_cb is None:
|
|
||||||
return Ok(node)
|
|
||||||
|
|
||||||
node._event_cb_handler = cls._make_event_cb(event_cb)
|
|
||||||
for event_name in EVENT_NAMES:
|
|
||||||
listener_id = lib.logosdelivery_add_event_listener(
|
|
||||||
ctx,
|
|
||||||
event_name.encode("utf-8"),
|
|
||||||
node._event_cb_handler,
|
|
||||||
ffi.NULL,
|
|
||||||
)
|
|
||||||
if listener_id == 0:
|
|
||||||
node.destroy()
|
|
||||||
return Err(f"create_node: add_event_listener({event_name}) failed")
|
|
||||||
node._listener_ids += (listener_id,)
|
|
||||||
|
|
||||||
return Ok(node)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_and_start(
|
def create_and_start(
|
||||||
@ -327,7 +263,7 @@ class NodeWrapper:
|
|||||||
|
|
||||||
def destroy(self, *, timeout_s: float = 20.0) -> Result[int, str]:
|
def destroy(self, *, timeout_s: float = 20.0) -> Result[int, str]:
|
||||||
if self.ctx == ffi.NULL:
|
if self.ctx == ffi.NULL:
|
||||||
return Ok(RET_OK)
|
return Ok(0)
|
||||||
|
|
||||||
# Drop the listeners first so the event thread cannot reach the Python
|
# Drop the listeners first so the event thread cannot reach the Python
|
||||||
# callback once the context is gone.
|
# callback once the context is gone.
|
||||||
@ -335,19 +271,12 @@ class NodeWrapper:
|
|||||||
lib.logosdelivery_remove_event_listener(self.ctx, listener_id)
|
lib.logosdelivery_remove_event_listener(self.ctx, listener_id)
|
||||||
self._listener_ids = ()
|
self._listener_ids = ()
|
||||||
|
|
||||||
state = _new_cb_state()
|
rc = lib.logosdelivery_destroy(self.ctx)
|
||||||
cb = self._make_waiting_cb(state)
|
|
||||||
|
|
||||||
rc = lib.logosdelivery_destroy(self.ctx, cb, ffi.NULL)
|
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
return Err(f"destroy: immediate call failed (ret={rc})")
|
return Err(f"destroy: call failed (ret={rc})")
|
||||||
|
|
||||||
wait_result = _wait_cb_ok(state, "destroy", timeout_s)
|
|
||||||
if wait_result.is_err():
|
|
||||||
return Err(wait_result.err())
|
|
||||||
|
|
||||||
self.ctx = ffi.NULL
|
self.ctx = ffi.NULL
|
||||||
return wait_result
|
return Ok(rc)
|
||||||
|
|
||||||
def stop_and_destroy(self, *, timeout_s: float = 20.0) -> Result[int, str]:
|
def stop_and_destroy(self, *, timeout_s: float = 20.0) -> Result[int, str]:
|
||||||
stop_result = self.stop_node(timeout_s=timeout_s)
|
stop_result = self.stop_node(timeout_s=timeout_s)
|
||||||
@ -363,14 +292,11 @@ class NodeWrapper:
|
|||||||
|
|
||||||
def subscribe_content_topic(self, content_topic: str, *, timeout_s: float = 20.0) -> Result[int, str]:
|
def subscribe_content_topic(self, content_topic: str, *, timeout_s: float = 20.0) -> Result[int, str]:
|
||||||
state = _new_cb_state()
|
state = _new_cb_state()
|
||||||
cb = self._make_waiting_cb(state)
|
cb = self._make_waiting_reply_cb(state)
|
||||||
|
|
||||||
rc = lib.logosdelivery_subscribe(
|
topic_buffer = ffi.new("char[]", content_topic.encode("utf-8"))
|
||||||
self.ctx,
|
req = ffi.new("SubscribeReq *", {"contentTopicStr": topic_buffer})
|
||||||
cb,
|
rc = lib.logosdelivery_subscribe(self.ctx, cb, ffi.NULL, req)
|
||||||
ffi.NULL,
|
|
||||||
content_topic.encode("utf-8"),
|
|
||||||
)
|
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
return Err(f"subscribe_content_topic: immediate call failed (ret={rc})")
|
return Err(f"subscribe_content_topic: immediate call failed (ret={rc})")
|
||||||
|
|
||||||
@ -378,14 +304,11 @@ class NodeWrapper:
|
|||||||
|
|
||||||
def unsubscribe_content_topic(self, content_topic: str, *, timeout_s: float = 20.0) -> Result[int, str]:
|
def unsubscribe_content_topic(self, content_topic: str, *, timeout_s: float = 20.0) -> Result[int, str]:
|
||||||
state = _new_cb_state()
|
state = _new_cb_state()
|
||||||
cb = self._make_waiting_cb(state)
|
cb = self._make_waiting_reply_cb(state)
|
||||||
|
|
||||||
rc = lib.logosdelivery_unsubscribe(
|
topic_buffer = ffi.new("char[]", content_topic.encode("utf-8"))
|
||||||
self.ctx,
|
req = ffi.new("UnsubscribeReq *", {"contentTopicStr": topic_buffer})
|
||||||
cb,
|
rc = lib.logosdelivery_unsubscribe(self.ctx, cb, ffi.NULL, req)
|
||||||
ffi.NULL,
|
|
||||||
content_topic.encode("utf-8"),
|
|
||||||
)
|
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
return Err(f"unsubscribe_content_topic: immediate call failed (ret={rc})")
|
return Err(f"unsubscribe_content_topic: immediate call failed (ret={rc})")
|
||||||
|
|
||||||
@ -393,16 +316,13 @@ class NodeWrapper:
|
|||||||
|
|
||||||
def send_message(self, message: dict, *, timeout_s: float = 20.0) -> Result[str, str]:
|
def send_message(self, message: dict, *, timeout_s: float = 20.0) -> Result[str, str]:
|
||||||
state = _new_cb_state()
|
state = _new_cb_state()
|
||||||
cb = self._make_waiting_cb(state)
|
cb = self._make_waiting_reply_cb(state)
|
||||||
|
|
||||||
message_json = json.dumps(message, separators=(",", ":"), ensure_ascii=False)
|
message_json = json.dumps(message, separators=(",", ":"), ensure_ascii=False)
|
||||||
|
|
||||||
rc = lib.logosdelivery_send(
|
message_buffer = ffi.new("char[]", message_json.encode("utf-8"))
|
||||||
self.ctx,
|
req = ffi.new("SendReq *", {"messageJson": message_buffer})
|
||||||
cb,
|
rc = lib.logosdelivery_send(self.ctx, cb, ffi.NULL, req)
|
||||||
ffi.NULL,
|
|
||||||
message_json.encode("utf-8"),
|
|
||||||
)
|
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
return Err(f"send_message: immediate call failed (ret={rc})")
|
return Err(f"send_message: immediate call failed (ret={rc})")
|
||||||
|
|
||||||
@ -436,20 +356,17 @@ class NodeWrapper:
|
|||||||
return Err("get_available_node_info_ids: empty response")
|
return Err("get_available_node_info_ids: empty response")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return Ok(json.loads(cb_msg.decode("utf-8").strip().lstrip("@")))
|
return Ok(json.loads(cb_msg.decode("utf-8")))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return Err(f"get_available_node_info_ids: invalid response: {e}")
|
return Err(f"get_available_node_info_ids: invalid response: {e}")
|
||||||
|
|
||||||
def get_node_info(self, node_info_id: str, *, timeout_s: float = 20.0) -> Result[dict, str]:
|
def get_node_info(self, node_info_id: str, *, timeout_s: float = 20.0) -> Result[str, str]:
|
||||||
state = _new_cb_state()
|
state = _new_cb_state()
|
||||||
cb = self._make_waiting_cb(state)
|
cb = self._make_waiting_reply_cb(state)
|
||||||
|
|
||||||
rc = lib.logosdelivery_get_node_info(
|
info_id_buffer = ffi.new("char[]", node_info_id.encode("utf-8"))
|
||||||
self.ctx,
|
req = ffi.new("GetNodeInfoReq *", {"nodeInfoId": info_id_buffer})
|
||||||
cb,
|
rc = lib.logosdelivery_get_node_info(self.ctx, cb, ffi.NULL, req)
|
||||||
ffi.NULL,
|
|
||||||
node_info_id.encode("utf-8"),
|
|
||||||
)
|
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
return Err(f"get_node_info: immediate call failed (ret={rc})")
|
return Err(f"get_node_info: immediate call failed (ret={rc})")
|
||||||
|
|
||||||
@ -461,15 +378,10 @@ class NodeWrapper:
|
|||||||
if cb_ret != 0:
|
if cb_ret != 0:
|
||||||
return Err(f"get_node_info: callback failed (ret={cb_ret}) msg={cb_msg!r}")
|
return Err(f"get_node_info: callback failed (ret={cb_ret}) msg={cb_msg!r}")
|
||||||
|
|
||||||
if not cb_msg:
|
# The item is a plain string, not JSON: a peer id, an ENR URI, a
|
||||||
return Err("get_node_info: empty response")
|
# comma-separated multiaddress list or the Prometheus metrics text.
|
||||||
|
# MyMixPubKey is legitimately empty when mix is not mounted.
|
||||||
try:
|
return Ok(cb_msg.decode("utf-8"))
|
||||||
result = json.loads(cb_msg.decode("utf-8"))
|
|
||||||
except Exception as e:
|
|
||||||
return Err(f"get_node_info: invalid json: {e}")
|
|
||||||
|
|
||||||
return Ok(result)
|
|
||||||
|
|
||||||
def get_available_configs(self, *, timeout_s: float = 20.0) -> Result[dict, str]:
|
def get_available_configs(self, *, timeout_s: float = 20.0) -> Result[dict, str]:
|
||||||
state = _new_cb_state()
|
state = _new_cb_state()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user