diff --git a/Makefile b/Makefile index 0b764dcb..ef34bcdc 100644 --- a/Makefile +++ b/Makefile @@ -128,20 +128,22 @@ static-library: -buildmode=c-archive \ -tags="${BUILD_TAGS}" \ -o ./build/lib/libgowaku.a \ - ./library/ + ./library/c/ @echo "Static library built:" + sed -i "s/#include //gi" ./build/lib/libgowaku.h @ls -la ./build/lib/libgowaku.* dynamic-library: @echo "Building shared library..." + rm -f ./build/lib/libgowaku.$(GOBIN_SHARED_LIB_EXT)* $(GOBIN_SHARED_LIB_CFLAGS) $(GOBIN_SHARED_LIB_CGO_LDFLAGS) ${GOBIN} build \ -buildmode=c-shared \ -tags="${BUILD_TAGS}" \ -o ./build/lib/libgowaku.$(GOBIN_SHARED_LIB_EXT) \ - ./library/ + ./library/c/ + sed -i "s/#include //gi" ./build/lib/libgowaku.h ifeq ($(detected_OS),Linux) cd ./build/lib && \ - ls -lah . && \ mv ./libgowaku.$(GOBIN_SHARED_LIB_EXT) ./libgowaku.$(GOBIN_SHARED_LIB_EXT).0 && \ ln -s ./libgowaku.$(GOBIN_SHARED_LIB_EXT).0 ./libgowaku.$(GOBIN_SHARED_LIB_EXT) endif @@ -152,14 +154,14 @@ mobile-android: @echo "Android target: ${ANDROID_TARGET} (override with ANDROID_TARGET var)" gomobile init && \ ${GOBIN} get -d golang.org/x/mobile/cmd/gomobile && \ - gomobile bind -v -target=android -androidapi=${ANDROID_TARGET} -ldflags="-s -w" -tags="${BUILD_TAGS}" $(BUILD_FLAGS) -o ./build/lib/gowaku.aar ./mobile + gomobile bind -v -target=android -androidapi=${ANDROID_TARGET} -ldflags="-s -w" -tags="${BUILD_TAGS}" $(BUILD_FLAGS) -o ./build/lib/gowaku.aar ./library/mobile @echo "Android library built:" @ls -la ./build/lib/*.aar ./build/lib/*.jar mobile-ios: gomobile init && \ ${GOBIN} get -d golang.org/x/mobile/cmd/gomobile && \ - gomobile bind -target=ios -ldflags="-s -w" -tags="nowatchdog ${BUILD_TAGS}" $(BUILD_FLAGS) -o ./build/lib/Gowaku.xcframework ./mobile + gomobile bind -target=ios -ldflags="-s -w" -tags="nowatchdog ${BUILD_TAGS}" $(BUILD_FLAGS) -o ./build/lib/Gowaku.xcframework ./library/mobile @echo "IOS library built:" @ls -la ./build/lib/*.xcframework diff --git a/ci/Jenkinsfile.nix-flake b/ci/Jenkinsfile.nix-flake index bbfa9fe6..a6fd4ea1 100644 --- a/ci/Jenkinsfile.nix-flake +++ b/ci/Jenkinsfile.nix-flake @@ -53,7 +53,7 @@ pipeline { } stage('Check') { steps { - sh 'ldd ./result/bin/library' + sh 'ldd ./result/bin/c' } } } diff --git a/examples/c-bindings/Makefile b/examples/c-bindings/Makefile index 8166fbb4..886cca31 100644 --- a/examples/c-bindings/Makefile +++ b/examples/c-bindings/Makefile @@ -31,7 +31,7 @@ build: + mkdir -p build $(CC) \ -I../../build/lib/ \ - main.c \ + main.c base64.c \ ../../build/lib/libgowaku.a \ -lm \ -pthread \ diff --git a/examples/c-bindings/base64.c b/examples/c-bindings/base64.c new file mode 100644 index 00000000..7331be5e --- /dev/null +++ b/examples/c-bindings/base64.c @@ -0,0 +1,152 @@ + +#include "base64.h" +#include + +// source: https://nachtimwald.com/2017/11/18/base64-encode-and-decode-in-c/ + +size_t b64_encoded_size(size_t inlen) +{ + size_t ret; + + ret = inlen; + if (inlen % 3 != 0) + ret += 3 - (inlen % 3); + ret /= 3; + ret *= 4; + + return ret; +} + +const char b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +char *b64_encode(const unsigned char *in, size_t len) +{ + char *out; + size_t elen; + size_t i; + size_t j; + size_t v; + + if (in == NULL || len == 0) + return NULL; + + elen = b64_encoded_size(len); + out = malloc(elen+1); + out[elen] = '\0'; + + for (i=0, j=0; i> 18) & 0x3F]; + out[j+1] = b64chars[(v >> 12) & 0x3F]; + if (i+1 < len) { + out[j+2] = b64chars[(v >> 6) & 0x3F]; + } else { + out[j+2] = '='; + } + if (i+2 < len) { + out[j+3] = b64chars[v & 0x3F]; + } else { + out[j+3] = '='; + } + } + + return out; +} + +size_t b64_decoded_size(const char *in) +{ + size_t len; + size_t ret; + size_t i; + + if (in == NULL) + return 0; + + len = strlen(in); + ret = len / 4 * 3; + + for (i=len; i-->0; ) { + if (in[i] == '=') { + ret--; + } else { + break; + } + } + + return ret; +} + +int b64_isvalidchar(char c) +{ + if (c >= '0' && c <= '9') + return 1; + if (c >= 'A' && c <= 'Z') + return 1; + if (c >= 'a' && c <= 'z') + return 1; + if (c == '+' || c == '/' || c == '=') + return 1; + return 0; +} + + +int b64invs[] = { 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51 }; + + +void b64_generate_decode_table() +{ + int inv[80]; + size_t i; + + memset(inv, -1, sizeof(inv)); + for (i=0; i> 16) & 0xFF; + if (in[i+2] != '=') + out[j+1] = (v >> 8) & 0xFF; + if (in[i+3] != '=') + out[j+2] = v & 0xFF; + } + + return 1; +} + + diff --git a/examples/c-bindings/base64.h b/examples/c-bindings/base64.h new file mode 100644 index 00000000..edd14784 --- /dev/null +++ b/examples/c-bindings/base64.h @@ -0,0 +1,15 @@ + +#ifndef _BASE64_H_ +#define _BASE64_H_ + +#include + +size_t b64_encoded_size(size_t inlen); + +char *b64_encode(const unsigned char *in, size_t len); + +size_t b64_decoded_size(const char *in); + +int b64_decode(const char *in, unsigned char *out, size_t outlen); + +#endif diff --git a/examples/c-bindings/main.c b/examples/c-bindings/main.c index 9d1fa39a..a17d28ae 100644 --- a/examples/c-bindings/main.c +++ b/examples/c-bindings/main.c @@ -8,6 +8,7 @@ #include "libgowaku.h" #include "nxjson.c" +#include "base64.h" #include "main.h" char *alicePrivKey = "0x4f012057e1a1458ce34189cb27daedbbe434f3df0825c1949475dec786e2c64e"; @@ -16,7 +17,24 @@ char *alicePubKey = "0x0440f05847c4c7166f57ae8ecaaf72d31bddcbca345e26713ca9e26c9 char *bobPrivKey = "0xb91d6b2df8fb6ef8b53b51b2b30a408c49d5e2b530502d58ac8f94e5c5de1453"; char *bobPubKey = "0x045eef61a98ba1cf44a2736fac91183ea2bd86e67de20fe4bff467a71249a8a0c05f795dd7f28ced7c15eaa69c89d4212cc4f526ca5e9a62e88008f506d850cccd"; -void callBack(char *signal) + +char* result = NULL; +char* contentTopic; + +void handle_ok(const char* msg, size_t len) { + if (result != NULL) { + free(result); + } + result = malloc(len * sizeof(char) + 1); + strcpy(result, msg); +} + +void handle_error(const char* msg, size_t len) { + printf("Error: %s\n", msg); + exit(1); +} + +void callBack(const char* signal, size_t len_0) { // This callback will be executed each time a new message is received @@ -36,29 +54,34 @@ void callBack(char *signal) } }*/ - const nx_json *json = nx_json_parse(signal, 0); + const nx_json *json = nx_json_parse((char*) signal, 0); const char *type = nx_json_get(json, "type")->text_value; if (strcmp(type, "message") == 0) { - char* msg = utils_extract_wakumessage_from_signal(json); - char *decodedMsg = waku_decode_asymmetric(msg, bobPrivKey); - free(msg); + const nx_json *wakuMsgJson = nx_json_get(nx_json_get(json, "event"), "wakuMessage"); + const char *contentTopic = nx_json_get(wakuMsgJson, "contentTopic")->text_value; - if(isError(decodedMsg)) { - free(decodedMsg); - return; + if (strcmp(contentTopic, contentTopic) == 0) + { + char* msg = utils_extract_wakumessage_from_signal(wakuMsgJson); + WAKU_CALL(waku_decode_asymmetric(msg, bobPrivKey, handle_ok, handle_error)); + + char *decodedMsg = strdup(result); + + const nx_json *dataJson = nx_json_parse(decodedMsg, 0); + + const char *pubkey = nx_json_get(dataJson, "pubkey")->text_value; + const char *base64data = nx_json_get(dataJson, "data")->text_value; + + size_t data_len = b64_decoded_size(base64data); + char *data = malloc(data_len); + + b64_decode(base64data, (unsigned char *)data, data_len) ; + + printf(">>> Received \"%s\" from %s\n", data, pubkey); + fflush(stdout); } - - - const nx_json *dataJson = nx_json_parse(decodedMsg, 0); - const char *pubkey = nx_json_get(nx_json_get(dataJson, "result"), "pubkey")->text_value; - const char *base64data = nx_json_get(nx_json_get(dataJson, "result"), "data")->text_value; - char *data = waku_utils_base64_decode((char*)base64data); - - printf(">>> Received \"%s\" from %s\n", utils_get_str(data), pubkey); - fflush(stdout); - free(data); } nx_json_free(json); @@ -66,50 +89,39 @@ void callBack(char *signal) int main(int argc, char *argv[]) { - char *response; waku_set_event_callback(callBack); char *configJSON = "{\"host\": \"0.0.0.0\", \"port\": 60000, \"logLevel\":\"error\", \"store\":true}"; - response = waku_new(configJSON); // configJSON can be NULL too to use defaults - if (isError(response)) - return 1; + WAKU_CALL( waku_new(configJSON, handle_error) ); // configJSON can be NULL too to use defaults - response = waku_start(); // Start the node, enabling the waku protocols - if (isError(response)) - return 1; + WAKU_CALL(waku_start(handle_error) ); // Start the node, enabling the waku protocols - response = waku_peerid(); // Obtain the node peerID - if (isError(response)) - return 1; - char *nodePeerID = utils_get_str(response); - printf("PeerID: %s\n", nodePeerID); + WAKU_CALL(waku_peerid(handle_ok, handle_error)); // Obtain the node peerID + char *peerID = strdup(result); + printf("PeerID: %s\n", result); + WAKU_CALL(waku_content_topic("example", 1, "default", "rfc26", handle_ok)); + contentTopic = strdup(result); - response = waku_connect("/dns4/node-01.gc-us-central1-a.wakuv2.test.statusim.net/tcp/30303/p2p/16Uiu2HAmJb2e28qLXxT5kZxVUUoJt72EMzNGXB47Rxx5hw3q4YjS", 0); // Connect to a node - if (isError(response)) - printf("Could not connect to node: %s\n", response); + WAKU_CALL(waku_connect("/dns4/node-01.gc-us-central1-a.wakuv2.test.statusim.net/tcp/30303/p2p/16Uiu2HAmJb2e28qLXxT5kZxVUUoJt72EMzNGXB47Rxx5hw3q4YjS", 0, handle_error)); // Connect to a node - - /* // To use dns discovery, and retrieve nodes from a enrtree url - response = waku_dns_discovery("enrtree://AOGECG2SPND25EEFMAJ5WF3KSGJNSGV356DSTL2YVLLZWIV6SAYBM@test.waku.nodes.status.im", "", 0); // Discover Nodes - if (isError(response)) - return 1; - printf("Discovered nodes: %s\n", response); - */ + WAKU_CALL( waku_dns_discovery("enrtree://AOGECG2SPND25EEFMAJ5WF3KSGJNSGV356DSTL2YVLLZWIV6SAYBM@test.waku.nodes.status.im", "", 0, handle_ok, handle_error)); // Discover Nodes + printf("Discovered nodes: %s\n", result); - /* + WAKU_CALL(waku_default_pubsub_topic(handle_ok)); + char *pubsubTopic = strdup(result); + printf("Default pubsub topic: %s\n", pubsubTopic); + // To see a store query in action: + /* char query[1000]; - sprintf(query, "{\"pubsubTopic\":\"%s\", \"pagingOptions\":{\"pageSize\": 40, \"forward\":false}}", waku_default_pubsub_topic()); - response = waku_store_query(query, NULL, 0); - if (isError(response)) - return 1; - printf("%s\n",response); + sprintf(query, "{\"pubsubTopic\":\"%s\", \"pagingOptions\":{\"pageSize\": 40, \"forward\":false}}", pubsubTopic); + WAKU_CALL(waku_store_query(query, NULL, 0, handle_ok, handle_error)); + printf("%s\n",result); */ - response = waku_relay_subscribe(NULL); - if (isError(response)) - return 1; + + WAKU_CALL( waku_relay_subscribe(NULL, handle_error)); int i = 0; int version = 1; @@ -118,20 +130,18 @@ int main(int argc, char *argv[]) i++; char wakuMsg[1000]; - char *msgPayload = waku_utils_base64_encode("Hello World!"); - char *contentTopic = waku_content_topic("example", 1, "default", "rfc26"); + char *msgPayload = b64_encode("Hello World!", 12); + sprintf(wakuMsg, "{\"payload\":\"%s\",\"contentTopic\":\"%s\",\"timestamp\":%"PRIu64"}", msgPayload, contentTopic, nowInNanosecs()); free(msgPayload); - free(contentTopic); + WAKU_CALL(waku_relay_publish_enc_asymmetric(wakuMsg, NULL, bobPubKey, alicePrivKey, 0, handle_ok, handle_error)); // Broadcast via waku relay a message encrypting it with Bob's PubK, and signing it with Alice PrivK + printf("C\n"); - response = waku_relay_publish_enc_asymmetric(wakuMsg, NULL, bobPubKey, alicePrivKey, 0); // Broadcast via waku relay a message encrypting it with Bob's PubK, and signing it with Alice PrivK - // response = waku_lightpush_publish_enc_asymmetric(wakuMsg, NULL, NULL, bobPubKey, alicePrivKey, 0); // Broadcast via waku lightpush a message encrypting it with Bob's PubK, and signing it with Alice PrivK - if (isError(response)) - return 1; - - // char *messageID = utils_get_str(response); - // free(messageID); + char *messageID = strdup(result); + printf("MessageID: %s\n",messageID); + + free(messageID); sleep(1); } @@ -140,16 +150,12 @@ int main(int argc, char *argv[]) // To retrieve messages from local store, set store:true in the node config, and use waku_store_local_query /* char query[1000]; - sprintf(query, "{\"pubsubTopic\":\"%s\", \"pagingOptions\":{\"pageSize\": 40, \"forward\":false}}", waku_default_pubsub_topic()); - response = waku_store_local_query(query); - if (isError(response)) - return 1; - printf("%s\n",response); + sprintf(query, "{\"pubsubTopic\":\"%s\", \"pagingOptions\":{\"pageSize\": 40, \"forward\":false}}", pubsubTopic); + WAKU_CALL(waku_store_local_query(query, handle_ok, handle_error)); + printf("%s\n",result); */ - response = waku_stop(); - if (isError(response)) - return 1; - + WAKU_CALL(waku_stop(handle_error)); + return 0; } diff --git a/examples/c-bindings/main.h b/examples/c-bindings/main.h index 31e9a95a..66fb1f69 100644 --- a/examples/c-bindings/main.h +++ b/examples/c-bindings/main.h @@ -7,6 +7,14 @@ /// Convert seconds to nanoseconds #define SEC_TO_NS(sec) ((sec)*1000000000) +#define WAKU_CALL(call) \ +do { \ + int ret = call; \ + if (ret != 0) { \ + printf("Failed the call to: %s. Returned code: %d\n", #call, ret); \ + exit(1); \ + } \ +} while (0) uint64_t nowInNanosecs(){ uint64_t nanoseconds; @@ -48,9 +56,8 @@ bool isError(char *input) } -char *utils_extract_wakumessage_from_signal(const nx_json *signal) +char *utils_extract_wakumessage_from_signal(const nx_json *wakuMsgJson) { - const nx_json *wakuMsgJson = nx_json_get(nx_json_get(signal, "event"), "wakuMessage"); const char *payload = nx_json_get(wakuMsgJson, "payload")->text_value; const char *contentTopic = nx_json_get(wakuMsgJson, "contentTopic")->text_value; int version = nx_json_get(wakuMsgJson, "version")->int_value; @@ -62,38 +69,5 @@ char *utils_extract_wakumessage_from_signal(const nx_json *signal) return response; } -long long utils_get_int(char *input) -{ - char *jsonStr = malloc(strlen(input) + 1); - strcpy(jsonStr, input); - const nx_json *json = nx_json_parse(jsonStr, 0); - long long result = -1; - if (json) - { - result = nx_json_get(json, "result")->int_value; - } - nx_json_free(json); - free(jsonStr); - - return result; -} - -char *utils_get_str(char *input) -{ - char *jsonStr = malloc(strlen(input) + 1); - strcpy(jsonStr, input); - const nx_json *json = nx_json_parse(jsonStr, 0); - char *result = ""; - if (json) - { - const char *text_value = nx_json_get(json, "result")->text_value; - result = strdup(text_value); - } - - nx_json_free(json); - free(jsonStr); - - return result; -} #endif /* MAIN_H */ \ No newline at end of file diff --git a/flake.nix b/flake.nix index 7bfe7309..fd0c0a9a 100644 --- a/flake.nix +++ b/flake.nix @@ -35,7 +35,7 @@ in rec { packages = forAllSystems (system: { node = buildPackage system ["cmd/waku"]; - library = buildPackage system ["library"]; + library = buildPackage system ["library/c"]; }); defaultPackage = forAllSystems (system: diff --git a/library/api_utils.go b/library/api_utils.go deleted file mode 100644 index c869f3ac..00000000 --- a/library/api_utils.go +++ /dev/null @@ -1,42 +0,0 @@ -package main - -/* -#include -#include -*/ -import "C" -import ( - "encoding/base64" - "unsafe" - - mobile "github.com/waku-org/go-waku/mobile" -) - -// Decode a base64 string (useful for reading the payload from waku messages) -// -//export waku_utils_base64_decode -func waku_utils_base64_decode(data *C.char) *C.char { - b, err := base64.StdEncoding.DecodeString(C.GoString(data)) - if err != nil { - return C.CString(mobile.MakeJSONResponse(err)) - } - - return C.CString(mobile.PrepareJSONResponse(string(b), nil)) -} - -// Encode data to base64 (useful for creating the payload of a waku message in the -// format understood by waku_relay_publish) -// -//export waku_utils_base64_encode -func waku_utils_base64_encode(data *C.char) *C.char { - str := base64.StdEncoding.EncodeToString([]byte(C.GoString(data))) - return C.CString(string(str)) - -} - -// Frees a char* since all strings returned by gowaku are allocated in the C heap using malloc. -// -//export waku_utils_free -func waku_utils_free(data *C.char) { - C.free(unsafe.Pointer(data)) -} diff --git a/library/README.md b/library/c/README.md similarity index 67% rename from library/README.md rename to library/c/README.md index d726b50e..d928ade7 100644 --- a/library/README.md +++ b/library/c/README.md @@ -6,7 +6,7 @@ This specification describes the API for consuming go-waku when built as a dynam # Introduction -Native applications that wish to integrate Waku may not be able to use nwaku and its JSON RPC API due to constraints +Native applications that wish to integrate Waku may not be able to use go-waku/nwaku and its JSON RPC API due to constraints on packaging, performance or executables. An alternative is to link existing Waku implementation as a static or dynamic library in their application. @@ -18,36 +18,21 @@ consume them. ## General -### `JsonResponse` type +### `WakuMessageCallback` type -All the API functions return a `JsonResponse` unless specified otherwise. -`JsonResponse` is a `char *` whose format depends on whether the function was executed successfully or not. - -On failure: - -```ts -{ - error: string; -} +All the API functions require passing callbacks which will be executed depending on the result of the execution result. +These callbacks are defined as +```c +typedef void (*WakuCallBack) (const char* msg, size_t len_0); ``` +With `msg` containing a `\0` terminated string, and `len_0` the length of this string. The format of the data sent to these callbacks +will depend on the function being executed. The data can be characters, numeric or json. -For example: - -```json -{ - "error": "the error message" -} -``` - -On success: - -```ts -{ - result: any -} -``` - -The type of the `result` object depends on the function it was returned by. +### Status Codes +The API functions return an integer with status codes depending on the execution result. The following status codes are defined: +- `0` - Success +- `1` - Error +- `2` - Missing callback ### `JsonMessage` type @@ -496,9 +481,7 @@ If a key is `undefined`, or `null`, a default value will be set. If using `secur - `keyPath`: secure websocket key path - - -### `extern char* waku_new(char* jsonConfig)` +### `extern int waku_new(char* jsonConfig, WakuCallBack onErrCb)` Instantiates a Waku node. @@ -507,96 +490,82 @@ Instantiates a Waku node. 1. `char* jsonConfig`: JSON string containing the options used to initialize a go-waku node. Type [`JsonConfig`](#jsonconfig-type). It can be `NULL` to use defaults. +2. `WakuCallBack onErrCb`: callback to be executed if the function fails **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field is set to `true`. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. -For example: - -```json -{ - "result": true -} -``` - -### `extern char* waku_start()` +### `extern int waku_start(WakuCallBack onErrCb)` Start a Waku node mounting all the protocols that were enabled during the Waku node instantiation. +**Parameters** + +1. `WakuCallBack onErrCb`: callback to be executed if the function fails + **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field is set to `true`. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. -For example: - -```json -{ - "result": true -} -``` - -### `extern char* waku_stop()` +### `extern int waku_stop(WakuCallBack onErrCb)` Stops a Waku node. +**Parameters** + +1. `WakuCallBack onErrCb`: callback to be executed if the function fails + **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field is set to `true`. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. -For example: -```json -{ - "result": true -} -``` - -### `extern char* waku_peerid()` +### `extern int waku_peerid(WakuCallBack onOkCb, WakuCallBack onErrCb)` Get the peer ID of the waku node. +**Parameters** + +1. `WakuCallBack onOkCb`: callback to be executed if the function is succesful +2. `WakuCallBack onErrCb`: callback to be executed if the function fails + **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field contains the peer ID as a `string` (base58 encoded). +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. -For example: +If the function is executed succesfully, `onOkCb` will receive the base58 encoded peer ID, for example `QmWjHKUrXDHPCwoWXpUZ77E8o6UbAoTTZwf1AD1tDC4KNP` -```json -{ - "result": "QmWjHKUrXDHPCwoWXpUZ77E8o6UbAoTTZwf1AD1tDC4KNP" -} -``` - -### `extern char* waku_listen_addresses()` +### `extern int waku_listen_addresses(WakuCallBack onOkCb, WakuCallBack onErrCb)` Get the multiaddresses the Waku node is listening to. +**Parameters** + +1. `WakuCallBack onOkCb`: callback to be executed if the function is succesful +2. `WakuCallBack onErrCb`: callback to be executed if the function fails + **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field contains an array of multiaddresses. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. + +If the function is executed succesfully, `onOkCb` will receive a json array of multiaddresses. The multiaddresses are `string`s. For example: ```json -{ - "result": [ +[ "/ip4/127.0.0.1/tcp/30303", "/ip4/1.2.3.4/tcp/30303", "/dns4/waku.node.example/tcp/8000/wss" - ] -} +] ``` + ## Connecting to peers -### `extern char* waku_add_peer(char* address, char* protocolId)` +### `extern int waku_add_peer(char* address, char* protocolId, WakuCallBack onOkCb, WakuCallBack onErrCb)` Add a node multiaddress and protocol to the waku node's peerstore. @@ -604,21 +573,18 @@ Add a node multiaddress and protocol to the waku node's peerstore. 1. `char* address`: A multiaddress (with peer id) to reach the peer being added. 2. `char* protocolId`: A protocol we expect the peer to support. +3. `WakuCallBack onOkCb`: callback to be executed if the function is succesful +4. `WakuCallBack onErrCb`: callback to be executed if the function fails **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field contains the peer ID as a base58 `string` of the peer that was added. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. -For example: +If the function is executed succesfully, `onOkCb` will receive the base 58 peer ID of the peer that was added. -```json -{ - "result": "QmWjHKUrXDHPCwoWXpUZ77E8o6UbAoTTZwf1AD1tDC4KNP" -} -``` +For example: `QmWjHKUrXDHPCwoWXpUZ77E8o6UbAoTTZwf1AD1tDC4KNP` -### `extern char* waku_connect(char* address, int timeoutMs)` +### `extern int waku_connect(char* address, int timeoutMs, WakuCallBack onErrCb)` Dial peer using a multiaddress. @@ -629,21 +595,13 @@ Dial peer using a multiaddress. If the function execution takes longer than this value, the execution will be canceled and an error returned. Use `0` for no timeout. +3. `WakuCallBack onErrCb`: callback to be executed if the function fails **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field is set to `true`. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. -For example: - -```json -{ - "result": true -} -``` - -### `extern char* waku_connect_peerid(char* peerId, int timeoutMs)` +### `extern int waku_connect_peerid(char* peerId, int timeoutMs, WakuCallBack onErrCb)` Dial peer using its peer ID. @@ -657,89 +615,74 @@ Dial peer using its peer ID. If the function execution takes longer than this value, the execution will be canceled and an error returned. Use `0` for no timeout. +3. `WakuCallBack onErrCb`: callback to be executed if the function fails **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field is set to `true`. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. -For example: - -```json -{ - "result": true -} -``` - -### `extern char* waku_disconnect(char* peerId)` +### `extern int waku_disconnect(char* peerId, WakuCallBack onErrCb)` Disconnect a peer using its peerID **Parameters** 1. `char* peerID`: Peer ID to disconnect. +2. `WakuCallBack onErrCb`: callback to be executed if the function fails **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field is set to `true`. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. -For example: - -```json -{ - "result": true -} -``` - -### `extern char* waku_peer_count()` +### `extern int waku_peer_cnt(WakuCallBack onOkCb, WakuCallBack onErrCb)` Get number of connected peers. +**Parameters** +1. `WakuCallBack onOkCb`: callback to be executed if the function is succesful +2. `WakuCallBack onErrCb`: callback to be executed if the function fails + **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field contains an `integer` which represents the number of connected peers. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. -For example: +If the function is executed succesfully, `onOkCb` will receive the number of connected peers. -```json -{ - "result": 0 -} -``` - -### `extern char* waku_peers()` +### `extern int waku_peers(WakuCallBack onOkCb, WakuCallBack onErrCb)` Retrieve the list of peers known by the Waku node. +**Parameters** +1. `WakuCallBack onOkCb`: callback to be executed if the function is succesful +2. `WakuCallBack onErrCb`: callback to be executed if the function fails + **Returns** -A [`JsonResponse`](#jsonresponse-type) containing a list of peers. -The list of peers has this format: +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. + +If the function is executed succesfully, `onOkCb` will receive a json array with the list of peers. +This list has this format: ```json -{ - "result": [ - { - "peerID": "16Uiu2HAmJb2e28qLXxT5kZxVUUoJt72EMzNGXB47RedcBafeDCBA", - "protocols": [ - "/ipfs/id/1.0.0", - "/vac/waku/relay/2.0.0", - "/ipfs/ping/1.0.0" - ], - "addrs": [ - "/ip4/1.2.3.4/tcp/30303" - ], - "connected": true - } - ] -} +[ + { + "peerID": "16Uiu2HAmJb2e28qLXxT5kZxVUUoJt72EMzNGXB47RedcBafeDCBA", + "protocols": [ + "/ipfs/id/1.0.0", + "/vac/waku/relay/2.0.0", + "/ipfs/ping/1.0.0" + ], + "addrs": [ + "/ip4/1.2.3.4/tcp/30303" + ], + "connected": true + } +] ``` ## Waku Relay -### `extern char* waku_content_topic(char* applicationName, unsigned int applicationVersion, char* contentTopicName, char* encoding)` +### `extern int waku_content_topic(char* applicationName, unsigned int applicationVersion, char* contentTopicName, char* encoding, WakuCallBack onOkCb)` Create a content topic string according to [RFC 23](https://rfc.vac.dev/spec/23/). @@ -749,16 +692,19 @@ Create a content topic string according to [RFC 23](https://rfc.vac.dev/spec/23/ 2. `unsigned int applicationVersion` 3. `char* contentTopicName` 4. `char* encoding`: depending on the payload, use `proto`, `rlp` or `rfc26` +5. `WakuCallBack onOkCb`: callback to be executed if the function is succesful **Returns** -`char *` containing a content topic formatted according to [RFC 23](https://rfc.vac.dev/spec/23/). +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. + +If the function is executed succesfully, `onOkCb` will receive the content topic formatted according to [RFC 23](https://rfc.vac.dev/spec/23/). ``` /{application-name}/{version-of-the-application}/{content-topic-name}/{encoding} ``` -### `extern char* waku_pubsub_topic(char* name, char* encoding)` +### `extern int waku_pubsub_topic(char* name, char* encoding, WakuCallBack onOkCb)` Create a pubsub topic string according to [RFC 23](https://rfc.vac.dev/spec/23/). @@ -766,28 +712,37 @@ Create a pubsub topic string according to [RFC 23](https://rfc.vac.dev/spec/23/) 1. `char* name` 2. `char* encoding`: depending on the payload, use `proto`, `rlp` or `rfc26` +3. `WakuCallBack onOkCb`: callback to be executed if the function is succesful **Returns** -`char *` containing a content topic formatted according to [RFC 23](https://rfc.vac.dev/spec/23/). +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. + +If the function is executed succesfully, `onOkCb` will receive the pubsub topic formatted according to [RFC 23](https://rfc.vac.dev/spec/23/). ``` /waku/2/{topic-name}/{encoding} ``` -### `extern char* waku_default_pubsub_topic()` +### `extern int waku_default_pubsub_topic(WakuCallBack onOkCb)` Returns the default pubsub topic used for exchanging waku messages defined in [RFC 10](https://rfc.vac.dev/spec/10/). + +**Parameters** + +1. `char* name` +2. `char* encoding`: depending on the payload, use `proto`, `rlp` or `rfc26` +3. `WakuCallBack onOkCb`: callback to be executed if the function is succesful + **Returns** -`char *` containing the default pubsub topic: +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. -``` -/waku/2/default-waku/proto -``` +If the function is executed succesfully, `onOkCb` will receive the default pubsub topic: `/waku/2/default-waku/proto` -### `extern char* waku_relay_publish(char* messageJson, char* pubsubTopic, int timeoutMs)` + +### `extern int waku_relay_publish(char* messageJson, char* pubsubTopic, int timeoutMs, WakuCallBack onOkCb, WakuCallBack onErrCb)` Publish a message using Waku Relay. @@ -800,15 +755,18 @@ Publish a message using Waku Relay. If the function execution takes longer than this value, the execution will be canceled and an error returned. Use `0` for no timeout. +4. `WakuCallBack onOkCb`: callback to be executed if the function is succesful +5. `WakuCallBack onErrCb`: callback to be executed if the function fails Note: `messageJson.version` is overwritten to `0`. **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field contains the message ID. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. -### `extern char* waku_relay_publish_enc_asymmetric(char* messageJson, char* pubsubTopic, char* publicKey, char* optionalSigningKey, int timeoutMs)` +If the function is executed succesfully, `onOkCb` will receive the message ID. + +### `extern int waku_relay_publish_enc_asymmetric(char* messageJson, char* pubsubTopic, char* publicKey, char* optionalSigningKey, int timeoutMs, WakuCallBack onOkCb, WakuCallBack onErrCb)` Optionally sign, encrypt using asymmetric encryption @@ -825,15 +783,18 @@ and publish a message using Waku Relay. If the function execution takes longer than this value, the execution will be canceled and an error returned. Use `0` for no timeout. +6. `WakuCallBack onOkCb`: callback to be executed if the function is succesful +7. `WakuCallBack onErrCb`: callback to be executed if the function fails Note: `messageJson.version` is overwritten to `1`. **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field contains the message ID. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. -### `extern char* waku_relay_publish_enc_symmetric(char* messageJson, char* pubsubTopic, char* symmetricKey, char* optionalSigningKey, int timeoutMs)` +If the function is executed succesfully, `onOkCb` will receive the message ID. + +### `extern int waku_relay_publish_enc_symmetric(char* messageJson, char* pubsubTopic, char* symmetricKey, char* optionalSigningKey, int timeoutMs, WakuCallBack onOkCb, WakuCallBack onErrCb)` Optionally sign, encrypt using symmetric encryption @@ -850,15 +811,18 @@ and publish a message using Waku Relay. If the function execution takes longer than this value, the execution will be canceled and an error returned. Use `0` for no timeout. +6. `WakuCallBack onOkCb`: callback to be executed if the function is succesful +7. `WakuCallBack onErrCb`: callback to be executed if the function fails Note: `messageJson.version` is overwritten to `1`. **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field contains the message ID. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. -### `extern char* waku_relay_enough_peers(char* pubsubTopic)` +If the function is executed succesfully, `onOkCb` will receive the message ID. + +### `extern int waku_relay_enough_peers(char* pubsubTopic, WakuCallBack onOkCb, WakuCallBack onErrCb)` Determine if there are enough peers to publish a message on a given pubsub topic. @@ -866,21 +830,16 @@ Determine if there are enough peers to publish a message on a given pubsub topic 1. `char* pubsubTopic`: Pubsub topic to verify. If `NULL`, it verifies the number of peers in the default pubsub topic. +2. `WakuCallBack onOkCb`: callback to be executed if the function is succesful +3. `WakuCallBack onErrCb`: callback to be executed if the function fails **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field contains a `boolean` indicating whether there are enough peers. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. -For example: +If the function is executed succesfully, `onOkCb` will receive a string `boolean` indicating whether there are enough peers, i.e. `true` or `false` -```json -{ - "result": true -} -``` - -### `extern char* waku_relay_subscribe(char* topic)` +### `extern int waku_relay_subscribe(char* topic, WakuCallBack onErrCb)` Subscribe to a Waku Relay pubsub topic to receive messages. @@ -888,35 +847,30 @@ Subscribe to a Waku Relay pubsub topic to receive messages. 1. `char* topic`: Pubsub topic to subscribe to. If `NULL`, it subscribes to the default pubsub topic. +2. `WakuCallBack onErrCb`: callback to be executed if the function fails **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field is set to `true`. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. -For example: - -```json -{ - "result": true -} -``` - -### `extern char* waku_relay_topics()` +### `extern int waku_relay_topics(WakuCallBack onOkCb, WakuCallBack onErrCb)` Get the list of subscribed pubsub topics in Waku Relay. +**Parameters** +1. `WakuCallBack onOkCb`: callback to be executed if the function is succesful +2. `WakuCallBack onErrCb`: callback to be executed if the function fails + **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field will contain an array of pubsub topics. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. + +If the function is executed succesfully, `onOkCb` will receive a json array of pubsub topics. For example: ```json -{ - "result": ["pubsubTopic1", "pubsubTopic2"] -} +["pubsubTopic1", "pubsubTopic2"] ``` @@ -945,7 +899,7 @@ For Example: } ``` -### `extern char* waku_relay_unsubscribe(char* topic)` +### `extern int waku_relay_unsubscribe(char* topic, WakuCallBack onErrCb)` Closes the pubsub subscription to a pubsub topic. No more messages will be received from this pubsub topic. @@ -954,24 +908,16 @@ from this pubsub topic. 1. `char* pusubTopic`: Pubsub topic to unsubscribe from. If `NULL`, unsubscribes from the default pubsub topic. +2. `WakuCallBack onErrCb`: callback to be executed if the function fails **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field is set to `true`. - -For example: - -```json -{ - "result": true -} -``` +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. ## Waku Filter -### `extern char* waku_filter_subscribe(char* filterJSON, char* peerID, int timeoutMs)` +### `extern int waku_filter_subscribe(char* filterJSON, char* peerID, int timeoutMs, WakuCallBack onOkCb, WakuCallBack onErrCb)` Creates a subscription to a filter full node matching a content filter.. @@ -987,21 +933,22 @@ Creates a subscription to a filter full node matching a content filter.. If the function execution takes longer than this value, the execution will be canceled and an error returned. Use `0` for no timeout. +4. `WakuCallBack onOkCb`: callback to be executed if the function is succesful +5. `WakuCallBack onErrCb`: callback to be executed if the function fails **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field will contain the subscription details. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. + +If the function is executed succesfully, `onOkCb` will receive the subscription details. For example: ```json { - "result": { - "peerID": "....", - "pubsubTopic": "...", - "contentTopics": [...] - } + "peerID": "....", + "pubsubTopic": "...", + "contentTopics": [...] } ``` @@ -1031,7 +978,7 @@ For Example: ``` -### `extern char* waku_filter_ping(char* peerID, int timeoutMs)` +### `extern int waku_filter_ping(char* peerID, int timeoutMs, WakuCallBack onErrCb)` Used to know if a service node has an active subscription for this client @@ -1045,22 +992,14 @@ Used to know if a service node has an active subscription for this client If the function execution takes longer than this value, the execution will be canceled and an error returned. Use `0` for no timeout. +3. `WakuCallBack onErrCb`: callback to be executed if the function fails **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field is set to `true`. - -For example: - -```json -{ - "result": true -} -``` +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. -### `extern char* waku_filter_unsubscribe(filterJSON *C.char, char* peerID, int timeoutMs)` +### `extern int waku_filter_unsubscribe(filterJSON *C.char, char* peerID, int timeoutMs, WakuCallBack onErrCb)` Sends a requests to a service node to stop pushing messages matching this filter to this client. It might be used to modify an existing subscription by providing a subset of the original filter criteria **Parameters** @@ -1074,21 +1013,14 @@ Sends a requests to a service node to stop pushing messages matching this filter If the function execution takes longer than this value, the execution will be canceled and an error returned. Use `0` for no timeout. +4. `WakuCallBack onErrCb`: callback to be executed if the function fails **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field is set to `true`. - -For example: -```json -{ - "result": true -} -``` +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. -### `extern char* waku_filter_unsubscribe_all(char* peerID, int timeoutMs)` +### `extern int waku_filter_unsubscribe_all(char* peerID, int timeoutMs, WakuCallBack onOkCb, WakuCallBack onErrCb)` Sends a requests to a service node (or all service nodes) to stop pushing messages @@ -1103,30 +1035,31 @@ Sends a requests to a service node (or all service nodes) to stop pushing messag If the function execution takes longer than this value, the execution will be canceled and an error returned. Use `0` for no timeout. +3. `WakuCallBack onOkCb`: callback to be executed if the function is succesful +4. `WakuCallBack onErrCb`: callback to be executed if the function fails **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field will contain an array with information about the state of each unsubscription attempt (one per peer) +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. + +If the function is executed succesfully, `onOkCb` will receive an array with information about the state of each unsubscription attempt (one per peer) For example: ```json -{ - "result": [ - { - "peerID": ...., - "error": "" // Empty if succesful - }, - ... - ] -} +[ + { + "peerID": ...., + "error": "" // Empty if succesful + }, + ... +] ``` ## Waku Legacy Filter -### `extern char* waku_legacy_filter_subscribe(char* filterJSON, char* peerID, int timeoutMs)` +### `extern int waku_legacy_filter_subscribe(char* filterJSON, char* peerID, int timeoutMs, WakuCallBack onErrCb)` Creates a subscription in a lightnode for messages that matches a content filter and optionally a [PubSub `topic`](https://github.com/libp2p/specs/blob/master/pubsub/README.md#the-topic-descriptor). @@ -1142,19 +1075,11 @@ Creates a subscription in a lightnode for messages that matches a content filter If the function execution takes longer than this value, the execution will be canceled and an error returned. Use `0` for no timeout. +4. `WakuCallBack onErrCb`: callback to be executed if the function fails **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field is set to `true`. - -For example: - -```json -{ - "result": true -} -``` +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. **Events** @@ -1181,7 +1106,7 @@ For Example: } ``` -### `extern char* waku_legacy_filter_unsubscribe(char* filterJSON, int timeoutMs)` +### `extern int waku_legacy_filter_unsubscribe(char* filterJSON, int timeoutMs, WakuCallBack onErrCb)` Removes subscriptions in a light node matching a content filter and, optionally, a [PubSub `topic`](https://github.com/libp2p/specs/blob/master/pubsub/README.md#the-topic-descriptor). @@ -1192,23 +1117,15 @@ Removes subscriptions in a light node matching a content filter and, optionally, If the function execution takes longer than this value, the execution will be canceled and an error returned. Use `0` for no timeout. +3. `WakuCallBack onErrCb`: callback to be executed if the function fails **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field is set to `true`. - -For example: - -```json -{ - "result": true -} -``` +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. ## Waku Lightpush -### `extern char* waku_lightpush_publish(char* messageJSON, char* topic, char* peerID, int timeoutMs)` +### `extern int waku_lightpush_publish(char* messageJSON, char* topic, char* peerID, int timeoutMs, WakuCallBack onOkCb, WakuCallBack onErrCb)` Publish a message using Waku Lightpush. @@ -1226,15 +1143,18 @@ Publish a message using Waku Lightpush. If the function execution takes longer than this value, the execution will be canceled and an error returned. Use `0` for no timeout. +4. `WakuCallBack onOkCb`: callback to be executed if the function is succesful +5. `WakuCallBack onErrCb`: callback to be executed if the function fails Note: `messageJson.version` is overwritten to `0`. **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field contains the message ID. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. -### `extern char* waku_lightpush_publish_enc_asymmetric(char* messageJson, char* pubsubTopic, char* peerID, char* publicKey, char* optionalSigningKey, int timeoutMs)` +If the function is executed succesfully, `onOkCb` will receive the message ID. + +### `extern int waku_lightpush_publish_enc_asymmetric(char* messageJson, char* pubsubTopic, char* peerID, char* publicKey, char* optionalSigningKey, int timeoutMs, WakuCallBack onOkCb, WakuCallBack onErrCb)` Optionally sign, encrypt using asymmetric encryption @@ -1255,15 +1175,18 @@ and publish a message using Waku Lightpush. If the function execution takes longer than this value, the execution will be canceled and an error returned. Use `0` for no timeout. +7. `WakuCallBack onOkCb`: callback to be executed if the function is succesful +8. `WakuCallBack onErrCb`: callback to be executed if the function fails Note: `messageJson.version` is overwritten to `1`. **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field contains the message ID. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. -### `extern char* waku_lightpush_publish_enc_symmetric(char* messageJson, char* pubsubTopic, char* peerID, char* symmetricKey, char* optionalSigningKey, int timeoutMs)` +If the function is executed succesfully, `onOkCb` will receive the message ID. + +### `extern int waku_lightpush_publish_enc_symmetric(char* messageJson, char* pubsubTopic, char* peerID, char* symmetricKey, char* optionalSigningKey, int timeoutMs, WakuCallBack onOkCb, WakuCallBack onErrCb)` Optionally sign, encrypt using symmetric encryption @@ -1284,17 +1207,20 @@ and publish a message using Waku Lightpush. If the function execution takes longer than this value, the execution will be canceled and an error returned. Use `0` for no timeout. +7. `WakuCallBack onOkCb`: callback to be executed if the function is succesful +8. `WakuCallBack onErrCb`: callback to be executed if the function fails Note: `messageJson.version` is overwritten to `1`. **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field contains the message ID. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. + +If the function is executed succesfully, `onOkCb` will receive the message ID. ## Waku Store -### `extern char* waku_store_query(char* queryJSON, char* peerID, int timeoutMs)` +### `extern int waku_store_query(char* queryJSON, char* peerID, int timeoutMs, WakuCallBack onOkCb, WakuCallBack onErrCb)` Retrieves historical messages on specific content topics. This method may be called with [`PagingOptions`](#pagingoptions-type), to retrieve historical messages on a per-page basis. If the request included [`PagingOptions`](#pagingoptions-type), the node @@ -1312,72 +1238,98 @@ must contain a cursor pointing to the Index from which a new page can be request If the function execution takes longer than this value, the execution will be canceled and an error returned. Use `0` for no timeout. +4. `WakuCallBack onOkCb`: callback to be executed if the function is succesful +5. `WakuCallBack onErrCb`: callback to be executed if the function fails **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field contains a [`StoreResponse`](#storeresponse-type).. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. +If the function is executed succesfully, `onOkCb` will receive a [`StoreResponse`](#storeresponse-type). + +### `extern int waku_store_local_query(char* queryJSON, WakuCallBack onOkCb, WakuCallBack onErrCb)` + +Retrieves locally stored historical messages on specific content topics. This method may be called with [`PagingOptions`](#pagingoptions-type), +to retrieve historical messages on a per-page basis. If the request included [`PagingOptions`](#pagingoptions-type), the node +must return messages on a per-page basis and include [`PagingOptions`](#pagingoptions-type) in the response. These [`PagingOptions`](#pagingoptions-type) +must contain a cursor pointing to the Index from which a new page can be requested. + +**Parameters** + +1. `char* queryJSON`: JSON string containing the [`StoreQuery`](#storequery-type). +2. `int timeoutMs`: Timeout value in milliseconds to execute the call. + If the function execution takes longer than this value, + the execution will be canceled and an error returned. + Use `0` for no timeout. +3. `WakuCallBack onOkCb`: callback to be executed if the function is succesful +4. `WakuCallBack onErrCb`: callback to be executed if the function fails + +**Returns** + +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. + +If the function is executed succesfully, `onOkCb` will receive a [`StoreResponse`](#storeresponse-type). ## Decrypting messages -### `extern char* waku_decode_symmetric(char* messageJson, char* symmetricKey)` +### `extern int waku_decode_symmetric(char* messageJson, char* symmetricKey, WakuCallBack onOkCb, WakuCallBack onErrCb)` Decrypt a message using a symmetric key **Parameters** 1. `char* messageJson`: JSON string containing the [Waku Message](https://rfc.vac.dev/spec/14/) as [`JsonMessage`](#jsonmessage-type). 2. `char* symmetricKey`: 32 byte symmetric key hex encoded. +3. `WakuCallBack onOkCb`: callback to be executed if the function is succesful +4. `WakuCallBack onErrCb`: callback to be executed if the function fails Note: `messageJson.version` is expected to be `1`. **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field contains the decoded payload as a [`DecodedPayload`](#decodedpayload-type). -An `error` message otherwise. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. + +If the function is executed succesfully, `onOkCb` will receive the decoded payload as a [`DecodedPayload`](#decodedpayload-type). + ```json { - "result": { - "pubkey": "0x......", - "signature": "0x....", - "data": "...", - "padding": "..." - } + "pubkey": "0x......", + "signature": "0x....", + "data": "...", + "padding": "..." } ``` -### `extern char* waku_decode_asymmetric(char* messageJson, char* privateKey)` +### `extern int waku_decode_asymmetric(char* messageJson, char* privateKey, WakuCallBack onOkCb, WakuCallBack onErrCb)` Decrypt a message using a secp256k1 private key **Parameters** 1. `char* messageJson`: JSON string containing the [Waku Message](https://rfc.vac.dev/spec/14/) as [`JsonMessage`](#jsonmessage-type). 2. `char* privateKey`: secp256k1 private key hex encoded. +3. `WakuCallBack onOkCb`: callback to be executed if the function is succesful +4. `WakuCallBack onErrCb`: callback to be executed if the function fails Note: `messageJson.version` is expected to be `1`. **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field contains the decoded payload as a [`DecodedPayload`](#decodedpayload-type). -An `error` message otherwise. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. + +If the function is executed succesfully, `onOkCb` will receive the decoded payload as a [`DecodedPayload`](#decodedpayload-type). ```json { - "result": { - "pubkey": "0x......", - "signature": "0x....", - "data": "...", - "padding": "..." - } + "pubkey": "0x......", + "signature": "0x....", + "data": "...", + "padding": "..." } ``` ## DNS Discovery -### `extern char* waku_dns_discovery(char* url, char* nameserver, int timeoutMs)` +### `extern int waku_dns_discovery(char* url, char* nameserver, int timeoutMs, WakuCallBack onOkCb, WakuCallBack onErrCb)` Returns a list of multiaddress and enrs given a url to a DNS discoverable ENR tree **Parameters** @@ -1389,16 +1341,18 @@ Returns a list of multiaddress and enrs given a url to a DNS discoverable ENR tr If the function execution takes longer than this value, the execution will be canceled and an error returned. Use `0` for no timeout. +4. `WakuCallBack onOkCb`: callback to be executed if the function is succesful +5. `WakuCallBack onErrCb`: callback to be executed if the function fails **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field contains an array objects describing the multiaddresses, enr and peerID each node found. -An `error` message otherwise. +A status code. Refer to the [`Status codes`](#status-codes) section for possible values. + +If the function is executed succesfully, `onOkCb` will receive an array objects describing the multiaddresses, enr and peerID each node found. + ```json -{ - "result":[ +[ { "peerID":"16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ", "multiaddrs":[ @@ -1408,98 +1362,22 @@ An `error` message otherwise. "enr":"enr:-M-4QCtJKX2WDloRYDT4yjeMGKUCRRcMlsNiZP3cnPO0HZn6IdJ035RPCqsQ5NvTyjqHzKnTM6pc2LoKliV4CeV0WrgBgmlkgnY0gmlwhIbRi9KKbXVsdGlhZGRyc7EALzYobm9kZS0wMS5kby1hbXMzLndha3V2Mi50ZXN0LnN0YXR1c2ltLm5ldAYfQN4DiXNlY3AyNTZrMaEDnr03Tuo77930a7sYLikftxnuG3BbC3gCFhA4632ooDaDdGNwgnZfg3VkcIIjKIV3YWt1Mg8" }, ... -} +] ``` ## DiscoveryV5 -### `extern char* waku_discv5_start()` -Starts the DiscoveryV5 service to discover and connect to new peers - -**Returns** - -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field is set to `true`. - -For example: - -```json -{ - "result": true -} -``` - -### `extern char* waku_discv5_update_bootnodes(char* bootnodes)` +### `extern int waku_discv5_update_bootnodes(char* bootnodes, WakuCallBack onErrCb)` Update the bootnode list used for discovering new peers via DiscoveryV5 **Parameters** 1. `char* bootnodes`: JSON array containing the bootnode ENRs i.e. `["enr:...", "enr:..."]` +2. `WakuCallBack onErrCb`: callback to be executed if the function fails **Returns** -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field is set to `true`. - -For example: - -```json -{ - "result": true -} -``` - - -### `extern char* waku_discv5_stop()` -Stops the DiscoveryV5 service - -**Returns** - -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field is set to `true`. - -For example: - -```json -{ - "result": true -} -``` -## Utils - -### `extern char* waku_utils_base64_encode(char* data)` - -Encode a byte array to base64. -Useful for creating the payload of a Waku Message in the format understood by [`waku_relay_publish`](#extern-char-waku_relay_publishchar-messagejson-char-pubsubtopic-int-timeoutms) - -**Parameters** - -1. `char* data`: Byte array to encode - -**Returns** - -A `char *` containing the base64 encoded byte array. - -### `extern char* waku_utils_base64_decode(char* data)` - -Decode a base64 string (useful for reading the payload from Waku Messages). - -**Parameters** - -1. `char* data`: base64 encoded byte array to decode. - -**Returns** - -A [`JsonResponse`](#jsonresponse-type). -If the execution is successful, the `result` field contains the decoded payload. - - -### `extern void waku_utils_free(char* data)` -Frees a char* since all strings returned by gowaku are allocated in the C heap using malloc. - -**Parameters** - -1. `char* data`: variable to free +A status code. Refer to the [`Status codes`](#status-codes) section for possible values # Copyright diff --git a/library/api.go b/library/c/api.go similarity index 69% rename from library/api.go rename to library/c/api.go index d41c8fc7..93752eaa 100644 --- a/library/api.go +++ b/library/c/api.go @@ -4,15 +4,27 @@ package main /* #include #include + +// The possible returned values for the functions that return int +#define RET_OK 0 +#define RET_ERR 1 +#define RET_MISSING_CALLBACK 2 + +typedef void (*WakuCallBack) (const char* msg, size_t len_0); */ import "C" import ( + "fmt" "unsafe" - mobile "github.com/waku-org/go-waku/mobile" + "github.com/waku-org/go-waku/library" "github.com/waku-org/go-waku/waku/v2/protocol" ) +const retOk = C.RET_OK +const retErr = C.RET_ERR +const retMissingCallback = C.RET_MISSING_CALLBACK + func main() {} // Initialize a waku node. Receives a JSON string containing the configuration @@ -80,110 +92,130 @@ func main() {} // - dns4DomainName: the domain name resolving to the node's public IPv4 address. // //export waku_new -func waku_new(configJSON *C.char) *C.char { - response := mobile.NewNode(C.GoString(configJSON)) - return C.CString(response) +func waku_new(configJSON *C.char, onErrCb C.WakuCallBack) C.int { + err := library.NewNode(C.GoString(configJSON)) + return execErrCB(onErrCb, err) } // Starts the waku node // //export waku_start -func waku_start() *C.char { - response := mobile.Start() - return C.CString(response) +func waku_start(onErrCb C.WakuCallBack) C.int { + err := library.Start() + return execErrCB(onErrCb, err) } // Stops a waku node // //export waku_stop -func waku_stop() *C.char { - response := mobile.Stop() - return C.CString(response) +func waku_stop(onErrCb C.WakuCallBack) C.int { + err := library.Stop() + return execErrCB(onErrCb, err) } // Determine is a node is started or not // //export waku_is_started -func waku_is_started() *C.char { - response := mobile.IsStarted() - return C.CString(response) +func waku_is_started() C.int { + started := library.IsStarted() + if started { + return 1 + } + return 0 +} + +type fn func() (string, error) + +func singleFnExec(f fn, onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + result, err := f() + if err != nil { + return execErrCB(onErrCb, err) + } + return execOkCB(onOkCb, result) } // Obtain the peer ID of the waku node // //export waku_peerid -func waku_peerid() *C.char { - response := mobile.PeerID() - return C.CString(response) +func waku_peerid(onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + return singleFnExec(func() (string, error) { + return library.PeerID() + }, onOkCb, onErrCb) } // Obtain the multiaddresses the wakunode is listening to // //export waku_listen_addresses -func waku_listen_addresses() *C.char { - response := mobile.ListenAddresses() - return C.CString(response) +func waku_listen_addresses(onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + return singleFnExec(func() (string, error) { + return library.ListenAddresses() + }, onOkCb, onErrCb) } // Add node multiaddress and protocol to the wakunode peerstore // //export waku_add_peer -func waku_add_peer(address *C.char, protocolID *C.char) *C.char { - response := mobile.AddPeer(C.GoString(address), C.GoString(protocolID)) - return C.CString(response) +func waku_add_peer(address *C.char, protocolID *C.char, onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + return singleFnExec(func() (string, error) { + return library.AddPeer(C.GoString(address), C.GoString(protocolID)) + }, onOkCb, onErrCb) } // Connect to peer at multiaddress. if ms > 0, cancel the function execution if it takes longer than N milliseconds // //export waku_connect -func waku_connect(address *C.char, ms C.int) *C.char { - response := mobile.Connect(C.GoString(address), int(ms)) - return C.CString(response) +func waku_connect(address *C.char, ms C.int, onErrCb C.WakuCallBack) C.int { + err := library.Connect(C.GoString(address), int(ms)) + return execErrCB(onErrCb, err) } // Connect to known peer by peerID. if ms > 0, cancel the function execution if it takes longer than N milliseconds // //export waku_connect_peerid -func waku_connect_peerid(peerID *C.char, ms C.int) *C.char { - response := mobile.ConnectPeerID(C.GoString(peerID), int(ms)) - return C.CString(response) +func waku_connect_peerid(peerID *C.char, ms C.int, onErrCb C.WakuCallBack) C.int { + err := library.ConnectPeerID(C.GoString(peerID), int(ms)) + return execErrCB(onErrCb, err) } // Close connection to a known peer by peerID // //export waku_disconnect -func waku_disconnect(peerID *C.char) *C.char { - response := mobile.Disconnect(C.GoString(peerID)) - return C.CString(response) +func waku_disconnect(peerID *C.char, onErrCb C.WakuCallBack) C.int { + err := library.Disconnect(C.GoString(peerID)) + return execErrCB(onErrCb, err) } // Get number of connected peers // //export waku_peer_cnt -func waku_peer_cnt() *C.char { - response := mobile.PeerCnt() - return C.CString(response) +func waku_peer_cnt(onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + return singleFnExec(func() (string, error) { + peerCnt, err := library.PeerCnt() + return fmt.Sprintf("%d", peerCnt), err + }, onOkCb, onErrCb) } // Create a content topic string according to RFC 23 // //export waku_content_topic -func waku_content_topic(applicationName *C.char, applicationVersion C.uint, contentTopicName *C.char, encoding *C.char) *C.char { - return C.CString(protocol.NewContentTopic(C.GoString(applicationName), uint(applicationVersion), C.GoString(contentTopicName), C.GoString(encoding)).String()) +func waku_content_topic(applicationName *C.char, applicationVersion C.uint, contentTopicName *C.char, encoding *C.char, onOkCb C.WakuCallBack) C.int { + contentTopic := protocol.NewContentTopic(C.GoString(applicationName), uint(applicationVersion), C.GoString(contentTopicName), C.GoString(encoding)).String() + return execOkCB(onOkCb, contentTopic) } // Create a pubsub topic string according to RFC 23 // //export waku_pubsub_topic -func waku_pubsub_topic(name *C.char, encoding *C.char) *C.char { - return C.CString(mobile.PubsubTopic(C.GoString(name), C.GoString(encoding))) +func waku_pubsub_topic(name *C.char, encoding *C.char, onOkCb C.WakuCallBack) C.int { + topic := library.PubsubTopic(C.GoString(name), C.GoString(encoding)) + return execOkCB(onOkCb, topic) } // Get the default pubsub topic used in waku2: /waku/2/default-waku/proto // //export waku_default_pubsub_topic -func waku_default_pubsub_topic() *C.char { - return C.CString(mobile.DefaultPubsubTopic()) +func waku_default_pubsub_topic(onOkCb C.WakuCallBack) C.int { + return execOkCB(onOkCb, library.DefaultPubsubTopic()) } // Register callback to act as signal handler and receive application signals @@ -191,30 +223,33 @@ func waku_default_pubsub_topic() *C.char { // signature for the callback should be `void myCallback(char* signalJSON)` // //export waku_set_event_callback -func waku_set_event_callback(cb unsafe.Pointer) { - mobile.SetEventCallback(cb) +func waku_set_event_callback(cb C.WakuCallBack) { + library.SetEventCallback(unsafe.Pointer(cb)) } // Retrieve the list of peers known by the waku node // //export waku_peers -func waku_peers() *C.char { - response := mobile.Peers() - return C.CString(response) +func waku_peers(onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + return singleFnExec(func() (string, error) { + return library.Peers() + }, onOkCb, onErrCb) } // Decode a waku message using a 32 bytes symmetric key. The key must be a hex encoded string with "0x" prefix // //export waku_decode_symmetric -func waku_decode_symmetric(messageJSON *C.char, symmetricKey *C.char) *C.char { - response := mobile.DecodeSymmetric(C.GoString(messageJSON), C.GoString(symmetricKey)) - return C.CString(response) +func waku_decode_symmetric(messageJSON *C.char, symmetricKey *C.char, onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + return singleFnExec(func() (string, error) { + return library.DecodeSymmetric(C.GoString(messageJSON), C.GoString(symmetricKey)) + }, onOkCb, onErrCb) } // Decode a waku message using a secp256k1 private key. The key must be a hex encoded string with "0x" prefix // //export waku_decode_asymmetric -func waku_decode_asymmetric(messageJSON *C.char, privateKey *C.char) *C.char { - response := mobile.DecodeAsymmetric(C.GoString(messageJSON), C.GoString(privateKey)) - return C.CString(response) +func waku_decode_asymmetric(messageJSON *C.char, privateKey *C.char, onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + return singleFnExec(func() (string, error) { + return library.DecodeAsymmetric(C.GoString(messageJSON), C.GoString(privateKey)) + }, onOkCb, onErrCb) } diff --git a/library/api_discovery.go b/library/c/api_discovery.go similarity index 62% rename from library/api_discovery.go rename to library/c/api_discovery.go index ddbfe898..31171e98 100644 --- a/library/api_discovery.go +++ b/library/c/api_discovery.go @@ -1,10 +1,11 @@ package main -import ( - "C" +/* +#include +*/ +import "C" - mobile "github.com/waku-org/go-waku/mobile" -) +import "github.com/waku-org/go-waku/library" // Returns a list of objects containing the peerID, enr and multiaddresses for each node found // @@ -16,16 +17,17 @@ import ( // (in milliseconds) is reached, or an error will be returned // //export waku_dns_discovery -func waku_dns_discovery(url *C.char, nameserver *C.char, ms C.int) *C.char { - response := mobile.DnsDiscovery(C.GoString(url), C.GoString(nameserver), int(ms)) - return C.CString(response) +func waku_dns_discovery(url *C.char, nameserver *C.char, ms C.int, onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + return singleFnExec(func() (string, error) { + return library.DNSDiscovery(C.GoString(url), C.GoString(nameserver), int(ms)) + }, onOkCb, onErrCb) } // Update the bootnode list used for discovering new peers via DiscoveryV5 // The bootnodes param should contain a JSON array containing the bootnode ENRs i.e. `["enr:...", "enr:..."]` // //export waku_discv5_update_bootnodes -func waku_discv5_update_bootnodes(bootnodes *C.char) *C.char { - response := mobile.SetBootnodes(C.GoString(bootnodes)) - return C.CString(response) +func waku_discv5_update_bootnodes(bootnodes *C.char, onErrCb C.WakuCallBack) C.int { + err := library.SetBootnodes(C.GoString(bootnodes)) + return execErrCB(onErrCb, err) } diff --git a/library/api_filter.go b/library/c/api_filter.go similarity index 71% rename from library/api_filter.go rename to library/c/api_filter.go index 604d133a..f4279c38 100644 --- a/library/api_filter.go +++ b/library/c/api_filter.go @@ -1,10 +1,10 @@ package main -import ( - "C" - - mobile "github.com/waku-org/go-waku/mobile" -) +/* +#include +*/ +import "C" +import "github.com/waku-org/go-waku/library" // Creates a subscription to a filter full node matching a content filter. // filterJSON must contain a JSON with this format: @@ -20,9 +20,10 @@ import ( // It returns a json object containing the peerID to which we are subscribed to and the details of the subscription // //export waku_filter_subscribe -func waku_filter_subscribe(filterJSON *C.char, peerID *C.char, ms C.int) *C.char { - response := mobile.FilterSubscribe(C.GoString(filterJSON), C.GoString(peerID), int(ms)) - return C.CString(response) +func waku_filter_subscribe(filterJSON *C.char, peerID *C.char, ms C.int, onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + return singleFnExec(func() (string, error) { + return library.FilterSubscribe(C.GoString(filterJSON), C.GoString(peerID), int(ms)) + }, onOkCb, onErrCb) } // Used to know if a service node has an active subscription for this client @@ -31,9 +32,9 @@ func waku_filter_subscribe(filterJSON *C.char, peerID *C.char, ms C.int) *C.char // (in milliseconds) is reached, or an error will be returned // //export waku_filter_ping -func waku_filter_ping(peerID *C.char, ms C.int) *C.char { - response := mobile.FilterPing(C.GoString(peerID), int(ms)) - return C.CString(response) +func waku_filter_ping(peerID *C.char, ms C.int, onErrCb C.WakuCallBack) C.int { + err := library.FilterPing(C.GoString(peerID), int(ms)) + return execErrCB(onErrCb, err) } // Sends a requests to a service node to stop pushing messages matching this filter to this client. @@ -50,9 +51,9 @@ func waku_filter_ping(peerID *C.char, ms C.int) *C.char { // (in milliseconds) is reached, or an error will be returned // //export waku_filter_unsubscribe -func waku_filter_unsubscribe(filterJSON *C.char, peerID *C.char, ms C.int) *C.char { - response := mobile.FilterUnsubscribe(C.GoString(filterJSON), C.GoString(peerID), int(ms)) - return C.CString(response) +func waku_filter_unsubscribe(filterJSON *C.char, peerID *C.char, ms C.int, onErrCb C.WakuCallBack) C.int { + err := library.FilterUnsubscribe(C.GoString(filterJSON), C.GoString(peerID), int(ms)) + return execErrCB(onErrCb, err) } // Sends a requests to a service node (or all service nodes) to stop pushing messages @@ -62,7 +63,8 @@ func waku_filter_unsubscribe(filterJSON *C.char, peerID *C.char, ms C.int) *C.ch // (in milliseconds) is reached, or an error will be returned // //export waku_filter_unsubscribe_all -func waku_filter_unsubscribe_all(peerID *C.char, ms C.int) *C.char { - response := mobile.FilterUnsubscribeAll(C.GoString(peerID), int(ms)) - return C.CString(response) +func waku_filter_unsubscribe_all(peerID *C.char, ms C.int, onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + return singleFnExec(func() (string, error) { + return library.FilterUnsubscribeAll(C.GoString(peerID), int(ms)) + }, onOkCb, onErrCb) } diff --git a/library/api_legacy_filter.go b/library/c/api_legacy_filter.go similarity index 76% rename from library/api_legacy_filter.go rename to library/c/api_legacy_filter.go index 7926ac61..4c0893f3 100644 --- a/library/api_legacy_filter.go +++ b/library/c/api_legacy_filter.go @@ -1,10 +1,10 @@ package main -import ( - "C" - - mobile "github.com/waku-org/go-waku/mobile" -) +/* +#include +*/ +import "C" +import "github.com/waku-org/go-waku/library" // Creates a subscription to a light node matching a content filter and, optionally, a pubSub topic. // filterJSON must contain a JSON with this format: @@ -23,9 +23,9 @@ import ( // (in milliseconds) is reached, or an error will be returned // //export waku_legacy_filter_subscribe -func waku_legacy_filter_subscribe(filterJSON *C.char, peerID *C.char, ms C.int) *C.char { - response := mobile.LegacyFilterSubscribe(C.GoString(filterJSON), C.GoString(peerID), int(ms)) - return C.CString(response) +func waku_legacy_filter_subscribe(filterJSON *C.char, peerID *C.char, ms C.int, onErrCb C.WakuCallBack) C.int { + err := library.LegacyFilterSubscribe(C.GoString(filterJSON), C.GoString(peerID), int(ms)) + return execErrCB(onErrCb, err) } // Removes subscriptions in a light node matching a content filter and, optionally, a pubSub topic. @@ -44,7 +44,7 @@ func waku_legacy_filter_subscribe(filterJSON *C.char, peerID *C.char, ms C.int) // (in milliseconds) is reached, or an error will be returned // //export waku_legacy_filter_unsubscribe -func waku_legacy_filter_unsubscribe(filterJSON *C.char, ms C.int) *C.char { - response := mobile.LegacyFilterUnsubscribe(C.GoString(filterJSON), int(ms)) - return C.CString(response) +func waku_legacy_filter_unsubscribe(filterJSON *C.char, ms C.int, onErrCb C.WakuCallBack) C.int { + err := library.LegacyFilterUnsubscribe(C.GoString(filterJSON), int(ms)) + return execErrCB(onErrCb, err) } diff --git a/library/api_lightpush.go b/library/c/api_lightpush.go similarity index 67% rename from library/api_lightpush.go rename to library/c/api_lightpush.go index b72840a2..b7decc72 100644 --- a/library/api_lightpush.go +++ b/library/c/api_lightpush.go @@ -1,41 +1,47 @@ package main -import ( - "C" +/* +#include +*/ +import "C" +import "github.com/waku-org/go-waku/library" - mobile "github.com/waku-org/go-waku/mobile" -) - -//export waku_lightpush_publish // Publish a message using waku lightpush. Use NULL for topic to use the default pubsub topic.. // peerID should contain the ID of a peer supporting the lightpush protocol. Use NULL to automatically select a node // If ms is greater than 0, the broadcast of the message must happen before the timeout // (in milliseconds) is reached, or an error will be returned -func waku_lightpush_publish(messageJSON *C.char, topic *C.char, peerID *C.char, ms C.int) *C.char { - response := mobile.LightpushPublish(C.GoString(messageJSON), C.GoString(topic), C.GoString(peerID), int(ms)) - return C.CString(response) +// +//export waku_lightpush_publish +func waku_lightpush_publish(messageJSON *C.char, topic *C.char, peerID *C.char, ms C.int, onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + return singleFnExec(func() (string, error) { + return library.LightpushPublish(C.GoString(messageJSON), C.GoString(topic), C.GoString(peerID), int(ms)) + }, onOkCb, onErrCb) } -//export waku_lightpush_publish_enc_asymmetric // Publish a message encrypted with a secp256k1 public key using waku lightpush. Use NULL for topic to use the default pubsub topic. // peerID should contain the ID of a peer supporting the lightpush protocol. Use NULL to automatically select a node // publicKey must be a hex string prefixed with "0x" containing a valid secp256k1 public key. // optionalSigningKey is an optional hex string prefixed with "0x" containing a valid secp256k1 private key for signing the message. Use NULL otherwise // If ms is greater than 0, the broadcast of the message must happen before the timeout // (in milliseconds) is reached, or an error will be returned. -func waku_lightpush_publish_enc_asymmetric(messageJSON *C.char, topic *C.char, peerID *C.char, publicKey *C.char, optionalSigningKey *C.char, ms C.int) *C.char { - response := mobile.LightpushPublishEncodeAsymmetric(C.GoString(messageJSON), C.GoString(topic), C.GoString(peerID), C.GoString(publicKey), C.GoString(optionalSigningKey), int(ms)) - return C.CString(response) +// +//export waku_lightpush_publish_enc_asymmetric +func waku_lightpush_publish_enc_asymmetric(messageJSON *C.char, topic *C.char, peerID *C.char, publicKey *C.char, optionalSigningKey *C.char, ms C.int, onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + return singleFnExec(func() (string, error) { + return library.LightpushPublishEncodeAsymmetric(C.GoString(messageJSON), C.GoString(topic), C.GoString(peerID), C.GoString(publicKey), C.GoString(optionalSigningKey), int(ms)) + }, onOkCb, onErrCb) } -//export waku_lightpush_publish_enc_symmetric // Publish a message encrypted with a 32 bytes symmetric key using waku relay. Use NULL for topic to use the default pubsub topic. // peerID should contain the ID of a peer supporting the lightpush protocol. Use NULL to automatically select a node // symmetricKey must be a hex string prefixed with "0x" containing a 32 bytes symmetric key // optionalSigningKey is an optional hex string prefixed with "0x" containing a valid secp256k1 private key for signing the message. Use NULL otherwise // If ms is greater than 0, the broadcast of the message must happen before the timeout // (in milliseconds) is reached, or an error will be returned. -func waku_lightpush_publish_enc_symmetric(messageJSON *C.char, topic *C.char, peerID *C.char, symmetricKey *C.char, optionalSigningKey *C.char, ms C.int) *C.char { - response := mobile.LightpushPublishEncodeSymmetric(C.GoString(messageJSON), C.GoString(topic), C.GoString(peerID), C.GoString(symmetricKey), C.GoString(optionalSigningKey), int(ms)) - return C.CString(response) +// +//export waku_lightpush_publish_enc_symmetric +func waku_lightpush_publish_enc_symmetric(messageJSON *C.char, topic *C.char, peerID *C.char, symmetricKey *C.char, optionalSigningKey *C.char, ms C.int, onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + return singleFnExec(func() (string, error) { + return library.LightpushPublishEncodeSymmetric(C.GoString(messageJSON), C.GoString(topic), C.GoString(peerID), C.GoString(symmetricKey), C.GoString(optionalSigningKey), int(ms)) + }, onOkCb, onErrCb) } diff --git a/library/api_relay.go b/library/c/api_relay.go similarity index 56% rename from library/api_relay.go rename to library/c/api_relay.go index d080b863..2d217322 100644 --- a/library/api_relay.go +++ b/library/c/api_relay.go @@ -1,18 +1,23 @@ package main -import ( - "C" - - mobile "github.com/waku-org/go-waku/mobile" -) +/* +#include +*/ +import "C" +import "github.com/waku-org/go-waku/library" // Determine if there are enough peers to publish a message on a topic. Use NULL // to verify the number of peers in the default pubsub topic // //export waku_relay_enough_peers -func waku_relay_enough_peers(topic *C.char) *C.char { - response := mobile.RelayEnoughPeers(C.GoString(topic)) - return C.CString(response) +func waku_relay_enough_peers(topic *C.char, onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + return singleFnExec(func() (string, error) { + result, err := library.RelayEnoughPeers(C.GoString(topic)) + if result { + return "true", err + } + return "false", err + }, onOkCb, onErrCb) } // Publish a message using waku relay and returns the message ID. Use NULL for topic to use the default pubsub topic @@ -20,33 +25,36 @@ func waku_relay_enough_peers(topic *C.char) *C.char { // (in milliseconds) is reached, or an error will be returned. // //export waku_relay_publish -func waku_relay_publish(messageJSON *C.char, topic *C.char, ms C.int) *C.char { - response := mobile.RelayPublish(C.GoString(messageJSON), C.GoString(topic), int(ms)) - return C.CString(response) +func waku_relay_publish(messageJSON *C.char, topic *C.char, ms C.int, onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + return singleFnExec(func() (string, error) { + return library.RelayPublish(C.GoString(messageJSON), C.GoString(topic), int(ms)) + }, onOkCb, onErrCb) } -// Publish a message encrypted with a secp256k1 public key using waku relay and returns the message ID. Use NULL for topic to use the default pubsub topic. +// Publish a message encrypted with a secp256k1 public key using waku relay and returns the message ID. Use NULL for topic to use the default pubsub topic. // publicKey must be a hex string prefixed with "0x" containing a valid secp256k1 public key. // optionalSigningKey is an optional hex string prefixed with "0x" containing a valid secp256k1 private key for signing the message. Use NULL otherwise // If ms is greater than 0, the broadcast of the message must happen before the timeout // (in milliseconds) is reached, or an error will be returned. // //export waku_relay_publish_enc_asymmetric -func waku_relay_publish_enc_asymmetric(messageJSON *C.char, topic *C.char, publicKey *C.char, optionalSigningKey *C.char, ms C.int) *C.char { - response := mobile.RelayPublishEncodeAsymmetric(C.GoString(messageJSON), C.GoString(topic), C.GoString(publicKey), C.GoString(optionalSigningKey), int(ms)) - return C.CString(response) +func waku_relay_publish_enc_asymmetric(messageJSON *C.char, topic *C.char, publicKey *C.char, optionalSigningKey *C.char, ms C.int, onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + return singleFnExec(func() (string, error) { + return library.RelayPublishEncodeAsymmetric(C.GoString(messageJSON), C.GoString(topic), C.GoString(publicKey), C.GoString(optionalSigningKey), int(ms)) + }, onOkCb, onErrCb) } -// Publish a message encrypted with a 32 bytes symmetric key using waku relay and returns the message ID. Use NULL for topic to use the default pubsub topic. +// Publish a message encrypted with a 32 bytes symmetric key using waku relay and returns the message ID. Use NULL for topic to use the default pubsub topic. // symmetricKey must be a hex string prefixed with "0x" containing a 32 bytes symmetric key // optionalSigningKey is an optional hex string prefixed with "0x" containing a valid secp256k1 private key for signing the message. Use NULL otherwise // If ms is greater than 0, the broadcast of the message must happen before the timeout // (in milliseconds) is reached, or an error will be returned. // //export waku_relay_publish_enc_symmetric -func waku_relay_publish_enc_symmetric(messageJSON *C.char, topic *C.char, symmetricKey *C.char, optionalSigningKey *C.char, ms C.int) *C.char { - response := mobile.RelayPublishEncodeSymmetric(C.GoString(messageJSON), C.GoString(topic), C.GoString(symmetricKey), C.GoString(optionalSigningKey), int(ms)) - return C.CString(response) +func waku_relay_publish_enc_symmetric(messageJSON *C.char, topic *C.char, symmetricKey *C.char, optionalSigningKey *C.char, ms C.int, onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + return singleFnExec(func() (string, error) { + return library.RelayPublishEncodeSymmetric(C.GoString(messageJSON), C.GoString(topic), C.GoString(symmetricKey), C.GoString(optionalSigningKey), int(ms)) + }, onOkCb, onErrCb) } // Subscribe to a WakuRelay topic. Set the topic to NULL to subscribe @@ -55,24 +63,25 @@ func waku_relay_publish_enc_symmetric(messageJSON *C.char, topic *C.char, symmet // the message was received // //export waku_relay_subscribe -func waku_relay_subscribe(topic *C.char) *C.char { - response := mobile.RelaySubscribe(C.GoString(topic)) - return C.CString(response) +func waku_relay_subscribe(topic *C.char, onErrCb C.WakuCallBack) C.int { + err := library.RelaySubscribe(C.GoString(topic)) + return execErrCB(onErrCb, err) } // Returns a json response with the list of pubsub topics the node // is subscribed to in WakuRelay // //export waku_relay_topics -func waku_relay_topics() *C.char { - response := mobile.RelayTopics() - return C.CString(response) +func waku_relay_topics(onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + return singleFnExec(func() (string, error) { + return library.RelayTopics() + }, onOkCb, onErrCb) } // Closes the pubsub subscription to a pubsub topic // //export waku_relay_unsubscribe -func waku_relay_unsubscribe(topic *C.char) *C.char { - response := mobile.RelayUnsubscribe(C.GoString(topic)) - return C.CString(response) +func waku_relay_unsubscribe(topic *C.char, onErrCb C.WakuCallBack) C.int { + err := library.RelayUnsubscribe(C.GoString(topic)) + return execErrCB(onErrCb, err) } diff --git a/library/api_store.go b/library/c/api_store.go similarity index 81% rename from library/api_store.go rename to library/c/api_store.go index 73715f86..7cf83cc4 100644 --- a/library/api_store.go +++ b/library/c/api_store.go @@ -1,10 +1,10 @@ package main -import ( - "C" - - mobile "github.com/waku-org/go-waku/mobile" -) +/* +#include +*/ +import "C" +import "github.com/waku-org/go-waku/library" // Query historic messages using waku store protocol. // queryJSON must contain a valid json string with the following format: @@ -36,9 +36,10 @@ import ( // (in milliseconds) is reached, or an error will be returned // //export waku_store_query -func waku_store_query(queryJSON *C.char, peerID *C.char, ms C.int) *C.char { - response := mobile.StoreQuery(C.GoString(queryJSON), C.GoString(peerID), int(ms)) - return C.CString(response) +func waku_store_query(queryJSON *C.char, peerID *C.char, ms C.int, onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + return singleFnExec(func() (string, error) { + return library.StoreQuery(C.GoString(queryJSON), C.GoString(peerID), int(ms)) + }, onOkCb, onErrCb) } // Query historic messages stored in the localDB using waku store protocol. @@ -69,7 +70,8 @@ func waku_store_query(queryJSON *C.char, peerID *C.char, ms C.int) *C.char { // Requires the `store` option to be passed when setting up the initial configuration // //export waku_store_local_query -func waku_store_local_query(queryJSON *C.char) *C.char { - response := mobile.StoreLocalQuery(C.GoString(queryJSON)) - return C.CString(response) +func waku_store_local_query(queryJSON *C.char, onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int { + return singleFnExec(func() (string, error) { + return library.StoreLocalQuery(C.GoString(queryJSON)) + }, onOkCb, onErrCb) } diff --git a/library/c/cgo_utils.c b/library/c/cgo_utils.c new file mode 100644 index 00000000..defe95f1 --- /dev/null +++ b/library/c/cgo_utils.c @@ -0,0 +1,9 @@ +#include +#include +#include + +// This is a bridge function to execute C callbacks. +// It's used internally in go-waku. Do not call directly +void _waku_execCB(WakuCallBack op, char* a, size_t b) { + op(a, b); +} diff --git a/library/c/cgo_utils.go b/library/c/cgo_utils.go new file mode 100644 index 00000000..7719fab5 --- /dev/null +++ b/library/c/cgo_utils.go @@ -0,0 +1,37 @@ +package main + +/* +#include +#include +extern void _waku_execCB(WakuCallBack op, char* a, size_t b); +*/ +import "C" +import "unsafe" + +func execOkCB(onOkCb C.WakuCallBack, value string) C.int { + if onOkCb == nil { + return retMissingCallback + } + + val := C.CString(value) + len := C.size_t(len(value)) + C._waku_execCB(onOkCb, val, len) + + C.free(unsafe.Pointer(val)) + + return retOk +} + +func execErrCB(onErrCb C.WakuCallBack, err error) C.int { + if onErrCb == nil { + return retMissingCallback + } + + if err != nil { + errMsg := err.Error() + execOkCB(onErrCb, errMsg) // reusing ok cb + return retErr + } + + return retOk +} diff --git a/library/c/cgo_utils.h b/library/c/cgo_utils.h new file mode 100644 index 00000000..47582ed2 --- /dev/null +++ b/library/c/cgo_utils.h @@ -0,0 +1,6 @@ +#include +#include + +typedef void (*WakuCallBack) (const char* msg, size_t len_0); + +typedef void (*BytesCallBack) (uint8_t* data, size_t len_0); \ No newline at end of file diff --git a/mobile/config.go b/library/config.go similarity index 99% rename from mobile/config.go rename to library/config.go index 130e4599..cce1a108 100644 --- a/mobile/config.go +++ b/library/config.go @@ -1,4 +1,4 @@ -package gowaku +package library import ( "encoding/json" @@ -308,7 +308,7 @@ func getSeenTTL(cfg WakuConfig) time.Duration { return time.Duration(*cfg.GossipSubParams.SeenMessagesTTLSeconds) } -func GetGossipSubParams(cfg *GossipSubParams) pubsub.GossipSubParams { +func getGossipSubParams(cfg *GossipSubParams) pubsub.GossipSubParams { params := pubsub.DefaultGossipSubParams() if cfg.D != nil { diff --git a/mobile/api_discovery.go b/library/discovery.go similarity index 61% rename from mobile/api_discovery.go rename to library/discovery.go index 50f39745..645cbb87 100644 --- a/mobile/api_discovery.go +++ b/library/discovery.go @@ -1,4 +1,4 @@ -package gowaku +package library import ( "context" @@ -10,13 +10,14 @@ import ( "github.com/waku-org/go-waku/waku/v2/dnsdisc" ) -type DnsDiscoveryItem struct { +type dnsDiscoveryItem struct { PeerID string `json:"peerID"` Addresses []string `json:"multiaddrs"` ENR string `json:"enr,omitempty"` } -func DnsDiscovery(url string, nameserver string, ms int) string { +// DNSDiscovery executes dns discovery on an url and returns a list of nodes +func DNSDiscovery(url string, nameserver string, ms int) (string, error) { var ctx context.Context var cancel context.CancelFunc @@ -34,12 +35,12 @@ func DnsDiscovery(url string, nameserver string, ms int) string { nodes, err := dnsdisc.RetrieveNodes(ctx, url, dnsDiscOpt...) if err != nil { - return MakeJSONResponse(err) + return "", err } - var response []DnsDiscoveryItem + var response []dnsDiscoveryItem for _, n := range nodes { - item := DnsDiscoveryItem{ + item := dnsDiscoveryItem{ PeerID: n.PeerID.String(), } for _, addr := range n.PeerInfo.Addrs { @@ -53,53 +54,51 @@ func DnsDiscovery(url string, nameserver string, ms int) string { response = append(response, item) } - return PrepareJSONResponse(response, nil) + return marshalJSON(response) } -func StartDiscoveryV5() string { +// StartDiscoveryV5 starts discv5 discovery +func StartDiscoveryV5() error { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return errWakuNodeNotReady } if wakuState.node.DiscV5() == nil { - return MakeJSONResponse(errors.New("DiscV5 is not mounted")) + return errors.New("DiscV5 is not mounted") } - err := wakuState.node.DiscV5().Start(context.Background()) - if err != nil { - return MakeJSONResponse(err) - } - - return MakeJSONResponse(nil) + return wakuState.node.DiscV5().Start(context.Background()) } -func StopDiscoveryV5() string { +// StopDiscoveryV5 stops discv5 discovery +func StopDiscoveryV5() error { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return errWakuNodeNotReady } if wakuState.node.DiscV5() == nil { - return MakeJSONResponse(errors.New("DiscV5 is not mounted")) + return errors.New("DiscV5 is not mounted") } wakuState.node.DiscV5().Stop() - return MakeJSONResponse(nil) + return nil } -func SetBootnodes(bootnodes string) string { +// SetBootnodes is used to update the bootnodes receiving a JSON array of ENRs +func SetBootnodes(bootnodes string) error { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return errWakuNodeNotReady } if wakuState.node.DiscV5() == nil { - return MakeJSONResponse(errors.New("DiscV5 is not mounted")) + return errors.New("DiscV5 is not mounted") } var tmp []json.RawMessage if err := json.Unmarshal([]byte(bootnodes), &tmp); err != nil { - return MakeJSONResponse(err) + return err } var enrList []string for _, el := range tmp { var enr string if err := json.Unmarshal(el, &enr); err != nil { - return MakeJSONResponse(err) + return err } enrList = append(enrList, enr) } @@ -108,11 +107,10 @@ func SetBootnodes(bootnodes string) string { for _, addr := range enrList { node, err := enode.Parse(enode.ValidSchemes, addr) if err != nil { - return MakeJSONResponse(err) + return err } nodes = append(nodes, node) } - err := wakuState.node.DiscV5().SetBootnodes(nodes) - return MakeJSONResponse(err) + return wakuState.node.DiscV5().SetBootnodes(nodes) } diff --git a/mobile/encoding.go b/library/encoding.go similarity index 99% rename from mobile/encoding.go rename to library/encoding.go index 528d27ad..90318466 100644 --- a/mobile/encoding.go +++ b/library/encoding.go @@ -1,4 +1,4 @@ -package gowaku +package library import ( "encoding/json" diff --git a/mobile/api_filter.go b/library/filter.go similarity index 74% rename from mobile/api_filter.go rename to library/filter.go index c69701c1..e78d6a3d 100644 --- a/mobile/api_filter.go +++ b/library/filter.go @@ -1,4 +1,4 @@ -package gowaku +package library import ( "context" @@ -10,13 +10,13 @@ import ( "github.com/waku-org/go-waku/waku/v2/protocol/filter" ) -type FilterArgument struct { +type filterArgument struct { Topic string `json:"pubsubTopic,omitempty"` ContentTopics []string `json:"contentTopics,omitempty"` } func toContentFilter(filterJSON string) (filter.ContentFilter, error) { - var f FilterArgument + var f filterArgument err := json.Unmarshal([]byte(filterJSON), &f) if err != nil { return filter.ContentFilter{}, err @@ -28,14 +28,15 @@ func toContentFilter(filterJSON string) (filter.ContentFilter, error) { }, nil } -func FilterSubscribe(filterJSON string, peerID string, ms int) string { +// FilterSubscribe is used to create a subscription to a filter node to receive messages +func FilterSubscribe(filterJSON string, peerID string, ms int) (string, error) { cf, err := toContentFilter(filterJSON) if err != nil { - return MakeJSONResponse(err) + return "", err } if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return "", errWakuNodeNotReady } var ctx context.Context @@ -52,7 +53,7 @@ func FilterSubscribe(filterJSON string, peerID string, ms int) string { if peerID != "" { p, err := peer.Decode(peerID) if err != nil { - return MakeJSONResponse(err) + return "", err } fOptions = append(fOptions, filter.WithPeer(p)) } else { @@ -61,7 +62,7 @@ func FilterSubscribe(filterJSON string, peerID string, ms int) string { subscriptionDetails, err := wakuState.node.FilterLightnode().Subscribe(ctx, cf, fOptions...) if err != nil { - return MakeJSONResponse(err) + return "", err } go func(subscriptionDetails *filter.SubscriptionDetails) { @@ -70,12 +71,13 @@ func FilterSubscribe(filterJSON string, peerID string, ms int) string { } }(subscriptionDetails) - return PrepareJSONResponse(subscriptionDetails, nil) + return marshalJSON(subscriptionDetails) } -func FilterPing(peerID string, ms int) string { +// FilterPing is used to determine if a peer has an active subscription +func FilterPing(peerID string, ms int) error { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return errWakuNodeNotReady } var ctx context.Context @@ -93,25 +95,24 @@ func FilterPing(peerID string, ms int) string { if peerID != "" { pID, err = peer.Decode(peerID) if err != nil { - return MakeJSONResponse(err) + return err } } else { - return MakeJSONResponse(errors.New("peerID is required")) + return errors.New("peerID is required") } - err = wakuState.node.FilterLightnode().Ping(ctx, pID) - - return MakeJSONResponse(err) + return wakuState.node.FilterLightnode().Ping(ctx, pID) } -func FilterUnsubscribe(filterJSON string, peerID string, ms int) string { +// FilterUnsubscribe is used to remove a filter criteria from an active subscription with a filter node +func FilterUnsubscribe(filterJSON string, peerID string, ms int) error { cf, err := toContentFilter(filterJSON) if err != nil { - return MakeJSONResponse(err) + return err } if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return errWakuNodeNotReady } var ctx context.Context @@ -128,21 +129,21 @@ func FilterUnsubscribe(filterJSON string, peerID string, ms int) string { if peerID != "" { p, err := peer.Decode(peerID) if err != nil { - return MakeJSONResponse(err) + return err } fOptions = append(fOptions, filter.Peer(p)) } else { - return MakeJSONResponse(errors.New("peerID is required")) + return errors.New("peerID is required") } pushResult, err := wakuState.node.FilterLightnode().Unsubscribe(ctx, cf, fOptions...) if err != nil { - return MakeJSONResponse(err) + return err } result := <-pushResult - return MakeJSONResponse(result.Err) + return result.Err } type unsubscribeAllResult struct { @@ -150,9 +151,10 @@ type unsubscribeAllResult struct { Error string `json:"error"` } -func FilterUnsubscribeAll(peerID string, ms int) string { +// FilterUnsubscribeAll is used to remove an active subscription to a peer. If no peerID is defined, it will stop all active filter subscriptions +func FilterUnsubscribeAll(peerID string, ms int) (string, error) { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return "", errWakuNodeNotReady } var ctx context.Context @@ -169,7 +171,7 @@ func FilterUnsubscribeAll(peerID string, ms int) string { if peerID != "" { p, err := peer.Decode(peerID) if err != nil { - return MakeJSONResponse(err) + return "", err } fOptions = append(fOptions, filter.Peer(p)) } else { @@ -178,7 +180,7 @@ func FilterUnsubscribeAll(peerID string, ms int) string { pushResult, err := wakuState.node.FilterLightnode().UnsubscribeAll(ctx, fOptions...) if err != nil { - return MakeJSONResponse(err) + return "", err } var unsubscribeResult []unsubscribeAllResult @@ -193,5 +195,5 @@ func FilterUnsubscribeAll(peerID string, ms int) string { unsubscribeResult = append(unsubscribeResult, ur) } - return PrepareJSONResponse(unsubscribeResult, nil) + return marshalJSON(unsubscribeResult) } diff --git a/mobile/ios.go b/library/ios.go similarity index 68% rename from mobile/ios.go rename to library/ios.go index f1a61e71..2a2ed23f 100644 --- a/mobile/ios.go +++ b/library/ios.go @@ -1,13 +1,13 @@ //go:build darwin && cgo // +build darwin,cgo -package gowaku +package library /* #cgo CFLAGS: -x objective-c #cgo LDFLAGS: -framework Foundation #include #include -extern bool StatusServiceSignalEvent( const char *jsonEvent ); +extern bool ServiceSignalEvent( const char *jsonEvent ); */ import "C" diff --git a/mobile/api_legacy_filter.go b/library/legacy_filter.go similarity index 79% rename from mobile/api_legacy_filter.go rename to library/legacy_filter.go index dd3f227c..e60c4993 100644 --- a/mobile/api_legacy_filter.go +++ b/library/legacy_filter.go @@ -1,4 +1,4 @@ -package gowaku +package library import ( "context" @@ -10,13 +10,13 @@ import ( "github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/pb" ) -type LegacyFilterArgument struct { +type legacyFilterArgument struct { Topic string `json:"pubsubTopic,omitempty"` ContentFilters []*pb.FilterRequest_ContentFilter `json:"contentFilters,omitempty"` } func toLegacyContentFilter(filterJSON string) (legacy_filter.ContentFilter, error) { - var f LegacyFilterArgument + var f legacyFilterArgument err := json.Unmarshal([]byte(filterJSON), &f) if err != nil { return legacy_filter.ContentFilter{}, err @@ -32,15 +32,16 @@ func toLegacyContentFilter(filterJSON string) (legacy_filter.ContentFilter, erro return result, err } +// LegacyFilterSubscribe is used to create a subscription to a filter node to receive messages // Deprecated: Use FilterSubscribe instead -func LegacyFilterSubscribe(filterJSON string, peerID string, ms int) string { +func LegacyFilterSubscribe(filterJSON string, peerID string, ms int) error { cf, err := toLegacyContentFilter(filterJSON) if err != nil { - return MakeJSONResponse(err) + return err } if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return errWakuNodeNotReady } var ctx context.Context @@ -57,7 +58,7 @@ func LegacyFilterSubscribe(filterJSON string, peerID string, ms int) string { if peerID != "" { p, err := peer.Decode(peerID) if err != nil { - return MakeJSONResponse(err) + return err } fOptions = append(fOptions, legacy_filter.WithPeer(p)) } else { @@ -66,7 +67,7 @@ func LegacyFilterSubscribe(filterJSON string, peerID string, ms int) string { _, f, err := wakuState.node.LegacyFilter().Subscribe(ctx, cf, fOptions...) if err != nil { - return MakeJSONResponse(err) + return err } go func(f legacy_filter.Filter) { @@ -75,18 +76,19 @@ func LegacyFilterSubscribe(filterJSON string, peerID string, ms int) string { } }(f) - return PrepareJSONResponse(true, nil) + return nil } +// LegacyFilterUnsubscribe is used to remove a filter criteria from an active subscription with a filter node // Deprecated: Use FilterUnsubscribe or FilterUnsubscribeAll instead -func LegacyFilterUnsubscribe(filterJSON string, ms int) string { +func LegacyFilterUnsubscribe(filterJSON string, ms int) error { cf, err := toLegacyContentFilter(filterJSON) if err != nil { - return MakeJSONResponse(err) + return err } if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return errWakuNodeNotReady } var ctx context.Context @@ -99,10 +101,5 @@ func LegacyFilterUnsubscribe(filterJSON string, ms int) string { ctx = context.Background() } - err = wakuState.node.LegacyFilter().UnsubscribeFilter(ctx, cf) - if err != nil { - return MakeJSONResponse(err) - } - - return MakeJSONResponse(nil) + return wakuState.node.LegacyFilter().UnsubscribeFilter(ctx, cf) } diff --git a/mobile/api_lightpush.go b/library/lightpush.go similarity index 68% rename from mobile/api_lightpush.go rename to library/lightpush.go index 13cc1eef..3713067e 100644 --- a/mobile/api_lightpush.go +++ b/library/lightpush.go @@ -1,4 +1,4 @@ -package gowaku +package library import ( "context" @@ -41,34 +41,32 @@ func lightpushPublish(msg *pb.WakuMessage, pubsubTopic string, peerID string, ms return hexutil.Encode(hash), err } -func LightpushPublish(messageJSON string, topic string, peerID string, ms int) string { +// LightpushPublish is used to publish a WakuMessage in a pubsub topic using Lightpush protocol +func LightpushPublish(messageJSON string, topic string, peerID string, ms int) (string, error) { msg, err := wakuMessage(messageJSON) if err != nil { - return MakeJSONResponse(err) + return "", err } - hash, err := lightpushPublish(msg, getTopic(topic), peerID, ms) - return PrepareJSONResponse(hash, err) + return lightpushPublish(msg, getTopic(topic), peerID, ms) } -func LightpushPublishEncodeAsymmetric(messageJSON string, topic string, peerID string, publicKey string, optionalSigningKey string, ms int) string { +// LightpushPublishEncodeAsymmetric is used to publish a WakuMessage in a pubsub topic using Lightpush protocol, and encrypting the message with some public key +func LightpushPublishEncodeAsymmetric(messageJSON string, topic string, peerID string, publicKey string, optionalSigningKey string, ms int) (string, error) { msg, err := wakuMessageAsymmetricEncoding(messageJSON, publicKey, optionalSigningKey) if err != nil { - return MakeJSONResponse(err) + return "", err } - hash, err := lightpushPublish(msg, getTopic(topic), peerID, ms) - - return PrepareJSONResponse(hash, err) + return lightpushPublish(msg, getTopic(topic), peerID, ms) } -func LightpushPublishEncodeSymmetric(messageJSON string, topic string, peerID string, symmetricKey string, optionalSigningKey string, ms int) string { +// LightpushPublishEncodeSymmetric is used to publish a WakuMessage in a pubsub topic using Lightpush protocol, and encrypting the message with a symmetric key +func LightpushPublishEncodeSymmetric(messageJSON string, topic string, peerID string, symmetricKey string, optionalSigningKey string, ms int) (string, error) { msg, err := wakuMessageSymmetricEncoding(messageJSON, symmetricKey, optionalSigningKey) if err != nil { - return MakeJSONResponse(err) + return "", err } - hash, err := lightpushPublish(msg, getTopic(topic), peerID, ms) - - return PrepareJSONResponse(hash, err) + return lightpushPublish(msg, getTopic(topic), peerID, ms) } diff --git a/mobile/README.md b/library/mobile/README.md similarity index 100% rename from mobile/README.md rename to library/mobile/README.md diff --git a/library/mobile/api.go b/library/mobile/api.go new file mode 100644 index 00000000..28eb723c --- /dev/null +++ b/library/mobile/api.go @@ -0,0 +1,106 @@ +// Package gowaku implements gomobile bindings for go-waku. Contains a set of functions that +// are exported when go-waku is exported as libraries for mobile devices +package gowaku + +import ( + "github.com/waku-org/go-waku/library" + "github.com/waku-org/go-waku/waku/v2/protocol" +) + +// NewNode initializes a waku node. Receives a JSON string containing the configuration, and use default values for those config items not specified +func NewNode(configJSON string) string { + err := library.NewNode(configJSON) + return makeJSONResponse(err) +} + +// Start starts the waku node +func Start() string { + err := library.Start() + return makeJSONResponse(err) +} + +// Stop stops a waku node +func Stop() string { + err := library.Stop() + return makeJSONResponse(err) +} + +// IsStarted is used to determine is a node is started or not +func IsStarted() string { + return prepareJSONResponse(library.IsStarted(), nil) +} + +// PeerID is used to obtain the peer ID of the waku node +func PeerID() string { + peerID, err := library.PeerID() + return prepareJSONResponse(peerID, err) +} + +// ListenAddresses returns the multiaddresses the wakunode is listening to +func ListenAddresses() string { + addresses, err := library.ListenAddresses() + return prepareJSONResponse(addresses, err) +} + +// AddPeer adds a node multiaddress and protocol to the wakunode peerstore +func AddPeer(address string, protocolID string) string { + peerID, err := library.AddPeer(address, protocolID) + return prepareJSONResponse(peerID, err) +} + +// Connect is used to connect to a peer at multiaddress. if ms > 0, cancel the function execution if it takes longer than N milliseconds +func Connect(address string, ms int) string { + err := library.Connect(address, ms) + return makeJSONResponse(err) +} + +// ConnectPeerID is usedd to connect to a known peer by peerID. if ms > 0, cancel the function execution if it takes longer than N milliseconds +func ConnectPeerID(peerID string, ms int) string { + err := library.ConnectPeerID(peerID, ms) + return makeJSONResponse(err) +} + +// Disconnect closes a connection to a known peer by peerID +func Disconnect(peerID string) string { + err := library.Disconnect(peerID) + return makeJSONResponse(err) +} + +// PeerCnt returns the number of connected peers +func PeerCnt() string { + peerCnt, err := library.PeerCnt() + return prepareJSONResponse(peerCnt, err) +} + +// ContentTopic creates a content topic string according to RFC 23 +func ContentTopic(applicationName string, applicationVersion int, contentTopicName string, encoding string) string { + return protocol.NewContentTopic(applicationName, uint(applicationVersion), contentTopicName, encoding).String() +} + +// PubsubTopic creates a pubsub topic string according to RFC 23 +func PubsubTopic(name string, encoding string) string { + return protocol.NewNamedShardingPubsubTopic(name + "/" + encoding).String() +} + +// DefaultPubsubTopic returns the default pubsub topic used in waku2: /waku/2/default-waku/proto +func DefaultPubsubTopic() string { + return protocol.DefaultPubsubTopic().String() +} + +// Peers retrieves the list of peers known by the waku node +func Peers() string { + peers, err := library.Peers() + return prepareJSONResponse(peers, err) +} + +// DecodeSymmetric decodes a waku message using a 32 bytes symmetric key. The key must be a hex encoded string with "0x" prefix +func DecodeSymmetric(messageJSON string, symmetricKey string) string { + response, err := library.DecodeSymmetric(messageJSON, symmetricKey) + return prepareJSONResponse(response, err) +} + +// DecodeAsymmetric decodes a waku message using a secp256k1 private key. The key must be a hex encoded string with "0x" prefix +func DecodeAsymmetric(messageJSON string, privateKey string) string { + response, err := library.DecodeAsymmetric(messageJSON, privateKey) + return prepareJSONResponse(response, err) +} diff --git a/library/mobile/api_discovery.go b/library/mobile/api_discovery.go new file mode 100644 index 00000000..679803af --- /dev/null +++ b/library/mobile/api_discovery.go @@ -0,0 +1,29 @@ +package gowaku + +import ( + "github.com/waku-org/go-waku/library" +) + +// DNSDiscovery executes dns discovery on an url and returns a list of nodes +func DNSDiscovery(url string, nameserver string, ms int) string { + response, err := library.DNSDiscovery(url, nameserver, ms) + return prepareJSONResponse(response, err) +} + +// StartDiscoveryV5 starts discv5 discovery +func StartDiscoveryV5() string { + err := library.StartDiscoveryV5() + return makeJSONResponse(err) +} + +// StopDiscoveryV5 stops discv5 discovery +func StopDiscoveryV5() string { + err := library.StopDiscoveryV5() + return makeJSONResponse(err) +} + +// SetBootnodes is used to update the bootnodes receiving a JSON array of ENRs +func SetBootnodes(bootnodes string) string { + err := library.SetBootnodes(bootnodes) + return makeJSONResponse(err) +} diff --git a/library/mobile/api_filter.go b/library/mobile/api_filter.go new file mode 100644 index 00000000..28a29701 --- /dev/null +++ b/library/mobile/api_filter.go @@ -0,0 +1,29 @@ +package gowaku + +import ( + "github.com/waku-org/go-waku/library" +) + +// FilterSubscribe is used to create a subscription to a filter node to receive messages +func FilterSubscribe(filterJSON string, peerID string, ms int) string { + response, err := library.FilterSubscribe(filterJSON, peerID, ms) + return prepareJSONResponse(response, err) +} + +// FilterPing is used to determine if a peer has an active subscription +func FilterPing(peerID string, ms int) string { + err := library.FilterPing(peerID, ms) + return makeJSONResponse(err) +} + +// FilterUnsubscribe is used to remove a filter criteria from an active subscription with a filter node +func FilterUnsubscribe(filterJSON string, peerID string, ms int) string { + err := library.FilterUnsubscribe(filterJSON, peerID, ms) + return makeJSONResponse(err) +} + +// FilterUnsubscribeAll is used to remove an active subscription to a peer. If no peerID is defined, it will stop all active filter subscriptions +func FilterUnsubscribeAll(peerID string, ms int) string { + response, err := library.FilterUnsubscribeAll(peerID, ms) + return prepareJSONResponse(response, err) +} diff --git a/library/mobile/api_legacy_filter.go b/library/mobile/api_legacy_filter.go new file mode 100644 index 00000000..ab154f0e --- /dev/null +++ b/library/mobile/api_legacy_filter.go @@ -0,0 +1,19 @@ +package gowaku + +import ( + "github.com/waku-org/go-waku/library" +) + +// LegacyFilterSubscribe is used to create a subscription to a filter node to receive messages +// Deprecated: Use FilterSubscribe instead +func LegacyFilterSubscribe(filterJSON string, peerID string, ms int) string { + err := library.LegacyFilterSubscribe(filterJSON, peerID, ms) + return makeJSONResponse(err) +} + +// LegacyFilterUnsubscribe is used to remove a filter criteria from an active subscription with a filter node +// Deprecated: Use FilterUnsubscribe or FilterUnsubscribeAll instead +func LegacyFilterUnsubscribe(filterJSON string, ms int) string { + err := library.LegacyFilterUnsubscribe(filterJSON, ms) + return makeJSONResponse(err) +} diff --git a/library/mobile/api_lightpush.go b/library/mobile/api_lightpush.go new file mode 100644 index 00000000..4d5d2532 --- /dev/null +++ b/library/mobile/api_lightpush.go @@ -0,0 +1,21 @@ +package gowaku + +import "github.com/waku-org/go-waku/library" + +// LightpushPublish is used to publish a WakuMessage in a pubsub topic using Lightpush protocol +func LightpushPublish(messageJSON string, topic string, peerID string, ms int) string { + response, err := library.LightpushPublish(messageJSON, topic, peerID, ms) + return prepareJSONResponse(response, err) +} + +// LightpushPublishEncodeAsymmetric is used to publish a WakuMessage in a pubsub topic using Lightpush protocol, and encrypting the message with some public key +func LightpushPublishEncodeAsymmetric(messageJSON string, topic string, peerID string, publicKey string, optionalSigningKey string, ms int) string { + response, err := library.LightpushPublishEncodeAsymmetric(messageJSON, topic, peerID, publicKey, optionalSigningKey, ms) + return prepareJSONResponse(response, err) +} + +// LightpushPublishEncodeSymmetric is used to publish a WakuMessage in a pubsub topic using Lightpush protocol, and encrypting the message with a symmetric key +func LightpushPublishEncodeSymmetric(messageJSON string, topic string, peerID string, symmetricKey string, optionalSigningKey string, ms int) string { + response, err := library.LightpushPublishEncodeSymmetric(messageJSON, topic, peerID, symmetricKey, optionalSigningKey, ms) + return prepareJSONResponse(response, err) +} diff --git a/library/mobile/api_relay.go b/library/mobile/api_relay.go new file mode 100644 index 00000000..2050c7b7 --- /dev/null +++ b/library/mobile/api_relay.go @@ -0,0 +1,47 @@ +package gowaku + +import ( + "github.com/waku-org/go-waku/library" +) + +// RelayEnoughPeers determines if there are enough peers to publish a message on a topic +func RelayEnoughPeers(topic string) string { + response, err := library.RelayEnoughPeers(topic) + return prepareJSONResponse(response, err) +} + +// RelayPublish publishes a message using waku relay and returns the message ID +func RelayPublish(messageJSON string, topic string, ms int) string { + hash, err := library.RelayPublish(messageJSON, topic, ms) + return prepareJSONResponse(hash, err) +} + +// RelayPublishEncodeAsymmetric publish a message encrypted with a secp256k1 public key using waku relay and returns the message ID +func RelayPublishEncodeAsymmetric(messageJSON string, topic string, publicKey string, optionalSigningKey string, ms int) string { + hash, err := library.RelayPublishEncodeAsymmetric(messageJSON, topic, publicKey, optionalSigningKey, ms) + return prepareJSONResponse(hash, err) +} + +// RelayPublishEncodeSymmetric publishes a message encrypted with a 32 bytes symmetric key using waku relay and returns the message ID +func RelayPublishEncodeSymmetric(messageJSON string, topic string, symmetricKey string, optionalSigningKey string, ms int) string { + hash, err := library.RelayPublishEncodeSymmetric(messageJSON, topic, symmetricKey, optionalSigningKey, ms) + return prepareJSONResponse(hash, err) +} + +// RelaySubscribe subscribes to a WakuRelay topic. +func RelaySubscribe(topic string) string { + err := library.RelaySubscribe(topic) + return makeJSONResponse(err) +} + +// RelayTopics returns a list of pubsub topics the node is subscribed to in WakuRelay +func RelayTopics() string { + topics, err := library.RelayTopics() + return prepareJSONResponse(topics, err) +} + +// RelayUnsubscribe closes the pubsub subscription to a pubsub topic +func RelayUnsubscribe(topic string) string { + err := library.RelayUnsubscribe(topic) + return makeJSONResponse(err) +} diff --git a/library/mobile/api_store.go b/library/mobile/api_store.go new file mode 100644 index 00000000..9eb35426 --- /dev/null +++ b/library/mobile/api_store.go @@ -0,0 +1,17 @@ +package gowaku + +import ( + "github.com/waku-org/go-waku/library" +) + +// StoreQuery is used to retrieve historic messages using waku store protocol. +func StoreQuery(queryJSON string, peerID string, ms int) string { + response, err := library.StoreQuery(queryJSON, peerID, ms) + return prepareJSONResponse(response, err) +} + +// StoreLocalQuery is used to retrieve historic messages stored in the localDB using waku store protocol. +func StoreLocalQuery(queryJSON string) string { + response, err := library.StoreLocalQuery(queryJSON) + return prepareJSONResponse(response, err) +} diff --git a/mobile/response.go b/library/mobile/response.go similarity index 83% rename from mobile/response.go rename to library/mobile/response.go index 825583ae..7344e285 100644 --- a/mobile/response.go +++ b/library/mobile/response.go @@ -10,7 +10,7 @@ type jsonResponseSuccess struct { Result interface{} `json:"result"` } -func PrepareJSONResponse(result interface{}, err error) string { +func prepareJSONResponse(result interface{}, err error) string { if err != nil { errStr := err.Error() @@ -23,12 +23,12 @@ func PrepareJSONResponse(result interface{}, err error) string { data, err := json.Marshal(jsonResponseSuccess{Result: result}) if err != nil { - return PrepareJSONResponse(nil, err) + return prepareJSONResponse(nil, err) } return string(data) } -func MakeJSONResponse(err error) string { +func makeJSONResponse(err error) string { if err != nil { errStr := err.Error() outBytes, _ := json.Marshal(jsonResponseError{Error: &errStr}) diff --git a/mobile/api.go b/library/node.go similarity index 71% rename from mobile/api.go rename to library/node.go index f175918a..e8352c9a 100644 --- a/mobile/api.go +++ b/library/node.go @@ -1,6 +1,4 @@ -// Implements gomobile bindings for go-waku. Contains a set of functions that -// are exported when go-waku is exported as libraries for mobile devices -package gowaku +package library import ( "context" @@ -35,6 +33,7 @@ import ( "github.com/waku-org/go-waku/waku/v2/utils" ) +// WakuState represents the state of the waku node type WakuState struct { ctx context.Context cancel context.CancelFunc @@ -56,35 +55,36 @@ func randomHex(n int) (string, error) { return hex.EncodeToString(bytes), nil } -func NewNode(configJSON string) string { +// NewNode initializes a waku node. Receives a JSON string containing the configuration, and use default values for those config items not specified +func NewNode(configJSON string) error { if wakuState.node != nil { - return MakeJSONResponse(errors.New("go-waku already initialized. stop it first")) + return errors.New("go-waku already initialized. stop it first") } config, err := getConfig(configJSON) if err != nil { - return MakeJSONResponse(err) + return err } hostAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", *config.Host, *config.Port)) if err != nil { - return MakeJSONResponse(err) + return err } var prvKey *ecdsa.PrivateKey if config.NodeKey != nil { prvKey, err = crypto.HexToECDSA(*config.NodeKey) if err != nil { - return MakeJSONResponse(err) + return err } } else { key, err := randomHex(32) if err != nil { - return MakeJSONResponse(err) + return err } prvKey, err = crypto.HexToECDSA(key) if err != nil { - return MakeJSONResponse(err) + return err } } @@ -97,7 +97,7 @@ func NewNode(configJSON string) string { if *config.EnableRelay { var pubsubOpt []pubsub.Option if config.GossipSubParams != nil { - params := GetGossipSubParams(config.GossipSubParams) + params := getGossipSubParams(config.GossipSubParams) pubsubOpt = append(pubsubOpt, pubsub.WithGossipSubParams(params)) } @@ -133,7 +133,7 @@ func NewNode(configJSON string) string { var migrationFn func(*sql.DB) error db, migrationFn, err = dbutils.ExtractDBAndMigration(*config.DatabaseURL) if err != nil { - return MakeJSONResponse(err) + return err } opts = append(opts, node.WithWakuStore()) dbStore, err := persistence.NewDBStore(utils.Logger(), @@ -142,7 +142,7 @@ func NewNode(configJSON string) string { persistence.WithRetentionPolicy(*config.RetentionMaxMessages, time.Duration(*config.RetentionTimeSeconds)*time.Second), ) if err != nil { - return MakeJSONResponse(err) + return err } opts = append(opts, node.WithMessageProvider(dbStore)) } @@ -152,7 +152,7 @@ func NewNode(configJSON string) string { for _, addr := range config.DiscV5BootstrapNodes { bootnode, err := enode.Parse(enode.ValidSchemes, addr) if err != nil { - return MakeJSONResponse(err) + return err } bootnodes = append(bootnodes, bootnode) } @@ -163,36 +163,37 @@ func NewNode(configJSON string) string { lvl, err := zapcore.ParseLevel(*config.LogLevel) if err != nil { - return MakeJSONResponse(err) + return err } opts = append(opts, node.WithLogLevel(lvl)) w, err := node.New(opts...) if err != nil { - return MakeJSONResponse(err) + return err } wakuState.node = w - return MakeJSONResponse(nil) + return nil } -func Start() string { +// Start starts the waku node +func Start() error { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return errWakuNodeNotReady } wakuState.ctx, wakuState.cancel = context.WithCancel(context.Background()) if err := wakuState.node.Start(wakuState.ctx); err != nil { - return MakeJSONResponse(err) + return err } if wakuState.node.DiscV5() != nil { if err := wakuState.node.DiscV5().Start(context.Background()); err != nil { wakuState.node.Stop() - return MakeJSONResponse(err) + return err } } @@ -200,16 +201,17 @@ func Start() string { err := relaySubscribe(topic) if err != nil { wakuState.node.Stop() - return MakeJSONResponse(err) + return err } } - return MakeJSONResponse(nil) + return nil } -func Stop() string { +// Stop stops a waku node +func Stop() error { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return errWakuNodeNotReady } wakuState.node.Stop() @@ -218,24 +220,27 @@ func Stop() string { wakuState.node = nil - return MakeJSONResponse(nil) + return nil } -func IsStarted() string { - return PrepareJSONResponse(wakuState.node != nil, nil) +// IsStarted is used to determine is a node is started or not +func IsStarted() bool { + return wakuState.node != nil } -func PeerID() string { +// PeerID is used to obtain the peer ID of the waku node +func PeerID() (string, error) { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return "", errWakuNodeNotReady } - return PrepareJSONResponse(wakuState.node.ID(), nil) + return wakuState.node.ID(), nil } -func ListenAddresses() string { +// ListenAddresses returns the multiaddresses the wakunode is listening to +func ListenAddresses() (string, error) { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return "", errWakuNodeNotReady } var addresses []string @@ -243,26 +248,32 @@ func ListenAddresses() string { addresses = append(addresses, addr.String()) } - return PrepareJSONResponse(addresses, nil) + return marshalJSON(addresses) } -func AddPeer(address string, protocolID string) string { +// AddPeer adds a node multiaddress and protocol to the wakunode peerstore +func AddPeer(address string, protocolID string) (string, error) { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return "", errWakuNodeNotReady } ma, err := multiaddr.NewMultiaddr(address) if err != nil { - return MakeJSONResponse(err) + return "", err } peerID, err := wakuState.node.AddPeer(ma, peerstore.Static, libp2pProtocol.ID(protocolID)) - return PrepareJSONResponse(peerID, err) + if err != nil { + return "", err + } + + return marshalJSON(peerID) } -func Connect(address string, ms int) string { +// Connect is used to connect to a peer at multiaddress. if ms > 0, cancel the function execution if it takes longer than N milliseconds +func Connect(address string, ms int) error { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return errWakuNodeNotReady } var ctx context.Context @@ -275,13 +286,13 @@ func Connect(address string, ms int) string { ctx = context.Background() } - err := wakuState.node.DialPeer(ctx, address) - return MakeJSONResponse(err) + return wakuState.node.DialPeer(ctx, address) } -func ConnectPeerID(peerID string, ms int) string { +// ConnectPeerID is usedd to connect to a known peer by peerID. if ms > 0, cancel the function execution if it takes longer than N milliseconds +func ConnectPeerID(peerID string, ms int) error { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return errWakuNodeNotReady } var ctx context.Context @@ -289,7 +300,7 @@ func ConnectPeerID(peerID string, ms int) string { pID, err := peer.Decode(peerID) if err != nil { - return MakeJSONResponse(err) + return err } if ms > 0 { @@ -299,40 +310,43 @@ func ConnectPeerID(peerID string, ms int) string { ctx = context.Background() } - err = wakuState.node.DialPeerByID(ctx, pID) - return MakeJSONResponse(err) + return wakuState.node.DialPeerByID(ctx, pID) } -func Disconnect(peerID string) string { +// Disconnect closes a connection to a known peer by peerID +func Disconnect(peerID string) error { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return errWakuNodeNotReady } pID, err := peer.Decode(peerID) if err != nil { - return MakeJSONResponse(err) + return err } - err = wakuState.node.ClosePeerById(pID) - return MakeJSONResponse(err) + return wakuState.node.ClosePeerById(pID) } -func PeerCnt() string { +// PeerCnt returns the number of connected peers +func PeerCnt() (int, error) { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return 0, errWakuNodeNotReady } - return PrepareJSONResponse(wakuState.node.PeerCount(), nil) + return wakuState.node.PeerCount(), nil } +// ContentTopic creates a content topic string according to RFC 23 func ContentTopic(applicationName string, applicationVersion int, contentTopicName string, encoding string) string { return protocol.NewContentTopic(applicationName, uint(applicationVersion), contentTopicName, encoding).String() } +// PubsubTopic creates a pubsub topic string according to RFC 23 func PubsubTopic(name string, encoding string) string { return protocol.NewNamedShardingPubsubTopic(name + "/" + encoding).String() } +// DefaultPubsubTopic returns the default pubsub topic used in waku2: /waku/2/default-waku/proto func DefaultPubsubTopic() string { return protocol.DefaultPubsubTopic().String() } @@ -358,13 +372,18 @@ func toSubscriptionMessage(msg *protocol.Envelope) *subscriptionMsg { } } -func Peers() string { +// Peers retrieves the list of peers known by the waku node +func Peers() (string, error) { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return "", errWakuNodeNotReady } peers, err := wakuState.node.Peers() - return PrepareJSONResponse(peers, err) + if err != nil { + return "", err + } + + return marshalJSON(peers) } func unmarshalPubkey(pub []byte) (ecdsa.PublicKey, error) { @@ -388,17 +407,18 @@ func extractPubKeyAndSignature(payload *payload.DecodedPayload) (pubkey string, return } -func DecodeSymmetric(messageJSON string, symmetricKey string) string { +// DecodeSymmetric decodes a waku message using a 32 bytes symmetric key. The key must be a hex encoded string with "0x" prefix +func DecodeSymmetric(messageJSON string, symmetricKey string) (string, error) { var msg pb.WakuMessage err := json.Unmarshal([]byte(messageJSON), &msg) if err != nil { - return MakeJSONResponse(err) + return "", err } if msg.Version == 0 { - return PrepareJSONResponse(msg.Payload, nil) + return marshalJSON(msg.Payload) } else if msg.Version > 1 { - return MakeJSONResponse(errors.New("unsupported wakumessage version")) + return "", errors.New("unsupported wakumessage version") } keyInfo := &payload.KeyInfo{ @@ -407,12 +427,12 @@ func DecodeSymmetric(messageJSON string, symmetricKey string) string { keyInfo.SymKey, err = utils.DecodeHexString(symmetricKey) if err != nil { - return MakeJSONResponse(err) + return "", err } payload, err := payload.DecodePayload(&msg, keyInfo) if err != nil { - return MakeJSONResponse(err) + return "", err } pubkey, signature := extractPubKeyAndSignature(payload) @@ -429,20 +449,21 @@ func DecodeSymmetric(messageJSON string, symmetricKey string) string { Padding: payload.Padding, } - return PrepareJSONResponse(response, err) + return marshalJSON(response) } -func DecodeAsymmetric(messageJSON string, privateKey string) string { +// DecodeAsymmetric decodes a waku message using a secp256k1 private key. The key must be a hex encoded string with "0x" prefix +func DecodeAsymmetric(messageJSON string, privateKey string) (string, error) { var msg pb.WakuMessage err := json.Unmarshal([]byte(messageJSON), &msg) if err != nil { - return MakeJSONResponse(err) + return "", err } if msg.Version == 0 { - return PrepareJSONResponse(msg.Payload, nil) + return marshalJSON(msg.Payload) } else if msg.Version > 1 { - return MakeJSONResponse(errors.New("unsupported wakumessage version")) + return "", errors.New("unsupported wakumessage version") } keyInfo := &payload.KeyInfo{ @@ -451,17 +472,17 @@ func DecodeAsymmetric(messageJSON string, privateKey string) string { keyBytes, err := utils.DecodeHexString(privateKey) if err != nil { - return MakeJSONResponse(err) + return "", err } keyInfo.PrivKey, err = crypto.ToECDSA(keyBytes) if err != nil { - return MakeJSONResponse(err) + return "", err } payload, err := payload.DecodePayload(&msg, keyInfo) if err != nil { - return MakeJSONResponse(err) + return "", err } pubkey, signature := extractPubKeyAndSignature(payload) @@ -478,5 +499,5 @@ func DecodeAsymmetric(messageJSON string, privateKey string) string { Padding: payload.Padding, } - return PrepareJSONResponse(response, err) + return marshalJSON(response) } diff --git a/mobile/api_relay.go b/library/relay.go similarity index 59% rename from mobile/api_relay.go rename to library/relay.go index 6ae67dda..54396489 100644 --- a/mobile/api_relay.go +++ b/library/relay.go @@ -1,4 +1,4 @@ -package gowaku +package library import ( "context" @@ -15,9 +15,10 @@ import ( var relaySubscriptions map[string]*relay.Subscription = make(map[string]*relay.Subscription) var relaySubsMutex sync.Mutex -func RelayEnoughPeers(topic string) string { +// RelayEnoughPeers determines if there are enough peers to publish a message on a topic +func RelayEnoughPeers(topic string) (bool, error) { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return false, errWakuNodeNotReady } topicToCheck := protocol.DefaultPubsubTopic().String() @@ -25,7 +26,7 @@ func RelayEnoughPeers(topic string) string { topicToCheck = topic } - return PrepareJSONResponse(wakuState.node.Relay().EnoughPeersToPublishToTopic(topicToCheck), nil) + return wakuState.node.Relay().EnoughPeersToPublishToTopic(topicToCheck), nil } func relayPublish(msg *pb.WakuMessage, pubsubTopic string, ms int) (string, error) { @@ -47,36 +48,34 @@ func relayPublish(msg *pb.WakuMessage, pubsubTopic string, ms int) (string, erro return hexutil.Encode(hash), err } -func RelayPublish(messageJSON string, topic string, ms int) string { +// RelayPublish publishes a message using waku relay and returns the message ID +func RelayPublish(messageJSON string, topic string, ms int) (string, error) { msg, err := wakuMessage(messageJSON) if err != nil { - return MakeJSONResponse(err) + return "", err } - hash, err := relayPublish(msg, getTopic(topic), int(ms)) - return PrepareJSONResponse(hash, err) + return relayPublish(msg, getTopic(topic), int(ms)) } -func RelayPublishEncodeAsymmetric(messageJSON string, topic string, publicKey string, optionalSigningKey string, ms int) string { +// RelayPublishEncodeAsymmetric publish a message encrypted with a secp256k1 public key using waku relay and returns the message ID +func RelayPublishEncodeAsymmetric(messageJSON string, topic string, publicKey string, optionalSigningKey string, ms int) (string, error) { msg, err := wakuMessageAsymmetricEncoding(messageJSON, publicKey, optionalSigningKey) if err != nil { - return MakeJSONResponse(err) + return "", err } - hash, err := relayPublish(msg, getTopic(topic), int(ms)) - - return PrepareJSONResponse(hash, err) + return relayPublish(msg, getTopic(topic), int(ms)) } -func RelayPublishEncodeSymmetric(messageJSON string, topic string, symmetricKey string, optionalSigningKey string, ms int) string { +// RelayPublishEncodeSymmetric publishes a message encrypted with a 32 bytes symmetric key using waku relay and returns the message ID +func RelayPublishEncodeSymmetric(messageJSON string, topic string, symmetricKey string, optionalSigningKey string, ms int) (string, error) { msg, err := wakuMessageSymmetricEncoding(messageJSON, symmetricKey, optionalSigningKey) if err != nil { - return MakeJSONResponse(err) + return "", err } - hash, err := relayPublish(msg, getTopic(topic), int(ms)) - - return PrepareJSONResponse(hash, err) + return relayPublish(msg, getTopic(topic), int(ms)) } func relaySubscribe(topic string) error { @@ -106,25 +105,28 @@ func relaySubscribe(topic string) error { return nil } -func RelaySubscribe(topic string) string { +// RelaySubscribe subscribes to a WakuRelay topic. +func RelaySubscribe(topic string) error { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return errWakuNodeNotReady } - return MakeJSONResponse(relaySubscribe(topic)) + return relaySubscribe(topic) } -func RelayTopics() string { +// RelayTopics returns a list of pubsub topics the node is subscribed to in WakuRelay +func RelayTopics() (string, error) { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return "", errWakuNodeNotReady } - return PrepareJSONResponse(wakuState.node.Relay().Topics(), nil) + return marshalJSON(wakuState.node.Relay().Topics()) } -func RelayUnsubscribe(topic string) string { +// RelayUnsubscribe closes the pubsub subscription to a pubsub topic +func RelayUnsubscribe(topic string) error { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return errWakuNodeNotReady } topicToUnsubscribe := getTopic(topic) @@ -134,17 +136,12 @@ func RelayUnsubscribe(topic string) string { subscription, ok := relaySubscriptions[topicToUnsubscribe] if ok { - return MakeJSONResponse(nil) + return nil } subscription.Unsubscribe() delete(relaySubscriptions, topicToUnsubscribe) - err := wakuState.node.Relay().Unsubscribe(context.Background(), topicToUnsubscribe) - if err != nil { - return MakeJSONResponse(err) - } - - return MakeJSONResponse(nil) + return wakuState.node.Relay().Unsubscribe(context.Background(), topicToUnsubscribe) } diff --git a/library/response.go b/library/response.go new file mode 100644 index 00000000..28ce97f5 --- /dev/null +++ b/library/response.go @@ -0,0 +1,11 @@ +package library + +import "encoding/json" + +func marshalJSON(result interface{}) (string, error) { + data, err := json.Marshal(result) + if err != nil { + return "", err + } + return string(data), nil +} diff --git a/mobile/signals.c b/library/signals.c similarity index 74% rename from mobile/signals.c rename to library/signals.c index b06ff9fd..8733fbc5 100644 --- a/mobile/signals.c +++ b/library/signals.c @@ -7,12 +7,12 @@ #include #include "_cgo_export.h" -typedef void (*callback)(const char *jsonEvent); +typedef void (*callback)(const char *jsonEvent, size_t len_0); callback gCallback = 0; -bool StatusServiceSignalEvent(const char *jsonEvent) { +bool ServiceSignalEvent(const char *jsonEvent, size_t len_0) { if (gCallback) { - gCallback(jsonEvent); + gCallback(jsonEvent, len_0); } return true; diff --git a/mobile/signals.go b/library/signals.go similarity index 91% rename from mobile/signals.go rename to library/signals.go index 142802e8..239cac5e 100644 --- a/mobile/signals.go +++ b/library/signals.go @@ -1,10 +1,10 @@ -package gowaku +package library /* #include #include #include -extern bool StatusServiceSignalEvent(const char *jsonEvent); +extern bool ServiceSignalEvent(const char *jsonEvent, size_t len); extern void SetEventCallback(void *cb); */ import "C" @@ -56,8 +56,9 @@ func send(signalType string, event interface{}) { mobileSignalHandler(data) } else { // ...and fallback to C implementation otherwise. - str := C.CString(string(data)) - C.StatusServiceSignalEvent(str) + dataStr := string(data) + str := C.CString(dataStr) + C.ServiceSignalEvent(str, C.size_t(len(data))) C.free(unsafe.Pointer(str)) } } diff --git a/mobile/api_store.go b/library/store.go similarity index 83% rename from mobile/api_store.go rename to library/store.go index b44ce3b7..0dbfe29a 100644 --- a/mobile/api_store.go +++ b/library/store.go @@ -1,4 +1,4 @@ -package gowaku +package library import ( "C" @@ -35,7 +35,7 @@ type storeMessagesReply struct { Error string `json:"error,omitempty"` } -func queryResponse(ctx context.Context, args storeMessagesArgs, options []store.HistoryRequestOption) string { +func queryResponse(ctx context.Context, args storeMessagesArgs, options []store.HistoryRequestOption) (string, error) { var contentTopics []string for _, ct := range args.ContentFilters { contentTopics = append(contentTopics, ct.ContentTopic) @@ -56,7 +56,7 @@ func queryResponse(ctx context.Context, args storeMessagesArgs, options []store. if err != nil { reply.Error = err.Error() - return PrepareJSONResponse(reply, nil) + return marshalJSON(reply) } reply.Messages = res.Messages reply.PagingInfo = storePagingOptions{ @@ -65,18 +65,19 @@ func queryResponse(ctx context.Context, args storeMessagesArgs, options []store. Forward: args.PagingOptions.Forward, } - return PrepareJSONResponse(reply, nil) + return marshalJSON(reply) } -func StoreQuery(queryJSON string, peerID string, ms int) string { +// StoreQuery is used to retrieve historic messages using waku store protocol. +func StoreQuery(queryJSON string, peerID string, ms int) (string, error) { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return "", errWakuNodeNotReady } var args storeMessagesArgs err := json.Unmarshal([]byte(queryJSON), &args) if err != nil { - return MakeJSONResponse(err) + return "", err } options := []store.HistoryRequestOption{ @@ -88,7 +89,7 @@ func StoreQuery(queryJSON string, peerID string, ms int) string { if peerID != "" { p, err := peer.Decode(peerID) if err != nil { - return MakeJSONResponse(err) + return "", err } options = append(options, store.WithPeer(p)) } else { @@ -108,15 +109,16 @@ func StoreQuery(queryJSON string, peerID string, ms int) string { return queryResponse(ctx, args, options) } -func StoreLocalQuery(queryJSON string) string { +// StoreLocalQuery is used to retrieve historic messages stored in the localDB using waku store protocol. +func StoreLocalQuery(queryJSON string) (string, error) { if wakuState.node == nil { - return MakeJSONResponse(errWakuNodeNotReady) + return "", errWakuNodeNotReady } var args storeMessagesArgs err := json.Unmarshal([]byte(queryJSON), &args) if err != nil { - return MakeJSONResponse(err) + return "", err } options := []store.HistoryRequestOption{