diff --git a/examples/android-kotlin/app/src/main/java/com/example/waku/Node.kt b/examples/android-kotlin/app/src/main/java/com/example/waku/Node.kt index ee885830..522f2d05 100644 --- a/examples/android-kotlin/app/src/main/java/com/example/waku/Node.kt +++ b/examples/android-kotlin/app/src/main/java/com/example/waku/Node.kt @@ -7,6 +7,7 @@ import com.example.waku.events.MessageEvent import com.example.waku.messages.Message import com.example.waku.store.StoreQuery import com.example.waku.store.StoreResponse +import com.example.waku.filter.FilterSubscription import gowaku.Gowaku import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString @@ -315,7 +316,7 @@ fun Node.peers(): List { * Query message history * @param query Query * @param peerID PeerID to ask the history from. Use "" to automatically select a peer - * @param ms If ms is greater than 0, the broadcast of the message must happen before the timeout + * @param ms If ms is greater than 0, response must be received before the timeout * (in milliseconds) is reached, or an error will be returned * @return Response containing the messages and cursor for pagination. Use the cursor in further queries to retrieve more results */ @@ -324,3 +325,28 @@ fun Node.storeQuery(query: StoreQuery, peerID: String = "", ms: Long = 0): Store val response = Gowaku.storeQuery(queryJSON, peerID, ms) return handleResponse(response) } + +/** + * Creates a subscription in a lightnode for messages + * @param filter Filter criteria + * @param peerID PeerID to subscribe to. Use "" to automatically select a peer + * @param ms If ms is greater than 0, the subscription must be done before the timeout + * (in milliseconds) is reached, or an error will be returned + */ +func Node.filterSubscribe(filter: FilterSubscription, peerID: String = "", ms: Long = 0) { + val filterJSON = Json.encodeToString(filter) + val response = Gowaku.filterSubscribe(filterJSON, peerID, ms) + handleResponse(response) +} + +/** + * Removes subscriptions in a light node + * @param filter Filter criteria + * @param ms If ms is greater than 0, the unsubscription must be done before the timeout + * (in milliseconds) is reached, or an error will be returned + */ +func Node.filterUnsubscribe(filter: FilterSubscription, ms: Long = 0) { + val filterJSON = Json.encodeToString(filter) + val response = Gowaku.filterUnsubscribe(filterJSON, ms) + handleResponse(response) +} diff --git a/examples/android-kotlin/app/src/main/java/com/example/waku/filter/ContentFilter.kt b/examples/android-kotlin/app/src/main/java/com/example/waku/filter/ContentFilter.kt new file mode 100644 index 00000000..6113bbe4 --- /dev/null +++ b/examples/android-kotlin/app/src/main/java/com/example/waku/filter/ContentFilter.kt @@ -0,0 +1,6 @@ +package com.example.waku.filter + +import kotlinx.serialization.Serializable + +@Serializable +data class ContentFilter(val contentTopic: String) \ No newline at end of file diff --git a/examples/android-kotlin/app/src/main/java/com/example/waku/filter/FilterSubscription.kt b/examples/android-kotlin/app/src/main/java/com/example/waku/filter/FilterSubscription.kt new file mode 100644 index 00000000..0d34b528 --- /dev/null +++ b/examples/android-kotlin/app/src/main/java/com/example/waku/filter/FilterSubscription.kt @@ -0,0 +1,9 @@ +package com.example.waku.filter + +import kotlinx.serialization.Serializable + +@Serializable +data class FilterSubscription( + var contentFilters: List, + var topic: String?, +) \ No newline at end of file diff --git a/examples/android-kotlin/app/src/main/java/com/example/waku/store/StoreQuery.kt b/examples/android-kotlin/app/src/main/java/com/example/waku/store/StoreQuery.kt index 3f181c31..75ef8bc0 100644 --- a/examples/android-kotlin/app/src/main/java/com/example/waku/store/StoreQuery.kt +++ b/examples/android-kotlin/app/src/main/java/com/example/waku/store/StoreQuery.kt @@ -8,6 +8,6 @@ data class StoreQuery( var pubsubTopic: String? = DefaultPubsubTopic(), var startTime: Long? = null, var endTime: Long? = null, - var contentFilter: List?, + var contentFilters: List?, var pagingOptions: PagingOptions? ) \ No newline at end of file diff --git a/examples/waku-csharp/waku-csharp/Waku.Node.cs b/examples/waku-csharp/waku-csharp/Waku.Node.cs index 8d28df5b..ace0d555 100644 --- a/examples/waku-csharp/waku-csharp/Waku.Node.cs +++ b/examples/waku-csharp/waku-csharp/Waku.Node.cs @@ -13,6 +13,7 @@ namespace Waku public int? keepAliveInterval { get; set; } public bool? relay { get; set; } public int? minPeersToPublish {get; set; } + public bool? enableFilter {get; set; } } public enum EventType @@ -109,6 +110,12 @@ namespace Waku public PagingOptions? pagingInfo { get; set; } } + public class FilterSubscription + { + public IList contentFilters { get; set; } = new List(); + public string? topic { get; set; } + } + public class Node { private bool _running; @@ -525,7 +532,7 @@ namespace Waku /// /// Query /// PeerID to ask the history from. Use NULL to automatically select a peer - /// 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 + /// If ms is greater than 0, the response must be received before the timeout (in milliseconds) is reached, or an error will be returned /// Response containing the messages and cursor for pagination. Use the cursor in further queries to retrieve more results public StoreResponse StoreQuery(StoreQuery query, string? peerID = null, int ms = 0) { @@ -535,8 +542,36 @@ namespace Waku return Response.HandleStoreResponse(ptr, "could not extract query response"); } + [DllImport(Constants.dllName)] + internal static extern IntPtr waku_filter_subscribe(string filterJSON, string? peerID, int ms); + /// + /// Creates a subscription in a lightnode for messages + /// + /// Filter criteria + /// PeerID to subscribe to + /// If ms is greater than 0, the subscription must be done before the timeout (in milliseconds) is reached, or an error will be returned + public FilterSubscribe(FilterSubscription filter, string? peerID = null, int ms = 0) + { + string filterJSON = JsonSerializer.Serialize(filter); + IntPtr ptr = waku_filter_subscribe(filterJSON, peerID, ms); + + Response.HandleResponse(ptr); + } - + [DllImport(Constants.dllName)] + internal static extern IntPtr waku_filter_unsubscribe(string filterJSON, int ms); + /// + /// Removes subscriptions in a light node + /// + /// Filter criteria + /// If ms is greater than 0, the unsubscription must be done before the timeout (in milliseconds) is reached, or an error will be returned + public FilterUnsubscribe(FilterSubscription filter, int ms = 0) + { + string filterJSON = JsonSerializer.Serialize(filter); + IntPtr ptr = waku_filter_unsubscribe(filterJSON, ms); + + Response.HandleResponse(ptr); + } } } diff --git a/library/README.md b/library/README.md index 91a89d70..506c7c7f 100644 --- a/library/README.md +++ b/library/README.md @@ -4,557 +4,1061 @@ This specification describes the API for consuming go-waku when built as a dynam # libgowaku.h +# Introduction + +Native applications that wish to integrate Waku may not be able to use 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. + +This specification describes the C API that SHOULD be implemented by native Waku library and that SHOULD be used to +consume them. + +# Design requirements + +The API should be generic enough, so: + +- it can be implemented by both nwaku and go-waku C-Bindings, +- it can be consumed from a variety of languages such as C#, Kotlin, Swift, Rust, C++, etc. + +The selected format to pass data to and from the API is `JSON`. + +It has been selected due to its widespread usage and easiness of use. Other alternatives MAY replace it in the future (C +structure, protobuf) if it brings limitations that need to be lifted. + +# The API + ## General +### `JsonResponse` type -### 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: -```js -// On failure: -{ "error": "the error message" } +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 success: -{ "result": ... } // result format depends on the function response -``` +On failure: -## 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: -```js +```ts { - "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 + error: string; } ``` -### `extern void waku_set_event_callback(void* cb)` -Register callback to act as signal 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)` +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. + +### `JsonMessage` type + +A Waku Message in JSON Format: + +```ts +{ + payload: string; + contentTopic: string; + version: number; + timestamp: number; +} +``` + +Fields: + +- `payload`: base64 encoded payload, [`waku_utils_base64_encode`](#extern-char-waku_utils_base64_encodechar-data) can be used for this. +- `contentTopic`: The content topic to be set on the message. +- `version`: The Waku Message version number. +- `timestamp`: Unix timestamp in nanoseconds. + +### `DecodedPayload` type + +A payload once decoded, used when a received Waku Message is encrypted: + +```ts +interface DecodedPayload { + pubkey?: string; + signature?: string; + data: string; + padding: string; + } +``` + +Fields: + +- `pubkey`: Public key that signed the message (optional), hex encoded with `0x` prefix, +- `signature`: Message signature (optional), hex encoded with `0x` prefix, +- `data`: Decrypted message payload base64 encoded, +- `padding`: Padding base64 encoded. + +### `FilterSubscription` type + +The criteria to create subscription to a light node in JSON Format: + +```ts +{ + contentFilters: ContentFilter[]; + topic: string?; +} +``` + +Fields: + +- `contentFilters`: Array of [`ContentFilter`](#contentfilter-type) being subscribed to / unsubscribed from. +- `topic`: Optional message topic. + + +### `ContentFilter` type + +```ts +{ + contentTopic: string; +} +``` + +Fields: + +- `contentTopic`: The content topic of a waku message. + +### `StoreQuery` type + +Criteria used to retreive historical messages + +```ts +interface StoreQuery { + pubsubTopic?: string; + contentFilters?: ContentFilter[]; + startTime?: number; + endTime?: number; + pagingOptions?: PagingOptions + } +``` + +Fields: + +- `pubsubTopic`: The pubsub topic on which messages are published. +- `contentFilters`: Array of [`ContentFilter`](#contentfilter-type) to query for historical messages, +- `startTime`: The inclusive lower bound on the timestamp of queried messages. This field holds the Unix epoch time in nanoseconds. +- `endTime`: The inclusive upper bound on the timestamp of queried messages. This field holds the Unix epoch time in nanoseconds. +- `pagingOptions`: Paging information in [`PagingOptions`](#pagingoptions-type) format. + +### `StoreResponse` type + +The response received after doing a query to a store node: + +```ts +interface StoreResponse { + messages: JsonMessage[]; + pagingOptions?: PagingOptions; + } +``` +Fields: + +- `messages`: Array of retrieved historical messages in [`JsonMessage`](#jsonmessage-type) format. +- `pagingOption`: Paging information in [`PagingOptions`](#pagingoptions-type) format from which to resume further historical queries + +### `PagingOptions` type + +```ts +interface PagingOptions { + pageSize: number; + cursor?: Index; + forward: bool; + } +``` +Fields: + +- `pageSize`: Number of messages to retrieve per page. +- `cursor`: Message Index from which to perform pagination. If not included and forward is set to true, paging will be performed from the beginning of the list. If not included and forward is set to false, paging will be performed from the end of the list. +- `forward`: `true` if paging forward, `false` if paging backward + +### `Index` type + +```ts +interface Index { + digest: string; + receiverTime: number; + senderTime: number; + pubsubTopic: string; + } +``` + +Fields: + +- `digest`: Hash of the message at this [`Index`](#index-type). +- `receiverTime`: UNIX timestamp in nanoseconds at which the message at this [`Index`](#index-type) was received +- `senderTime`: UNIX timestamp in nanoseconds at which the message is generated by its sender. +- `pubsubTopic`: The pubsub topic of the message at this [`Index`](#index-type). + +## 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 of type `JsonSignal`. + +### `JsonSignal` type + +```ts +{ + type: string; + event: any; +} +``` + +Fields: + +- `type`: Type of signal being emitted. Currently, only `message` is available. +- `event`: Format depends on the type of signal. + +For example: + +```json +{ + "type": "message", + "event": { + "subscriptionId": 1, + "pubsubTopic": "/waku/2/default-waku/proto", + "messageId": "0x6496491e40dbe0b6c3a2198c2426b16301688a2daebc4f57ad7706115eac3ad1", + "wakuMessage": { + "payload": "TODO", + "contentTopic": "/my-app/1/notification/proto", + "version": 1, + "timestamp": 1647826358000000000 + } + } +} +``` + +| `type` | `event` Type | +|:----------|--------------------| +| `message` | `JsonMessageEvent` | + +### `JsonMessageEvent` type + +Type of `event` field for a `message` event: + +```ts +{ + pubsubTopic: string; + messageId: string; + wakuMessage: JsonMessage; +} +``` + +- `pubsubTopic`: The pubsub topic on which the message was received. +- `messageId`: The message id. +- `wakuMessage`: The message in [`JsonMessage`](#jsonmessage-type) format. + +### `extern void waku_set_event_callback(void* cb)` + +Register callback to act as event handler and receive application signals, +which are used to react to asynchronous 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* jsonSignal)` ## Node management -### `extern char* waku_new(char* configJSON)` -Initialize a go-waku node. +### `JsonConfig` type -**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 - ```js - // 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` +Type holding a node configuration: -**Returns** -`JSONResponse` with a NULL `result`. An `error` message otherwise - ---- - -### `extern char* waku_start()` -Initialize a go-waku node mounting all the protocols that were enabled during the waku node initialization. - -**Returns** -`JSONResponse` containing a null `result` if the function executes successfully. An `error` message otherwise - ---- - -### `extern char* waku_stop()` -Stops a go-waku node - -**Returns** -`JSONResponse` containing a null `result` if the function executes successfully. An `error` message otherwise - ---- - -### `extern char* waku_peerid()` -Obtain the peer ID of the go-waku node. - -**Returns** -`JSONResponse` containing the peer ID (base58 encoded) if the function executes successfully. An `error` message otherwise - ---- - -### `extern char* waku_listen_addresses()` -Obtain the multiaddresses the wakunode is listening to - -**Returns** -`JSONResponse` containing an array of multiaddresses if the function executes successfully. An `error` message otherwise - - -## Connecting to peers - -### `extern char* waku_add_peer(char* address, char* protocolID)` -Add node multiaddress and protocol to the wakunode peerstore - -**Parameters** -1. `char* address`: multiaddress of the peer being added -2. `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* waku_connect(char* address, int ms)` -Connect to peer at multiaddress. - -**Parameters** -1. `char* address`: multiaddress of the peer being dialed -2. `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* waku_connect_peerid(char* id, int ms)` -Connect to peer using peerID. - -**Parameters** -1. `char* peerID`: peerID to dial. The peer must be already known. It must have been added before with `waku_add_peer` or previously dialed with `waku_dial_peer` -2. `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* waku_disconnect(char* peerID)` -Disconnect a peer using its peerID. - -**Parameters** -1. `char* peerID`: peerID to disconnect. - -**Returns** -`JSONResponse` with a null `result` if the function executes successfully. An `error` message otherwise - ---- - -### `extern char* waku_peer_cnt()` -Obtain number of connected peers - -**Returns** -`JSONResponse` containing an `int` with the number of connected peers. An `error` message otherwise - ---- - -### `extern char* waku_peers()` -Retrieve the list of peers known by the go-waku node - -**Returns** -`JSONResponse` containing a list of peers. An `error` message otherwise. The list of peers has this format: -```js -{ - "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 - } - ] +```ts +interface JsonSignal { + host?: string; + port?: number; + advertiseAddr?: string; + nodeKey?: string; + keepAliveInterval?: number; + relay?: boolean; + minPeersToPublish?: number + filter?: boolean; } ``` +Fields: + +All fields are optional. +If a key is `undefined`, or `null`, a default value will be set. + +- `host`: Listening IP address. + Default `0.0.0.0`. +- `port`: Libp2p TCP listening port. + Default `60000`. + Use `0` for random. +- `advertiseAddr`: External address to advertise to other nodes. + Can be ip4, ip6 or dns4, dns6. + If `null`, the multiaddress(es) generated from the ip and port specified in the config (or default ones) will be used. + Default: `null`. +- `nodeKey`: Secp256k1 private key in Hex format (`0x123...abc`). + Default random. +- `keepAliveInterval`: Interval in seconds for pinging peers to keep the connection alive. + Default `20`. +- `relay`: Enable relay protocol. + Default `true`. +- `minPeersToPublish`: The minimum number of peers required on a topic to allow broadcasting a message. + Default `0`. +- `filter`: Enable filter protocol. + Default `false`. + +For example: +```json +{ + "host": "0.0.0.0", + "port": 60000, + "advertiseAddr": "1.2.3.4", + "nodeKey": "0x123...567", + "keepAliveInterval": 20, + "relay": true, + "minPeersToPublish": 0 +} +``` + +### `extern char* waku_new(char* jsonConfig)` + +Instantiates a Waku node. + +**Parameters** + +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. + +**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_start()` + +Start a Waku node mounting all the protocols that were enabled during the Waku node instantiation. + +**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_stop()` + +Stops a Waku node. + +**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_peerid()` + +Get the peer ID of the waku node. + +**Returns** + +A [`JsonResponse`](#jsonresponse-type). +If the execution is successful, the `result` field contains the peer ID as a `string` (base58 encoded). + +For example: + +```json +{ + "result": "QmWjHKUrXDHPCwoWXpUZ77E8o6UbAoTTZwf1AD1tDC4KNP" +} +``` + +### `extern char* waku_listen_addresses()` + +Get the multiaddresses the Waku node is listening to. + +**Returns** + +A [`JsonResponse`](#jsonresponse-type). +If the execution is successful, the `result` field contains an 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)` + +Add a node multiaddress and protocol to the waku node's peerstore. + +**Parameters** + +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. + +**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. + +For example: + +```json +{ + "result": "QmWjHKUrXDHPCwoWXpUZ77E8o6UbAoTTZwf1AD1tDC4KNP" +} +``` + +### `extern char* waku_connect_peer(char* address, int timeoutMs)` + +Dial peer using a multiaddress. + +**Parameters** + +1. `char* address`: A multiaddress to reach the peer being dialed. +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. + +**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_connect_peerid(char* peerId, int timeoutMs)` + +Dial peer using its peer ID. + +**Parameters** + +1`char* peerID`: Peer ID to dial. + The peer must be already known. + It must have been added before with [`waku_add_peer`](#extern-char-waku_add_peerchar-address-char-protocolid) + or previously dialed with [`waku_connect_peer`](#extern-char-waku_connect_peerchar-address-int-timeoutms). +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. + +**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_disconnect_peer(char* peerId)` + +Disconnect a peer using its peerID + +**Parameters** + +1. `char* peerID`: Peer ID to disconnect. + +**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_peer_count()` + +Get number of connected peers. + +**Returns** + +A [`JsonResponse`](#jsonresponse-type). +If the execution is successful, the `result` field contains an `integer` which represents the number of connected peers. + +For example: + +```json +{ + "result": 0 +} +``` + +### `extern char* waku_peers()` + +Retrieve the list of peers known by the Waku node. + +**Returns** + +A [`JsonResponse`](#jsonresponse-type) containing a list of peers. +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* waku_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/) + +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/) + +`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* waku_pubsub_topic(char* name, char* encoding)` -Create a pubsub topic string according to [RFC 23](https://rfc.vac.dev/spec/23/) + +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/) + +`char *` containing a content topic formatted according to [RFC 23](https://rfc.vac.dev/spec/23/). + ``` /waku/2/{topic-name}/{encoding} ``` ---- - ### `extern char* waku_default_pubsub_topic()` -Returns the default pubsub topic used for exchanging waku messages defined in [RFC 10](https://rfc.vac.dev/spec/10/) + +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* waku_relay_publish(char* messageJson, char* pubsubTopic, int timeoutMs)` -### `extern char* waku_relay_publish(char* messageJSON, char* topic, int ms)` -Publish a message using waku relay. +Publish a message using Waku Relay. **Parameters** -1. `char* messageJSON`: json string containing the [Waku Message](https://rfc.vac.dev/spec/14/) - ```js - { - "payload":"", // base64 encoded payload. waku_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 + +1. `char* messageJson`: JSON string containing the [Waku Message](https://rfc.vac.dev/spec/14/) as [`JsonMessage`](#jsonmessage-type). +2. `char* pubsubTopic`: pubsub topic on which to publish the message. + If `NULL`, it uses the default pubsub topic. +3. `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. + +Note: `messageJson.version` is overwritten to `0`. **Returns** -`JSONResponse` containing the message ID. An `error` message otherwise ---- +A [`JsonResponse`](#jsonresponse-type). +If the execution is successful, the `result` field contains the message ID. -### `extern char* waku_relay_publish_enc_asymmetric(char* messageJSON, char* topic, char* publicKey, char* optionalSigningKey, int ms)` -Publish a message encrypted with a secp256k1 public key using waku relay +### `extern char* waku_relay_publish_enc_asymmetric(char* messageJson, char* pubsubTopic, char* publicKey, char* optionalSigningKey, int timeoutMs)` + +Optionally sign, +encrypt using asymmetric encryption +and publish a message using Waku Relay. **Parameters** -1. `char* messageJSON`: json string containing the [Waku Message](https://rfc.vac.dev/spec/14/) - ```js - { - "payload":"", // base64 encoded payload. waku_utils_base64_encode can be used for this - "contentTopic: "...", - "version": 1, - "timestamp": 1647963508000000000 // Unix timestamp in nanoseconds - } - ``` -2. `char* topic`: pubsub topic. Set to `NULL` to use the default pubsub topic -3. `char* publicKey`: hex string prefixed with "0x" containing a valid secp256k1 public key. -4. `char* optionalSigningKey`: optional hex string prefixed with "0x" containing a valid secp256k1 private key for signing the message. Use NULL otherwise -5. `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 + +1. `char* messageJson`: JSON string containing the [Waku Message](https://rfc.vac.dev/spec/14/) as [`JsonMessage`](#jsonmessage-type). +2. `char* pubsubTopic`: pubsub topic on which to publish the message. + If `NULL`, it uses the default pubsub topic. +3. `char* publicKey`: hex encoded public key to be used for encryption. +4. `char* optionalSigningKey`: hex encoded private key to be used to sign the message. +5. `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. + +Note: `messageJson.version` is overwritten to `1`. **Returns** -`JSONResponse` containing the message ID. An `error` message otherwise ---- +A [`JsonResponse`](#jsonresponse-type). +If the execution is successful, the `result` field contains the message ID. -### `extern char* waku_relay_publish_enc_symmetric(char* messageJSON, char* topic, char* symmetricKey, char* optionalSigningKey, int ms)` -Publish a message encrypted with a 32 bytes symmetric key using waku relay +### `extern char* waku_relay_publish_enc_symmetric(char* messageJson, char* pubsubTopic, char* symmetricKey, char* optionalSigningKey, int timeoutMs)` + +Optionally sign, +encrypt using symmetric encryption +and publish a message using Waku Relay. **Parameters** -1. `char* messageJSON`: json string containing the [Waku Message](https://rfc.vac.dev/spec/14/) - ```js - { - "payload":"", // base64 encoded payload. waku_utils_base64_encode can be used for this - "contentTopic: "...", - "version": 1, - "timestamp": 1647963508000000000 // Unix timestamp in nanoseconds - } - ``` -2. `char* topic`: pubsub topic. Set to `NULL` to use the default pubsub topic -3. `char* symmetricKey`: hex string prefixed with "0x" containing a 32 bytes symmetric key -4. `char* optionalSigningKey`: optional hex string prefixed with "0x" containing a valid secp256k1 private key for signing the message. Use NULL otherwise -5. `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 + +1. `char* messageJson`: JSON string containing the [Waku Message](https://rfc.vac.dev/spec/14/) as [`JsonMessage`](#jsonmessage-type). +2. `char* pubsubTopic`: pubsub topic on which to publish the message. + If `NULL`, it uses the default pubsub topic. +3. `char* symmetricKey`: hex encoded secret key to be used for encryption. +4. `char* optionalSigningKey`: hex encoded private key to be used to sign the message. +5. `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. + +Note: `messageJson.version` is overwritten to `1`. **Returns** -`JSONResponse` containing the message ID. An `error` message otherwise ---- +A [`JsonResponse`](#jsonresponse-type). +If the execution is successful, the `result` field contains the message ID. -### `extern char* waku_enough_peers(char* topic)` -Determine if there are enough peers to publish a message on a topic. +### `extern char* waku_relay_enough_peers(char* pubsubTopic)` + +Determine if there are enough peers to publish a message on a given pubsub topic. **Parameters** -1. `char* topic`: pubsub topic to verify. Use `NULL` to verify the number of peers in the default pubsub topic + +1. `char* pubsubTopic`: Pubsub topic to verify. + If `NULL`, it verifies 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 ---- +A [`JsonResponse`](#jsonresponse-type). +If the execution is successful, the `result` field contains a `boolean` indicating whether there are enough peers. + +For example: + +```json +{ + "result": true +} +``` ### `extern char* waku_relay_subscribe(char* topic)` -Subscribe to a WakuRelay topic to receive messages. + +Subscribe to a Waku Relay pubsub topic to receive messages. **Parameters** -1. `char* topic`: pubsub topic to subscribe to. Use `NULL` for subscribing to the default pubsub topic +1. `char* topic`: Pubsub topic to subscribe to. + If `NULL`, it subscribes to the default pubsub topic. **Returns** -`JSONResponse` with a null result. An `error` message otherwise + +A [`JsonResponse`](#jsonresponse-type). +If the execution is successful, the `result` field is set to `true`. + +For example: + +```json +{ + "result": true +} +``` **Events** -When a message is received, a ``"message"` event` is emitted containing the message, and pubsub topic in which the message was received. Here's an example event that could be received: -```js + +When a message is received, a ``"message"` event` is emitted containing the message, pubsub topic, and node ID in which +the message was received. + +The `event` type is [`JsonMessageEvent`](#jsonmessageevent-type). + +For Example: + +```json { - "type":"message", - "event":{ - "pubsubTopic":"/waku/2/default-waku/proto", - "messageID":"0x6496491e40dbe0b6c3a2198c2426b16301688a2daebc4f57ad7706115eac3ad1", - "wakuMessage":{ - "payload":"...", // base64 encoded message. Use waku_decode_data to decode - "contentTopic":"ABC", - "version":1, - "timestamp":1647826358000000000 // in nanoseconds + "type": "message", + "event": { + "subscriptionID": 1, + "pubsubTopic": "/waku/2/default-waku/proto", + "messageID": "0x6496491e40dbe0b6c3a2198c2426b16301688a2daebc4f57ad7706115eac3ad1", + "wakuMessage": { + "payload": "TODO", + "contentTopic": "/my-app/1/notification/proto", + "version": 1, + "timestamp": 1647826358000000000 } } } ``` ---- - ### `extern char* waku_relay_unsubscribe(char* topic)` -Closes the subscription to a pubsub topic. + +Closes the pubsub subscription to a pubsub topic. No more messages will be received +from this pubsub topic. **Parameters** -1. `char* topic`: pubsub topic to unsubscribe from. Use `NULL` for unsubscribe from the default pubsub topic + +1. `char* pusubTopic`: Pubsub topic to unsubscribe from. + If `NULL`, unsubscribes from the default pubsub topic. **Returns** -`JSONResponse` with null `response` if successful. An `error` message otherwise + +A [`JsonResponse`](#jsonresponse-type). +If the execution is successful, the `result` field is set to `true`. + +For example: + +```json +{ + "result": true +} +``` -## Waku LightPush +## Waku Filter +### `extern char* waku_filter_subscribe(char* filterJSON, char* peerID, int ms)` -### `extern char* waku_lightpush_publish(char* messageJSON, char* topic, char* peerID, int ms)` -Publish a message using waku lightpush. +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). **Parameters** -1. `char* messageJSON`: json string containing the [Waku Message](https://rfc.vac.dev/spec/14/) - ```js - { - "payload":"", // base64 encoded payload. waku_utils_base64_encode can be used for this - "contentTopic: "...", - "version": 1, - "timestamp": 1647963508000000000 // Unix timestamp in nanoseconds + +1. `char* filterJSON`: JSON string containing the [`FilterSubscription`](#filtersubscription-type) to unsubscribe from +2. `char* peerID`: Peer ID to subscribe to. + The peer must be already known. + It must have been added before with [`waku_add_peer`](#extern-char-waku_add_peerchar-address-char-protocolid) + or previously dialed with [`waku_connect_peer`](#extern-char-waku_connect_peerchar-address-int-timeoutms). + Use `NULL` to automatically select a node. +3. `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. + +**Returns** + +A [`JsonResponse`](#jsonresponse-type). +If the execution is successful, the `result` field is set to `true`. + +For example: + +```json +{ + "result": true +} +``` + +**Events** + +When a message is received, a ``"message"` event` is emitted containing the message, pubsub topic, and node ID in which +the message was received. + +The `event` type is [`JsonMessageEvent`](#jsonmessageevent-type). + +For Example: + +```json +{ + "type": "message", + "event": { + "subscriptionID": 1, + "pubsubTopic": "/waku/2/default-waku/proto", + "messageID": "0x6496491e40dbe0b6c3a2198c2426b16301688a2daebc4f57ad7706115eac3ad1", + "wakuMessage": { + "payload": "TODO", + "contentTopic": "/my-app/1/notification/proto", + "version": 1, + "timestamp": 1647826358000000000 } - ``` -2. `char* topic`: pubsub topic. Set to `NULL` to use the default pubsub topic -3. `char* peerID`: should contain the ID of a peer supporting the lightpush protocol. Use NULL to automatically select a node -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* waku_filter_unsubscribe(char* filterJSON, int timeoutMs)` ---- - -### `extern char* waku_lightpush_publish_enc_asymmetric(char* messageJSON, char* topic, char* publicKey, char* optionalSigningKey, char* peerID, int ms)` -Publish a message encrypted with a secp256k1 public key using waku lightpush +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). **Parameters** -1. `char* messageJSON`: json string containing the [Waku Message](https://rfc.vac.dev/spec/14/) - ```js - { - "payload":"", // base64 encoded payload. waku_utils_base64_encode can be used for this - "contentTopic: "...", - "version": 1, - "timestamp": 1647963508000000000 // Unix timestamp in nanoseconds - } - ``` -2. `char* topic`: pubsub topic. Set to `NULL` to use the default pubsub topic -3. `char* publicKey`: hex string prefixed with "0x" containing a valid secp256k1 public key. -4. `char* optionalSigningKey`: optional hex string prefixed with "0x" containing a valid secp256k1 private key for signing the message. Use NULL otherwise -5. `char* peerID`: should contain the ID of a peer supporting the lightpush protocol. Use NULL to automatically select a node -6. `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 + +1. `char* filterJSON`: JSON string containing the [`FilterSubscription`](#filtersubscription-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. **Returns** -`JSONResponse` containing the message ID. An `error` message otherwise ---- +A [`JsonResponse`](#jsonresponse-type). +If the execution is successful, the `result` field is set to `true`. -### `extern char* waku_lightpush_publish_enc_symmetric(char* messageJSON, char* topic, char* symmetricKey, char* optionalSigningKey, char* peerID, int ms)` -Publish a message encrypted with a 32 bytes symmetric key using waku relay +For example: + +```json +{ + "result": true +} +``` + +## Waku Lightpush + +### `extern char* waku_lightpush_publish(char* messageJSON, char* topic, char* peerID, int timeoutMs)` + +Publish a message using Waku Lightpush. **Parameters** -1. `char* messageJSON`: json string containing the [Waku Message](https://rfc.vac.dev/spec/14/) - ```js - { - "payload":"", // base64 encoded payload. waku_utils_base64_encode can be used for this - "contentTopic: "...", - "version": 1, - "timestamp": 1647963508000000000 // Unix timestamp in nanoseconds - } - ``` -2. `char* topic`: pubsub topic. Set to `NULL` to use the default pubsub topic -3. `char* symmetricKey`: hex string prefixed with "0x" containing a 32 bytes symmetric key -4. `char* optionalSigningKey`: optional hex string prefixed with "0x" containing a valid secp256k1 private key for signing the message. Use NULL otherwise -5. `char* peerID`: should contain the ID of a peer supporting the lightpush protocol. Use NULL to automatically select a node -6. `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 + +1. `char* messageJson`: JSON string containing the [Waku Message](https://rfc.vac.dev/spec/14/) as [`JsonMessage`](#jsonmessage-type). +2. `char* pubsubTopic`: pubsub topic on which to publish the message. + If `NULL`, it uses the default pubsub topic. +3. `char* peerID`: Peer ID supporting the lightpush protocol. + The peer must be already known. + It must have been added before with [`waku_add_peer`](#extern-char-waku_add_peerchar-address-char-protocolid) + or previously dialed with [`waku_connect_peer`](#extern-char-waku_connect_peerchar-address-int-timeoutms). + Use `NULL` to automatically select a node. +3. `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. + +Note: `messageJson.version` is overwritten to `0`. **Returns** -`JSONResponse` containing the message ID. An `error` message otherwise +A [`JsonResponse`](#jsonresponse-type). +If the execution is successful, the `result` field contains the message ID. + +### `extern char* waku_lightpush_publish_enc_asymmetric(char* messageJson, char* pubsubTopic, char* peerID, char* publicKey, char* optionalSigningKey, int timeoutMs)` + +Optionally sign, +encrypt using asymmetric encryption +and publish a message using Waku Lightpush. + +**Parameters** + +1. `char* messageJson`: JSON string containing the [Waku Message](https://rfc.vac.dev/spec/14/) as [`JsonMessage`](#jsonmessage-type). +2. `char* pubsubTopic`: pubsub topic on which to publish the message. + If `NULL`, it uses the default pubsub topic. +3. `char* peerID`: Peer ID supporting the lightpush protocol. + The peer must be already known. + It must have been added before with [`waku_add_peer`](#extern-char-waku_add_peerchar-address-char-protocolid) + or previously dialed with [`waku_connect_peer`](#extern-char-waku_connect_peerchar-address-int-timeoutms). +4. `char* publicKey`: hex encoded public key to be used for encryption. +5. `char* optionalSigningKey`: hex encoded private key to be used to sign the message. +6. `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. + +Note: `messageJson.version` is overwritten to `1`. + +**Returns** + +A [`JsonResponse`](#jsonresponse-type). +If the execution is successful, the `result` field contains the message ID. + +### `extern char* waku_lightpush_publish_enc_symmetric(char* messageJson, char* pubsubTopic, char* peerID, char* symmetricKey, char* optionalSigningKey, int timeoutMs)` + +Optionally sign, +encrypt using symmetric encryption +and publish a message using Waku Lightpush. + +**Parameters** + +1. `char* messageJson`: JSON string containing the [Waku Message](https://rfc.vac.dev/spec/14/) as [`JsonMessage`](#jsonmessage-type). +2. `char* pubsubTopic`: pubsub topic on which to publish the message. + If `NULL`, it uses the default pubsub topic. +3. `char* peerID`: Peer ID supporting the lightpush protocol. + The peer must be already known. + It must have been added before with [`waku_add_peer`](#extern-char-waku_add_peerchar-address-char-protocolid) + or previously dialed with [`waku_connect_peer`](#extern-char-waku_connect_peerchar-address-int-timeoutms). +4. `char* symmetricKey`: hex encoded secret key to be used for encryption. +5. `char* optionalSigningKey`: hex encoded private key to be used to sign the message. +6. `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. + +Note: `messageJson.version` is overwritten to `1`. + +**Returns** + +A [`JsonResponse`](#jsonresponse-type). +If the execution is successful, the `result` field contains the message ID. ## Waku Store +### `extern char* waku_store_query(char* queryJSON, char* peerID, int timeoutMs)` -### `extern char* waku_store_query(char* queryJSON, char* peerID, int ms)` -Query historic messages using waku store protocol. +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 +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 query. If the message length is greater than 0, this function should be executed again, setting the `cursor` attribute with the cursor returned in the response -```js -{ - "pubsubTopic": "...", // optional string - "startTime": 1234, // optional, unix epoch time in nanoseconds - "endTime": 1234, // optional, unix epoch time in nanoseconds - "contentFilters": [ // optional - "contentTopic1", "contentTopic2" ... - ], - "pagingOptions": { // optional pagination information - "pageSize": 40, // number - "cursor": { // optional - "digest": ..., - "receiverTime": ..., - "senderTime": ..., - "pubsubTopic" ..., - }, - "forward": true, // sort order - } -} -``` -2. `char* peerID`: should contain the ID of a peer supporting the store protocol. Use NULL to automatically select a node -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 + +1. `char* queryJSON`: JSON string containing the [`StoreQuery`](#storequery-type). +2. `char* peerID`: Peer ID supporting the store protocol. + The peer must be already known. + It must have been added before with [`waku_add_peer`](#extern-char-waku_add_peerchar-address-char-protocolid) + or previously dialed with [`waku_connect_peer`](#extern-char-waku_connect_peerchar-address-int-timeoutms). +3. `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. **Returns** -`JSONResponse` containing the store response. An `error` message otherwise -```js -{ - "result": { - "messages": [ ... ], // array of waku messages - "pagingOptions": { // optional pagination information - "pageSize": 40, // number - "cursor": { // optional - "digest": ..., - "receiverTime": ..., - "senderTime": ..., - "pubsubTopic" ..., - }, - "forward": true, // sort order - } - } -} -``` + +A [`JsonResponse`](#jsonresponse-type). +If the execution is successful, the `result` field contains a [`StoreResponse`](#storeresponse-type).. + ## Decrypting messages -### `extern char* waku_decode_symmetric(char* messageJSON, char* symmetricKey)` +### `extern char* waku_decode_symmetric(char* messageJson, char* symmetricKey)` Decrypt a message using a symmetric key **Parameters** -1. `char* messageJSON`: json string containing the [Waku Message](https://rfc.vac.dev/spec/14/) - ```js - { - "payload":"...", // encrypted payload encoded in base64. - "contentTopic: "...", - "version": 1, - "timestamp": 1647963508000000000 // Unix timestamp in nanoseconds - } - ``` -2. `char* symmetricKey`: 32 byte symmetric key + +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. + +Note: `messageJson.version` is expected to be `1`. **Returns** -`JSONResponse` containing a `DecodedPayload`. An `error` message otherwise -```js + +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. + +```json { "result": { - "pubkey": "0x......", // pubkey that signed the message (optional) - "signature": "0x....", // message signature (optional) - "data": "...", // decrypted message payload encoded in base64 - "padding": "...", // base64 encoded padding + "pubkey": "0x......", + "signature": "0x....", + "data": "...", + "padding": "..." } } - ``` -### `extern char* waku_decode_asymmetric(char* messageJSON, char* privateKey)` +### `extern char* waku_decode_asymmetric(char* messageJson, char* privateKey)` Decrypt a message using a secp256k1 private key **Parameters** -1. `char* messageJSON`: json string containing the [Waku Message](https://rfc.vac.dev/spec/14/) - ```js - { - "payload":"...", // encrypted payload encoded in base64. - "contentTopic: "...", - "version": 1, - "timestamp": 1647963508000000000 // Unix timestamp in nanoseconds - } - ``` -2. `char* privateKey`: secp256k1 private key + +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. + +Note: `messageJson.version` is expected to be `1`. **Returns** -`JSONResponse` containing a `DecodedPayload`. An `error` message otherwise -```js + +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. + +```json { "result": { - "pubkey": "0x......", // pubkey that signed the message (optional) - "signature": "0x....", // message signature (optional) - "data": "...", // decrypted message payload encoded in base64 - "padding": "...", // base64 encoded padding + "pubkey": "0x......", + "signature": "0x....", + "data": "...", + "padding": "..." } } ``` -## Waku Message Utils - +## 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` + +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 ---- +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) + +Decode a base64 string (useful for reading the payload from Waku Messages). **Parameters** -1. `char* data`: base64 encoded byte array to decode - + +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: ---- -### `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 [`JsonResponse`](#jsonresponse-type). +If the execution is successful, the `result` field contains the decoded payload. # Copyright diff --git a/library/api_filter.go b/library/api_filter.go new file mode 100644 index 00000000..ef7c3a88 --- /dev/null +++ b/library/api_filter.go @@ -0,0 +1,44 @@ +package main + +import ( + "C" + + mobile "github.com/status-im/go-waku/mobile" +) + +//export waku_filter_subscribe +// Creates a subscription to a light node matching a content filter and, optionally, a pubSub topic. +// filterJSON must contain a JSON with this format: +// { +// "contentFilters": [ // mandatory +// { +// "contentTopic": "the content topic" +// }, ... +// ], +// "topic": "the pubsub topic" // optional +// } +// peerID should contain the ID of a peer supporting the filter protocol. Use NULL to automatically select a node +// If ms is greater than 0, the subscription must happen before the timeout +// (in milliseconds) is reached, or an error will be returned +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) +} + +//export waku_filter_unsubscribe +// Removes subscriptions in a light node matching a content filter and, optionally, a pubSub topic. +// filterJSON must contain a JSON with this format: +// { +// "contentFilters": [ // mandatory +// { +// "contentTopic": "the content topic" +// }, ... +// ], +// "topic": "the pubsub topic" // optional +// } +// If ms is greater than 0, the subscription must happen before the timeout +// (in milliseconds) is reached, or an error will be returned +func waku_filter_unsubscribe(filterJSON *C.char, ms C.int) *C.char { + response := mobile.FilterUnsubscribe(C.GoString(filterJSON), int(ms)) + return C.CString(response) +} diff --git a/mobile/api.go b/mobile/api.go index be48fd7d..0e67c37d 100644 --- a/mobile/api.go +++ b/mobile/api.go @@ -44,6 +44,7 @@ type wakuConfig struct { NodeKey *string `json:"nodeKey,omitempty"` KeepAliveInterval *int `json:"keepAliveInterval,omitempty"` EnableRelay *bool `json:"relay"` + EnableFilter *bool `json:"filter"` MinPeersToPublish *int `json:"minPeersToPublish"` } @@ -52,6 +53,7 @@ var defaultPort = 60000 var defaultKeepAliveInterval = 20 var defaultEnableRelay = true var defaultMinPeersToPublish = 0 +var defaultEnableFilter = false func getConfig(configJSON string) (wakuConfig, error) { var config wakuConfig @@ -70,6 +72,10 @@ func getConfig(configJSON string) (wakuConfig, error) { config.EnableRelay = &defaultEnableRelay } + if config.EnableFilter == nil { + config.EnableFilter = &defaultEnableFilter + } + if config.Host == nil { config.Host = &defaultHost } @@ -131,6 +137,10 @@ func NewNode(configJSON string) string { opts = append(opts, node.WithWakuRelayAndMinPeers(*config.MinPeersToPublish)) } + if *config.EnableFilter { + opts = append(opts, node.WithWakuFilter(false)) + } + ctx := context.Background() w, err := node.New(ctx, opts...) diff --git a/mobile/api_filter.go b/mobile/api_filter.go new file mode 100644 index 00000000..e1b62fe8 --- /dev/null +++ b/mobile/api_filter.go @@ -0,0 +1,106 @@ +package gowaku + +import ( + "context" + "encoding/json" + "time" + + "github.com/libp2p/go-libp2p-core/peer" + "github.com/status-im/go-waku/waku/v2/protocol/filter" + "github.com/status-im/go-waku/waku/v2/protocol/pb" +) + +type FilterArgument struct { + Topic string `json:"topic,omitempty"` + ContentFilters []pb.ContentFilter `json:"contentFilters,omitempty"` +} + +func toContentFilter(filterJSON string) (filter.ContentFilter, error) { + var f FilterArgument + err := json.Unmarshal([]byte(filterJSON), &f) + if err != nil { + return filter.ContentFilter{}, err + } + + result := filter.ContentFilter{ + Topic: f.Topic, + } + for _, cf := range f.ContentFilters { + result.ContentTopics = append(result.ContentTopics, cf.ContentTopic) + } + + return result, err +} + +func FilterSubscribe(filterJSON string, peerID string, ms int) string { + cf, err := toContentFilter(filterJSON) + if err != nil { + return makeJSONResponse(err) + } + + if wakuNode == nil { + return makeJSONResponse(errWakuNodeNotReady) + } + + var ctx context.Context + var cancel context.CancelFunc + + if ms > 0 { + ctx, cancel = context.WithTimeout(context.Background(), time.Duration(int(ms))*time.Millisecond) + defer cancel() + } else { + ctx = context.Background() + } + + var fOptions []filter.FilterSubscribeOption + if peerID != "" { + p, err := peer.Decode(peerID) + if err != nil { + return makeJSONResponse(err) + } + fOptions = append(fOptions, filter.WithPeer(p)) + } else { + fOptions = append(fOptions, filter.WithAutomaticPeerSelection()) + } + + _, f, err := wakuNode.Filter().Subscribe(ctx, cf, fOptions...) + if err != nil { + return makeJSONResponse(err) + } + + go func(f filter.Filter) { + for envelope := range f.Chan { + send("message", toSubscriptionMessage(envelope)) + } + }(f) + + return prepareJSONResponse(true, nil) +} + +func FilterUnsubscribe(filterJSON string, ms int) string { + cf, err := toContentFilter(filterJSON) + if err != nil { + return makeJSONResponse(err) + } + + if wakuNode == nil { + return makeJSONResponse(errWakuNodeNotReady) + } + + var ctx context.Context + var cancel context.CancelFunc + + if ms > 0 { + ctx, cancel = context.WithTimeout(context.Background(), time.Duration(int(ms))*time.Millisecond) + defer cancel() + } else { + ctx = context.Background() + } + + err = wakuNode.Filter().UnsubscribeFilter(ctx, cf) + if err != nil { + return makeJSONResponse(err) + } + + return makeJSONResponse(nil) +} diff --git a/mobile/api_relay.go b/mobile/api_relay.go index 83892233..bd5cde08 100644 --- a/mobile/api_relay.go +++ b/mobile/api_relay.go @@ -12,8 +12,8 @@ import ( "github.com/status-im/go-waku/waku/v2/protocol/relay" ) -var subscriptions map[string]*relay.Subscription = make(map[string]*relay.Subscription) -var mutex sync.Mutex +var relaySubscriptions map[string]*relay.Subscription = make(map[string]*relay.Subscription) +var relaySubsMutex sync.Mutex func RelayEnoughPeers(topic string) string { if wakuNode == nil { @@ -86,10 +86,10 @@ func RelaySubscribe(topic string) string { topicToSubscribe := getTopic(topic) - mutex.Lock() - defer mutex.Unlock() + relaySubsMutex.Lock() + defer relaySubsMutex.Unlock() - _, ok := subscriptions[topicToSubscribe] + _, ok := relaySubscriptions[topicToSubscribe] if ok { return makeJSONResponse(nil) } @@ -99,7 +99,7 @@ func RelaySubscribe(topic string) string { return makeJSONResponse(err) } - subscriptions[topicToSubscribe] = subscription + relaySubscriptions[topicToSubscribe] = subscription go func(subscription *relay.Subscription) { for envelope := range subscription.C { @@ -117,17 +117,17 @@ func RelayUnsubscribe(topic string) string { topicToUnsubscribe := getTopic(topic) - mutex.Lock() - defer mutex.Unlock() + relaySubsMutex.Lock() + defer relaySubsMutex.Unlock() - subscription, ok := subscriptions[topicToUnsubscribe] + subscription, ok := relaySubscriptions[topicToUnsubscribe] if ok { return makeJSONResponse(nil) } subscription.Unsubscribe() - delete(subscriptions, topicToUnsubscribe) + delete(relaySubscriptions, topicToUnsubscribe) err := wakuNode.Relay().Unsubscribe(context.Background(), topicToUnsubscribe) if err != nil { diff --git a/mobile/response.go b/mobile/response.go index cf799baf..c87772b3 100644 --- a/mobile/response.go +++ b/mobile/response.go @@ -27,13 +27,16 @@ func prepareJSONResponse(result interface{}, err error) string { func makeJSONResponse(err error) string { var errString *string = nil + result := true if err != nil { errStr := err.Error() errString = &errStr + result = false } out := jsonResponse{ - Error: errString, + Error: errString, + Result: result, } outBytes, _ := json.Marshal(out)