From aa6d8c8b58436452b9858d0416cf7e36a097b759 Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Tue, 22 Mar 2022 12:30:14 -0400 Subject: [PATCH] fix: c api and add some documentation (#219) --- Makefile | 5 +- build/.gitignore | 1 + build/lib/.gitignore | 2 + examples/c-bindings/main.c | 14 +- library/README.md | 441 +++++++++++++++++++++++++++++++++++++ library/api.go | 66 +++--- library/response.go | 38 +--- library/signals.go | 7 + library/types.go | 74 ------- waku/v2/node/wakunode2.go | 8 +- 10 files changed, 515 insertions(+), 141 deletions(-) create mode 100644 build/lib/.gitignore create mode 100644 library/README.md delete mode 100644 library/types.go diff --git a/Makefile b/Makefile index 3cf797c4..727035a0 100644 --- a/Makefile +++ b/Makefile @@ -83,7 +83,10 @@ build-example-chat-2: build-example-filter2: cd examples/filter2 && $(MAKE) -build-example: build-example-basic2 build-example-chat-2 build-example-filter2 +build-example-c-bindings: + cd examples/c-bindings && $(MAKE) + +build-example: build-example-basic2 build-example-chat-2 build-example-filter2 build-example-c-bindings static-library: ##@cross-compile Build go-waku as static library for current platform mkdir -p ./build/lib diff --git a/build/.gitignore b/build/.gitignore index d6b7ef32..2db665d9 100644 --- a/build/.gitignore +++ b/build/.gitignore @@ -1,2 +1,3 @@ * !.gitignore +!lib \ No newline at end of file diff --git a/build/lib/.gitignore b/build/lib/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/build/lib/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/examples/c-bindings/main.c b/examples/c-bindings/main.c index d8484db8..797bce75 100644 --- a/examples/c-bindings/main.c +++ b/examples/c-bindings/main.c @@ -29,13 +29,13 @@ int main(int argc, char *argv[]) - response = gowaku_start(nodeID); // Start the node, enabling the waku protocols + response = gowaku_start(1); // Start the node, enabling the waku protocols if (isError(response)) return 1; - response = gowaku_id(nodeID); // Obtain the node peerID + response = gowaku_peerid(nodeID); // Obtain the node peerID if (isError(response)) return 1; char *nodePeerID = getStrValue(response); @@ -61,14 +61,15 @@ int main(int argc, char *argv[]) int version = 1; while (true){ i++; - - response = gowaku_encode_data("Hello World!", ASYMMETRIC, bobPubKey, alicePrivKey, version); // Send a message encrypting it with Bob's PubK, and signing it with Alice PrivK + + char *msg = gowaku_utils_base64_encode("Hello World!"); + response = gowaku_encode_data(msg, ASYMMETRIC, bobPubKey, alicePrivKey, version); // Send a message encrypting it with Bob's PubK, and signing it with Alice PrivK if (isError(response)) return 1; char *encodedData = getStrValue(response); - char *contentTopic = getStrValue(gowaku_content_topic("example", 1, "default", "rfc26")); + char *contentTopic = gowaku_content_topic("example", 1, "default", "rfc26"); char wakuMsg[1000]; @@ -100,8 +101,9 @@ void callBack(char *signal) "nodeId":1, "type":"message", "event":{ - "messageID":"0x6496491e40dbe0b6c3a2198c2426b16301688a2daebc4f57ad7706115eac3ad1", "pubsubTopic":"/waku/2/default-waku/proto", + "subscriptionID": ".....", + "messageID":"0x6496491e40dbe0b6c3a2198c2426b16301688a2daebc4f57ad7706115eac3ad1", "wakuMessage":{ "payload":"BPABASUqWgRkgp73aW/FHIyGtJDYnStvaQvCoX9MdaNsOH39Vet0em6ipZc3lZ7kK9uFFtbJgIWfRaqTxSRjiFOPx88gXt1JeSm2SUwGSz+1gh2xTy0am8tXkc8OWSSjamdkEbXuVgAueLxHOnV3xlGwYt7nx2G5DWYqUu1BXv4yWHPOoiH2yx3fxX0OajgKGBwiMbadRNUuAUFPRM90f+bzG2y22ssHctDV/U6sXOa9ljNgpAx703Q3WIFleSRozto7ByNAdRFwWR0RGGV4l0btJXM7JpnrYcVC24dB0tJ3HVWuD0ZcwOM1zTL0wwc0hTezLHvI+f6bHSzsFGcCWIlc03KSoMjK1XENNL4dtDmSFI1DQCGgq09c2Bc3Je3Ci6XJHu+FP1F1pTnRzevv2WP8FSBJiTXpmJXdm6evB7V1Xxj4QlzQDvmHLRpBOL6PSttxf1Dc0IwC6BfZRN5g0dNmItNlS2pcY1MtZLxD5zpj", "contentTopic":"ABC", diff --git a/library/README.md b/library/README.md new file mode 100644 index 00000000..51d7d8da --- /dev/null +++ b/library/README.md @@ -0,0 +1,441 @@ +--- +slug: # +title: #/GOWAKU-BINDINGS +name: Go-Waku v2 C Bindings +status: draft +tags: go-waku +editor: Richard Ramos +contributors: + +--- + +This specification describes the API for consuming go-waku when built as a dynamic or static library + + +# libgowaku.h + + +## General + + +### JSONResponse +All the API functions return a `JSONResponse` unless specified otherwise. `JSONResponse` is a `char *` whose format depends on whether the function was executed sucessfully or not: +```json +// On failure: +{ "error": "the error message" } + +// On success: +{ "result": ... } // result format depends on the function response +``` + +## Events +Asynchronous events require a callback to be registered. An example of an asynchronous event that might be emitted is receiving a message. When an event is emitted, this callback will be triggered receiving a json string with the following format: +```json +{ + "nodeId": 0, // go-waku node that emitted the signal + "type": "message", // type of signal being emitted. Currently only "message" is available + "event": ... // format depends on the type of signal. In the case of "message", a waku message can be expected here +} +``` + +### `extern void gowaku_set_event_callback(void* cb)` +Register callback to act as event handler and receive application signals, which are used to react to asyncronous events in waku. + +**Parameters** +1. `void* cb`: callback that will be executed when an async event is emitted. The function signature for the callback should be `void myCallback(char* signalJSON)` + + +## Node management + +### `extern char* gowaku_new(char* configJSON)` +Initialize a go-waku node. + +**Parameters** +1. `char* configJSON`: JSON string containing the options used to initialize a go-waku node. It can be `NULL` to use defaults. All the keys from the configuration are optional. If a key is `undefined`, or `null`, a default value will be set + ```json + // example config: + { + "host": "0.0.0.0", + "port": 60000, + "advertiseAddr": "1.2.3.4", + "nodeKey": "0x123...567", + "keepAliveInterval": 20, + "relay": true, + "minPeersToPublish": 0 + } + ``` + - `host` - `String` (optional): Listening IP address. Default `0.0.0.0` + - `port` - `Number` (optional): Libp2p TCP listening port. Default `60000`. Use `0` for random + - `advertiseAddr` - `String` (optional): External address to advertise to other nodes. + - `nodeKey` - `String` (optional): secp256k1 private key in Hex format (`0x123...abc`). Default random + - `keepAliveInterval` - `Number` (optional): Interval in seconds for pinging peers to keep the connection alive. Default `20` + - `relay` - `Boolean` (optional): Enable relay protocol. Default `true` + - `minPeersToPublish` - `Number` (optional). The minimum number of peers required on a topic to allow broadcasting a message. Default `0` + +**Returns** +`JSONResponse` with a `result` containing an `int` which represents the `nodeID` which should be used in all calls from this API that require interacting with the node if the function executes successfully. An `error` message otherwise + +--- + +### `extern char* gowaku_start(int nodeID)` +Initialize a go-waku node mounting all the protocols that were enabled during the waku node initialization. + +**Parameters** +1. `int nodeID`: the node identifier obtained from a succesful execution of `gowaku_new` + +--- + +### `extern char* gowaku_stop(int nodeID)` +Stops a go-waku node + +**Parameters** +1. `int nodeID`: the node identifier obtained from a succesful execution of `gowaku_new` + +**Returns** +`JSONResponse` containing a null `result` if the function executes successfully. An `error` message otherwise + +--- + +### `extern char* gowaku_peerid(int nodeID)` +Obtain the peer ID of the go-waku node. + +**Parameters** +1. `int nodeID`: the node identifier obtained from a succesful execution of `gowaku_new` + +**Returns** +`JSONResponse` containing the peer ID (base58 encoded) if the function executes successfully. An `error` message otherwise + +--- + +### `extern char* gowaku_listen_addresses(int nodeID)` +Obtain the multiaddresses the wakunode is listening to + +**Parameters** +1. `int nodeID`: the node identifier obtained from a succesful execution of `gowaku_new` + +**Returns** +`JSONResponse` containing an array of multiaddresses if the function executes successfully. An `error` message otherwise + + +## Connecting to peers + +### `extern char* gowaku_add_peer(int nodeID, char* address, char* protocolID)` +Add node multiaddress and protocol to the wakunode peerstore + +**Parameters** +1. `int nodeID`: the node identifier obtained from a succesful execution of `gowaku_new` +2. `char* address`: multiaddress of the peer being added +3. `char* protocolID`: protocol supported by the peer + +**Returns** +`JSONResponse` containing the peer ID (base58 encoded) of the peer that was added if the function executes successfully. An `error` message otherwise + +--- + +### `extern char* gowaku_dial_peer(int nodeID, char* address, int ms)` +Dial peer at multiaddress. + +**Parameters** +1. `int nodeID`: the node identifier obtained from a succesful execution of `gowaku_new` +2. `char* address`: multiaddress of the peer being dialed +3. `int ms`: max duration in milliseconds this function might take to execute. If the function execution takes longer than this value, the execution will be canceled and an error returned. Use `0` for unlimited duration + +**Returns** +`JSONResponse` with a null `result` if the function executes successfully. An `error` message otherwise + +--- + +### `extern char* gowaku_dial_peerid(int nodeID, char* id, int ms)` +Dial peer using peerID. + +**Parameters** +1. `int nodeID`: the node identifier obtained from a succesful execution of `gowaku_new` +2. `char* peerID`: peerID to dial. The peer must be already known. It must have been added before with `gowaku_add_peer` or previously dialed with `gowaku_dial_peer` +3. `int ms`: max duration in milliseconds this function might take to execute. If the function execution takes longer than this value, the execution will be canceled and an error returned. Use `0` for unlimited duration + +**Returns** +`JSONResponse` with a null `result` if the function executes successfully. An `error` message otherwise + +--- + +### `extern char* gowaku_close_peer(int nodeID, char* address)` +Disconnect a peer using its multiaddress + +**Parameters** +1. `int nodeID`: the node identifier obtained from a succesful execution of `gowaku_new` +2. `char* address`: multiaddress of the peer being disconnected. + +**Returns** +`JSONResponse` with a null `result` if the function executes successfully. An `error` message otherwise + +--- + +### `extern char* gowaku_close_peerid(int nodeID, char* id)` +Disconnect a peer using its peerID +**Parameters** +1. `int nodeID`: the node identifier obtained from a succesful execution of `gowaku_new` +2. `char* peerID`: peerID to disconnect. + +**Returns** +`JSONResponse` with a null `result` if the function executes successfully. An `error` message otherwise + +--- + +### `extern char* gowaku_peer_cnt(int nodeID)` +Obtain number of connected peers + +**Parameters** +1. `int nodeID`: the node identifier obtained from a succesful execution of `gowaku_new` + +**Returns** +`JSONResponse` containing an `int` with the number of connected peers. An `error` message otherwise + +--- + +### `extern char* gowaku_peers(int nodeID)` +Retrieve the list of peers known by the go-waku node + +**Parameters** +1. `int nodeID`: the node identifier obtained from a succesful execution of `gowaku_new` + +**Returns** +`JSONResponse` containing a list of peers. An `error` message otherwise. The list of peers 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 + } + ] +} +``` + + +## Waku Relay + + +### `extern char* gowaku_content_topic(char* applicationName, unsigned int applicationVersion, char* contentTopicName, char* encoding)` +Create a content topic string according to [RFC 23](https://rfc.vac.dev/spec/23/) + +**Parameters** +1. `char* applicationName` +2. `unsigned int applicationVersion` +3. `char* contentTopicName` +4. `char* encoding`: depending on the payload, use `proto`, `rlp` or `rfc26` + +**Returns** +`char *` containing a 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* gowaku_pubsub_topic(char* name, char* encoding)` +Create a pubsub topic string according to [RFC 23](https://rfc.vac.dev/spec/23/) + +**Parameters** +1. `char* name` +2. `char* encoding`: depending on the payload, use `proto`, `rlp` or `rfc26` + +**Returns** +`char *` containing a content topic formatted according to [RFC 23](https://rfc.vac.dev/spec/23/) +``` +/waku/2/{topic-name}/{encoding} +``` + +--- + +### `extern char* gowaku_default_pubsub_topic()` +Returns the default pubsub topic used for exchanging waku messages defined in [RFC 10](https://rfc.vac.dev/spec/10/) + +**Returns** +`char *` containing the default pubsub topic: +``` +/waku/2/default-waku/proto +``` + +--- + +### `extern char* gowaku_relay_publish(int nodeID, char* messageJSON, char* topic, int ms)` +Publish a message using waku relay. + +**Parameters** +1. `int nodeID`: the node identifier obtained from a succesful execution of `gowaku_new` +2. `char* messageJSON`: json string containing the [Waku Message](https://rfc.vac.dev/spec/14/) + ```json + { + "payload":"", // base64 encoded payload. gowaku_utils_base64_encode can be used for this + "contentTopic: "...", + "version": 1, + "timestamp": 1647963508000000000 // Unix timestamp in nanoseconds + } + ``` +3. `char* topic`: pubsub topic. Set to `NULL` to use the default pubsub topic +4. `int ms`: max duration in milliseconds this function might take to execute. If the function execution takes longer than this value, the execution will be canceled and an error returned. Use `0` for unlimited duration + +**Returns** +`JSONResponse` containing the message ID. An `error` message otherwise + +--- + +### `extern char* gowaku_enough_peers(int nodeID, char* topic)` +Determine if there are enough peers to publish a message on a topic. + +**Parameters** +1. `int nodeID`: the node identifier obtained from a succesful execution of `gowaku_new` +2. `char* topic`: pubsub topic to verify. Use `NULL` to verify the number of peers in the default pubsub topic + +**Returns** +`JSONResponse` with a boolean indicating if there are enough peers or not. An `error` message otherwise + +--- + +### `extern char* gowaku_relay_subscribe(int nodeID, char* topic)` +Subscribe to a WakuRelay topic to receive messages. + +**Parameters** +1. `int nodeID`: the node identifier obtained from a succesful execution of `gowaku_new` +2. `char* topic`: pubsub topic to subscribe to. Use `NULL` for subscribing to the default pubsub topic + + +**Returns** +`JSONResponse` with a subscription ID. An `error` message otherwise + +**Events** +When a message is received, a ``"message"` event` is emitted containing the message, pubsub topic, and nodeID in which the message was received. Here's an example event that could be received: +```json +{ + "nodeId":1, + "type":"message", + "event":{ + "subscriptionID":"...", + "pubsubTopic":"/waku/2/default-waku/proto", + "messageID":"0x6496491e40dbe0b6c3a2198c2426b16301688a2daebc4f57ad7706115eac3ad1", + "wakuMessage":{ + "payload":"...", // base64 encoded message. Use gowaku_decode_data to decode + "contentTopic":"ABC", + "version":1, + "timestamp":1647826358000000000 // in nanoseconds + } + } +} +``` + +### `extern char* gowaku_relay_close_subscription(int nodeID, char* subsID)` +Closes a waku relay subscription. No more messages will be received from this subscription + +**Parameters** +1. `int nodeID`: the node identifier obtained from a succesful execution of `gowaku_new` +2. `char* subsID`: subscription ID to close + +**Returns** +`JSONResponse` with null `response` if successful. An `error` message otherwise + +--- + +### `extern char* gowaku_relay_unsubscribe_from_topic(int nodeID, char* topic)` +Closes the pubsub subscription to a pubsub topic. Existing subscriptions will not be closed, but they will stop receiving messages + +**Parameters** +1. `int nodeID`: the node identifier obtained from a succesful execution of `gowaku_new` +2. `char* topic`: pubsub topic to unsubscribe. Use `NULL` for unsubscribe from the default pubsub topic + +**Returns** +`JSONResponse` with null `response` if successful. An `error` message otherwise + + + +## Waku Message Utils + +### `extern char* gowaku_encode_data(char* data, char* keyType, char* key, char* signingKey, int version)` +Encode a byte array according to [RFC 26](https://rfc.vac.dev/spec/26/). This function can be used to encrypt the payload of a waku message + +**Parameters** +1. `char* data`: byte array to encode in base64 format. (gowaku_utils_base64_encode can be used to encode the message) +2. `char* keyType`: defines the type of key to use: + - `NONE`: no encryption will be applied + - `ASYMMETRIC`: encrypt the payload using a secp256k1 public key + - `SYMMETRIC`: encrypt the payload using a 32 bit key. +3. `char* key`: hex key (`0x123...abc`) used for encrypting the `data`. + - When `version` is 0: No encryption is used + - When `version` is 1 + - If using `ASYMMETRIC` encoding, `key` must contain a secp256k1 public key to encrypt the data with + - If using `SYMMETRIC` encoding, `key` must contain a 32 bytes symmetric key. +4. `char* signingKey`: Hex string containing a secp256k1 private key to sign the encoded message, It's optional. To not sign the message use `NULL` instead. +5. `int version`: is used to define the type of payload encryption + +**Returns** +`JSONResponse` with the base64 encoded payload. An `error` message otherwise. + +--- + +### `extern char* gowaku_decode_data(char* data, char* keyType, char* key, int version)` +Decode a byte array according to [RFC 26](https://rfc.vac.dev/spec/26/). This function can be used to decrypt the payload of a waku message + +**Parameters** +1. `char* data`: byte array to decode, in base64. +2. `char* keyType`: defines the type of key to use: + - `NONE`: no encryption was used in the payload + - `ASYMMETRIC`: decrypt the payload using a secp256k1 public key + - `SYMMETRIC`: decrypt the payload using a 32 bit key. +3. `char* key`: hex key (`0x123...abc`) used for decrypting the `data`. + - When `version` is 0: No encryption is used + - When `version` is 1 + - If using `ASYMMETRIC` encoding, `key` must contain a secp256k1 private key to decrypt the data + - If using `SYMMETRIC` encoding, `key` must contain a 32 bytes symmetric key. +4. `int version`: is used to define the type of payload encryption + +**Returns** +`JSONResponse` with the decoded payload. An `error` message otherwise. The decoded payload has this format: +```json +{ + "result": { + "pubkey":"0x04123...abc", // secp256k1 public key + "signature":"0x123...abc", // secp256k1 signature + "data":"...", // base64 encoded + "padding":"..." // base64 encoded + } +} +``` + +--- + +### `extern char* gowaku_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 `gowaku_relay_publish` + +**Parameters** +1. `char* data`: byte array to encode + +**Returns** +A `char *` containing the base64 encoded byte array + +--- + +### `extern char* gowaku_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** +`JSONResponse` with the decoded payload. An `error` message otherwise. The decoded payload has this format: + + + +# Copyright + +Copyright and related rights waived via +[CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/library/api.go b/library/api.go index 10158913..38aba320 100644 --- a/library/api.go +++ b/library/api.go @@ -66,12 +66,14 @@ type WakuConfig struct { NodeKey *string `json:"nodeKey,omitempty"` KeepAliveInterval *int `json:"keepAliveInterval,omitempty"` EnableRelay *bool `json:"relay"` + MinPeersToPublish *int `json:"minPeersToPublish` } var DefaultHost = "0.0.0.0" var DefaultPort = 60000 var DefaultKeepAliveInterval = 20 var DefaultEnableRelay = true +var DefaultMinPeersToPublish = 0 func getConfig(configJSON *C.char) (WakuConfig, error) { var config WakuConfig @@ -102,6 +104,10 @@ func getConfig(configJSON *C.char) (WakuConfig, error) { config.KeepAliveInterval = &DefaultKeepAliveInterval } + if config.MinPeersToPublish == nil { + config.MinPeersToPublish = &DefaultMinPeersToPublish + } + return config, nil } @@ -155,7 +161,7 @@ func gowaku_new(configJSON *C.char) *C.char { } if *config.EnableRelay { - opts = append(opts, node.WithWakuRelay()) + opts = append(opts, node.WithWakuRelayAndMinPeers(*config.MinPeersToPublish)) } ctx := context.Background() @@ -207,9 +213,9 @@ func gowaku_stop(nodeID C.int) *C.char { return makeJSONResponse(nil) } -//export gowaku_id +//export gowaku_peerid // Obtain the peer ID of the waku node -func gowaku_id(nodeID C.int) *C.char { +func gowaku_peerid(nodeID C.int) *C.char { mutex.Lock() defer mutex.Unlock() wakuNode, ok := nodes[int(nodeID)] @@ -279,7 +285,7 @@ func gowaku_dial_peer(nodeID C.int, address *C.char, ms C.int) *C.char { //export gowaku_dial_peerid // Dial known peer by peerID. if ms > 0, cancel the function execution if it takes longer than N milliseconds -func gowaku_dial_peerid(nodeID C.int, id *C.char, ms C.int) *C.char { +func gowaku_dial_peerid(nodeID C.int, peerID *C.char, ms C.int) *C.char { mutex.Lock() defer mutex.Unlock() wakuNode, ok := nodes[int(nodeID)] @@ -290,7 +296,7 @@ func gowaku_dial_peerid(nodeID C.int, id *C.char, ms C.int) *C.char { var ctx context.Context var cancel context.CancelFunc - peerID, err := peer.Decode(C.GoString(id)) + pID, err := peer.Decode(C.GoString(peerID)) if err != nil { return makeJSONResponse(err) } @@ -302,7 +308,7 @@ func gowaku_dial_peerid(nodeID C.int, id *C.char, ms C.int) *C.char { ctx = context.Background() } - err = wakuNode.DialPeerByID(ctx, peerID) + err = wakuNode.DialPeerByID(ctx, pID) return makeJSONResponse(err) } @@ -322,7 +328,7 @@ func gowaku_close_peer(nodeID C.int, address *C.char) *C.char { //export gowaku_close_peerid // Close connection to a known peer by peerID -func gowaku_close_peerid(nodeID C.int, id *C.char) *C.char { +func gowaku_close_peerid(nodeID C.int, peerID *C.char) *C.char { mutex.Lock() defer mutex.Unlock() wakuNode, ok := nodes[int(nodeID)] @@ -330,12 +336,12 @@ func gowaku_close_peerid(nodeID C.int, id *C.char) *C.char { return makeJSONResponse(ErrWakuNodeNotReady) } - peerID, err := peer.Decode(C.GoString(id)) + pID, err := peer.Decode(C.GoString(peerID)) if err != nil { return makeJSONResponse(err) } - err = wakuNode.ClosePeerById(peerID) + err = wakuNode.ClosePeerById(pID) return makeJSONResponse(err) } @@ -355,7 +361,7 @@ func gowaku_peer_cnt(nodeID C.int) *C.char { //export gowaku_content_topic // Create a content topic string according to RFC 23 func gowaku_content_topic(applicationName *C.char, applicationVersion C.uint, contentTopicName *C.char, encoding *C.char) *C.char { - return prepareJSONResponse(protocol.NewContentTopic(C.GoString(applicationName), uint(applicationVersion), C.GoString(contentTopicName), C.GoString(encoding)).String(), nil) + return C.CString(protocol.NewContentTopic(C.GoString(applicationName), uint(applicationVersion), C.GoString(contentTopicName), C.GoString(encoding)).String()) } //export gowaku_pubsub_topic @@ -367,7 +373,7 @@ func gowaku_pubsub_topic(name *C.char, encoding *C.char) *C.char { //export gowaku_default_pubsub_topic // Get the default pubsub topic used in waku2: /waku/2/default-waku/proto func gowaku_default_pubsub_topic() *C.char { - return prepareJSONResponse(protocol.DefaultPubsubTopic().String(), nil) + return C.CString(protocol.DefaultPubsubTopic().String()) } func publish(nodeID int, message string, pubsubTopic string, ms int) (string, error) { @@ -442,16 +448,18 @@ func gowaku_set_event_callback(cb unsafe.Pointer) { } type SubscriptionMsg struct { - MessageID string `json:"messageID"` - PubsubTopic string `json:"pubsubTopic"` - Message *pb.WakuMessage `json:"wakuMessage"` + MessageID string `json:"messageID"` + SubscriptionID string `json:"subscriptionID"` + PubsubTopic string `json:"pubsubTopic"` + Message *pb.WakuMessage `json:"wakuMessage"` } -func toSubscriptionMessage(msg *protocol.Envelope) *SubscriptionMsg { +func toSubscriptionMessage(subsID string, msg *protocol.Envelope) *SubscriptionMsg { return &SubscriptionMsg{ - MessageID: hexutil.Encode(msg.Hash()), - PubsubTopic: msg.PubsubTopic(), - Message: msg.Message(), + SubscriptionID: subsID, + MessageID: hexutil.Encode(msg.Hash()), + PubsubTopic: msg.PubsubTopic(), + Message: msg.Message(), } } @@ -460,7 +468,7 @@ func toSubscriptionMessage(msg *protocol.Envelope) *SubscriptionMsg { // to the default topic. Returns a json response containing the subscription ID // or an error message. When a message is received, a "message" is emitted containing // the message, pubsub topic, and nodeID in which the message was received -func gowaku_relay_subscribe(nodeID int, topic *C.char) *C.char { +func gowaku_relay_subscribe(nodeID C.int, topic *C.char) *C.char { mutex.Lock() defer mutex.Unlock() wakuNode, ok := nodes[int(nodeID)] @@ -483,7 +491,7 @@ func gowaku_relay_subscribe(nodeID int, topic *C.char) *C.char { go func() { for envelope := range subscription.C { - send(nodeID, "message", toSubscriptionMessage(envelope)) + send(int(nodeID), "message", toSubscriptionMessage(subsID, envelope)) } }() @@ -493,7 +501,7 @@ func gowaku_relay_subscribe(nodeID int, topic *C.char) *C.char { //export gowaku_relay_unsubscribe_from_topic // Closes the pubsub subscription to a pubsub topic. Existing subscriptions // will not be closed, but they will stop receiving messages -func gowaku_relay_unsubscribe_from_topic(nodeID int, topic *C.char) *C.char { +func gowaku_relay_unsubscribe_from_topic(nodeID C.int, topic *C.char) *C.char { mutex.Lock() defer mutex.Unlock() wakuNode, ok := nodes[int(nodeID)] @@ -516,7 +524,7 @@ func gowaku_relay_unsubscribe_from_topic(nodeID int, topic *C.char) *C.char { //export gowaku_relay_close_subscription // Closes a waku relay subscription -func gowaku_relay_close_subscription(nodeID int, subsID *C.char) *C.char { +func gowaku_relay_close_subscription(nodeID C.int, subsID *C.char) *C.char { mutex.Lock() defer mutex.Unlock() wakuNode, ok := nodes[int(nodeID)] @@ -537,8 +545,8 @@ func gowaku_relay_close_subscription(nodeID int, subsID *C.char) *C.char { } //export gowaku_peers -// Retrieve the list of peers connected to the waku node -func gowaku_peers(nodeID int) *C.char { +// Retrieve the list of peers known by the waku node +func gowaku_peers(nodeID C.int) *C.char { mutex.Lock() defer mutex.Unlock() wakuNode, ok := nodes[int(nodeID)] @@ -604,8 +612,13 @@ func gowaku_encode_data(data *C.char, keyType *C.char, key *C.char, signingKey * keyInfo.PubKey = *pubK } + b, err := base64.StdEncoding.DecodeString(C.GoString(data)) + if err != nil { + return makeJSONResponse(err) + } + payload := node.Payload{ - Data: []byte(C.GoString(data)), + Data: b, Key: keyInfo, } @@ -689,7 +702,8 @@ func gowaku_utils_base64_decode(data *C.char) *C.char { // format understood by gowaku_relay_publish) func gowaku_utils_base64_encode(data *C.char) *C.char { str := base64.StdEncoding.EncodeToString([]byte(C.GoString(data))) - return prepareJSONResponse(str, nil) + return C.CString(string(str)) + } // TODO: diff --git a/library/response.go b/library/response.go index a5e5b14f..d3d27ecd 100644 --- a/library/response.go +++ b/library/response.go @@ -5,51 +5,29 @@ import ( "encoding/json" ) -const ( - codeUnknown int = iota - // special codes - codeFailedParseResponse - // codeFailedParseParams -) - var errToCodeMap = map[error]int{ //transactions.ErrInvalidTxSender: codeErrInvalidTxSender, } -type jsonrpcSuccessfulResponse struct { +type JSONResponse struct { + Error *string `json:"error,omitempty"` Result interface{} `json:"result"` } -type jsonrpcErrorResponse struct { - Error jsonError `json:"error"` -} - -type jsonError struct { - Code int `json:"code,omitempty"` - Message string `json:"message"` -} - func prepareJSONResponse(result interface{}, err error) *C.char { - code := codeUnknown - if c, ok := errToCodeMap[err]; ok { - code = c - } - return prepareJSONResponseWithCode(result, err, code) -} - -func prepareJSONResponseWithCode(result interface{}, err error, code int) *C.char { if err != nil { - errResponse := jsonrpcErrorResponse{ - Error: jsonError{Code: code, Message: err.Error()}, + errStr := err.Error() + errResponse := JSONResponse{ + Error: &errStr, } response, _ := json.Marshal(&errResponse) return C.CString(string(response)) } - data, err := json.Marshal(jsonrpcSuccessfulResponse{result}) + data, err := json.Marshal(JSONResponse{Result: result}) if err != nil { - return prepareJSONResponseWithCode(nil, err, codeFailedParseResponse) + return prepareJSONResponse(nil, err) } return C.CString(string(data)) } @@ -61,7 +39,7 @@ func makeJSONResponse(err error) *C.char { errString = &errStr } - out := APIResponse{ + out := JSONResponse{ Error: errString, } outBytes, _ := json.Marshal(out) diff --git a/library/signals.go b/library/signals.go index be6f0d40..a7bae60d 100644 --- a/library/signals.go +++ b/library/signals.go @@ -14,6 +14,13 @@ import ( "unsafe" ) +// SignalHandler defines a minimal interface +// a signal handler needs to implement. +//nolint +type SignalHandler interface { + HandleSignal(string) +} + // SignalHandler is a simple callback function that gets called when any signal is received type MobileSignalHandler func([]byte) diff --git a/library/types.go b/library/types.go deleted file mode 100644 index fda855f6..00000000 --- a/library/types.go +++ /dev/null @@ -1,74 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "strings" -) - -// APIResponse generic response from API. -type APIResponse struct { - Error *string `json:"error"` -} - -// APIDetailedResponse represents a generic response -// with possible errors. -//nolint -type APIDetailedResponse struct { - Status bool `json:"status"` - Message string `json:"message,omitempty"` - FieldErrors []APIFieldError `json:"field_errors,omitempty"` -} - -// Error string representation of APIDetailedResponse. -//nolint -func (r APIDetailedResponse) Error() string { - buf := bytes.NewBufferString("") - - for _, err := range r.FieldErrors { - buf.WriteString(err.Error() + "\n") // nolint: gas - } - - return strings.TrimSpace(buf.String()) -} - -// APIFieldError represents a set of errors -// related to a parameter. -//nolint -type APIFieldError struct { - Parameter string `json:"parameter,omitempty"` - Errors []APIError `json:"errors"` -} - -// Error string representation of APIFieldError. -func (e APIFieldError) Error() string { - if len(e.Errors) == 0 { - return "" - } - - buf := bytes.NewBufferString(fmt.Sprintf("Parameter: %s\n", e.Parameter)) - - for _, err := range e.Errors { - buf.WriteString(err.Error() + "\n") // nolint: gas - } - - return strings.TrimSpace(buf.String()) -} - -// APIError represents a single error. -//nolint -type APIError struct { - Message string `json:"message"` -} - -// Error string representation of APIError. -func (e APIError) Error() string { - return fmt.Sprintf("message=%s", e.Message) -} - -// SignalHandler defines a minimal interface -// a signal handler needs to implement. -//nolint -type SignalHandler interface { - HandleSignal(string) -} diff --git a/waku/v2/node/wakunode2.go b/waku/v2/node/wakunode2.go index 97583318..9bdd9bf5 100644 --- a/waku/v2/node/wakunode2.go +++ b/waku/v2/node/wakunode2.go @@ -39,10 +39,10 @@ import ( ) type Peer struct { - ID peer.ID - Protocols []string - Addrs []ma.Multiaddr - Connected bool + ID peer.ID `json:"peerID"` + Protocols []string `json:"protocols"` + Addrs []ma.Multiaddr `json:"addrs"` + Connected bool `json:"connected"` } type storeFactory func(w *WakuNode) store.Store