mirror of https://github.com/status-im/go-waku.git
feat: c-bindings for waku relay (#212)
This commit is contained in:
parent
21b2e1d97c
commit
df235db6b7
51
Makefile
51
Makefile
|
@ -4,7 +4,30 @@ GO_HTML_COV := ./coverage.html
|
|||
GO_TEST_OUTFILE := ./c.out
|
||||
CC_PREFIX := github.com/status-im/go-waku
|
||||
|
||||
.PHONY: all build lint test coverage build-example
|
||||
SHELL := bash # the shell used internally by Make
|
||||
|
||||
.PHONY: all build lint test coverage build-example static-library dynamic-library test-c test-c-template
|
||||
|
||||
ifeq ($(OS),Windows_NT) # is Windows_NT on XP, 2000, 7, Vista, 10...
|
||||
detected_OS := Windows
|
||||
else
|
||||
detected_OS := $(strip $(shell uname))
|
||||
endif
|
||||
|
||||
ifeq ($(detected_OS),Darwin)
|
||||
GOBIN_SHARED_LIB_EXT := dylib
|
||||
ifeq ("$(shell sysctl -nq hw.optional.arm64)","1")
|
||||
# Building on M1 is still not supported, so in the meantime we crosscompile to amd64
|
||||
GOBIN_SHARED_LIB_CFLAGS=CGO_ENABLED=1 GOOS=darwin GOARCH=amd64
|
||||
endif
|
||||
else ifeq ($(detected_OS),Windows)
|
||||
# on Windows need `--export-all-symbols` flag else expected symbols will not be found in libgowaku.dll
|
||||
GOBIN_SHARED_LIB_CGO_LDFLAGS := CGO_LDFLAGS="-Wl,--export-all-symbols"
|
||||
GOBIN_SHARED_LIB_EXT := dll
|
||||
else
|
||||
GOBIN_SHARED_LIB_EXT := so
|
||||
GOBIN_SHARED_LIB_CGO_LDFLAGS := CGO_LDFLAGS="-Wl,-soname,libgowaku.so.0"
|
||||
endif
|
||||
|
||||
all: build
|
||||
|
||||
|
@ -61,3 +84,29 @@ build-example-filter2:
|
|||
cd examples/filter2 && $(MAKE)
|
||||
|
||||
build-example: build-example-basic2 build-example-chat-2 build-example-filter2
|
||||
|
||||
static-library: ##@cross-compile Build go-waku as static library for current platform
|
||||
mkdir -p ./build/lib
|
||||
@echo "Building static library..."
|
||||
go build \
|
||||
-buildmode=c-archive \
|
||||
-o ./build/lib/libgowaku.a \
|
||||
./library/
|
||||
@echo "Static library built:"
|
||||
@ls -la ./build/lib/libgowaku.*
|
||||
|
||||
dynamic-library: ##@cross-compile Build status-go as shared library for current platform
|
||||
mkdir -p ./build/lib
|
||||
@echo "Building shared library..."
|
||||
$(GOBIN_SHARED_LIB_CFLAGS) $(GOBIN_SHARED_LIB_CGO_LDFLAGS) go build \
|
||||
-buildmode=c-shared \
|
||||
-o ./build/lib/libgowaku.$(GOBIN_SHARED_LIB_EXT) \
|
||||
./library/
|
||||
ifeq ($(detected_OS),Linux)
|
||||
cd ./build/lib && \
|
||||
ls -lah . && \
|
||||
mv ./libgowaku.$(GOBIN_SHARED_LIB_EXT) ./libgowaku.$(GOBIN_SHARED_LIB_EXT).0 && \
|
||||
ln -s ./libgowaku.$(GOBIN_SHARED_LIB_EXT).0 ./libgowaku.$(GOBIN_SHARED_LIB_EXT)
|
||||
endif
|
||||
@echo "Shared library built:"
|
||||
@ls -la ./build/lib/libgowaku.*
|
||||
|
|
|
@ -34,6 +34,12 @@ docker run go-waku:latest --help
|
|||
go get github.com/status-im/go-waku
|
||||
```
|
||||
|
||||
## C Bindings
|
||||
```
|
||||
make static-library
|
||||
make dynamic-library
|
||||
```
|
||||
|
||||
## Examples
|
||||
Examples of usage of go-waku as a library can be found in the examples folder. There is a fully featured chat example.
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
SHELL := bash # the shell used internally by Make
|
||||
|
||||
.PHONY: all build run
|
||||
|
||||
ifeq ($(OS),Windows_NT) # is Windows_NT on XP, 2000, 7, Vista, 10...
|
||||
detected_OS := Windows
|
||||
else
|
||||
detected_OS := $(strip $(shell uname))
|
||||
endif
|
||||
|
||||
all: build
|
||||
|
||||
ifeq ($(detected_OS),Linux)
|
||||
PLATFORM_FLAGS_TEST_C ?= -ldl
|
||||
else ifeq ($(detected_OS),macOS)
|
||||
PLATFORM_FLAGS_TEST_C ?= -Wl,-headerpad_max_install_names
|
||||
endif
|
||||
|
||||
build:
|
||||
cd ../../ && $(MAKE) static-library # Building library
|
||||
rm -rf build && \
|
||||
echo "Compiling 'main.c'"
|
||||
+ mkdir -p build
|
||||
$(CC) \
|
||||
-I../../build/lib/ \
|
||||
main.c \
|
||||
../../build/lib/libgowaku.a \
|
||||
-lm \
|
||||
-pthread \
|
||||
$(PLATFORM_FLAGS_TEST_C) \
|
||||
-o build/main
|
||||
|
||||
|
||||
run:
|
||||
echo "Executing './build/main.c'"
|
||||
ifeq ($(detected_OS),macOS)
|
||||
./build/main
|
||||
else ifeq ($(detected_OS),Windows)
|
||||
PATH="$(PATH_TEST)" \
|
||||
./build/main
|
||||
else
|
||||
./build/main
|
||||
endif
|
||||
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "libgowaku.h"
|
||||
#include "nxjson.c"
|
||||
#include "main.h"
|
||||
|
||||
|
||||
char *alicePrivKey = "0x4f012057e1a1458ce34189cb27daedbbe434f3df0825c1949475dec786e2c64e";
|
||||
char *alicePubKey = "0x0440f05847c4c7166f57ae8ecaaf72d31bddcbca345e26713ca9e26c93fb8362ddcd5ae7f4533ee956428ad08a89cd18b234c2911a3b1c7fbd1c0047610d987302";
|
||||
|
||||
|
||||
char *bobPrivKey = "0xb91d6b2df8fb6ef8b53b51b2b30a408c49d5e2b530502d58ac8f94e5c5de1453";
|
||||
char *bobPubKey = "0x045eef61a98ba1cf44a2736fac91183ea2bd86e67de20fe4bff467a71249a8a0c05f795dd7f28ced7c15eaa69c89d4212cc4f526ca5e9a62e88008f506d850cccd";
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *response;
|
||||
gowaku_set_event_callback(callBack);
|
||||
|
||||
char *configJSON = "{\"host\": \"0.0.0.0\", \"port\": 60000}";
|
||||
response = gowaku_new(configJSON); // configJSON can be NULL too to use defaults
|
||||
if (isError(response))
|
||||
return 1;
|
||||
int nodeID = getIntValue(response); // Obtain the nodeID from the response
|
||||
|
||||
|
||||
|
||||
response = gowaku_start(nodeID); // Start the node, enabling the waku protocols
|
||||
if (isError(response))
|
||||
return 1;
|
||||
|
||||
|
||||
|
||||
response = gowaku_id(nodeID); // Obtain the node peerID
|
||||
if (isError(response))
|
||||
return 1;
|
||||
char *nodePeerID = getStrValue(response);
|
||||
printf("PeerID: %s\n", nodePeerID);
|
||||
|
||||
|
||||
/*
|
||||
response = gowaku_dial_peer(nodeID, "/dns4/node-01.gc-us-central1-a.wakuv2.test.statusim.net/tcp/30303/p2p/16Uiu2HAmJb2e28qLXxT5kZxVUUoJt72EMzNGXB47Rxx5hw3q4YjS", 0); // Connect to a node
|
||||
if (isError(response))
|
||||
return 1;
|
||||
*/
|
||||
|
||||
|
||||
response = gowaku_relay_subscribe(nodeID, NULL);
|
||||
if (isError(response))
|
||||
return 1;
|
||||
char *subscriptionID = getStrValue(response);
|
||||
printf("SubscriptionID: %s\n", subscriptionID);
|
||||
|
||||
|
||||
|
||||
int i = 0;
|
||||
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
|
||||
if (isError(response))
|
||||
return 1;
|
||||
char *encodedData = getStrValue(response);
|
||||
|
||||
|
||||
char *contentTopic = getStrValue(gowaku_content_topic("example", 1, "default", "rfc26"));
|
||||
|
||||
|
||||
char wakuMsg[1000];
|
||||
sprintf(wakuMsg, "{\"payload\":\"%s\",\"contentTopic\":\"%s\",\"version\":%d,\"timestamp\":%d}", encodedData, contentTopic, version, i);
|
||||
|
||||
response = gowaku_relay_publish(nodeID, wakuMsg, NULL, 0); // Broadcast a message
|
||||
if (isError(response))
|
||||
return 1;
|
||||
// char *messageID = getStrValue(response);
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
response = gowaku_stop(nodeID);
|
||||
if (isError(response))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void callBack(char *signal)
|
||||
{
|
||||
// This callback will be executed each time a new message is received
|
||||
|
||||
// Example signal:
|
||||
/*{
|
||||
"nodeId":1,
|
||||
"type":"message",
|
||||
"event":{
|
||||
"messageID":"0x6496491e40dbe0b6c3a2198c2426b16301688a2daebc4f57ad7706115eac3ad1",
|
||||
"pubsubTopic":"/waku/2/default-waku/proto",
|
||||
"wakuMessage":{
|
||||
"payload":"BPABASUqWgRkgp73aW/FHIyGtJDYnStvaQvCoX9MdaNsOH39Vet0em6ipZc3lZ7kK9uFFtbJgIWfRaqTxSRjiFOPx88gXt1JeSm2SUwGSz+1gh2xTy0am8tXkc8OWSSjamdkEbXuVgAueLxHOnV3xlGwYt7nx2G5DWYqUu1BXv4yWHPOoiH2yx3fxX0OajgKGBwiMbadRNUuAUFPRM90f+bzG2y22ssHctDV/U6sXOa9ljNgpAx703Q3WIFleSRozto7ByNAdRFwWR0RGGV4l0btJXM7JpnrYcVC24dB0tJ3HVWuD0ZcwOM1zTL0wwc0hTezLHvI+f6bHSzsFGcCWIlc03KSoMjK1XENNL4dtDmSFI1DQCGgq09c2Bc3Je3Ci6XJHu+FP1F1pTnRzevv2WP8FSBJiTXpmJXdm6evB7V1Xxj4QlzQDvmHLRpBOL6PSttxf1Dc0IwC6BfZRN5g0dNmItNlS2pcY1MtZLxD5zpj",
|
||||
"contentTopic":"ABC",
|
||||
"version":1,
|
||||
"timestamp":1647826358000000000
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
const nx_json *json = nx_json_parse(signal, 0);
|
||||
const char *type = nx_json_get(json, "type")->text_value;
|
||||
|
||||
if (strcmp(type,"message") == 0){
|
||||
const char *encodedPayload = nx_json_get(nx_json_get(nx_json_get(json, "event"), "wakuMessage"), "payload")->text_value;
|
||||
int version = nx_json_get(nx_json_get(nx_json_get(json, "event"), "wakuMessage"), "version")->int_value;
|
||||
|
||||
char *decodedData = gowaku_decode_data((char*)encodedPayload, ASYMMETRIC, bobPrivKey, version);
|
||||
if(isError(decodedData)) return;
|
||||
|
||||
const nx_json *dataJson = nx_json_parse(decodedData, 0);
|
||||
const char *pubkey = nx_json_get(nx_json_get(dataJson, "result"), "pubkey")->text_value;
|
||||
const char *base64data = nx_json_get(nx_json_get(dataJson, "result"), "data")->text_value;
|
||||
char *data = gowaku_utils_base64_decode((char*)base64data);
|
||||
|
||||
printf("Received \"%s\" from %s\n", getStrValue(data), pubkey);
|
||||
fflush(stdout);
|
||||
|
||||
nx_json_free(dataJson);
|
||||
}
|
||||
|
||||
nx_json_free(json);
|
||||
}
|
||||
|
||||
bool isError(char *input)
|
||||
{
|
||||
char *jsonStr = malloc(strlen(input) + 1);
|
||||
strcpy(jsonStr, input);
|
||||
const nx_json *json = nx_json_parse(jsonStr, 0);
|
||||
bool result = false;
|
||||
if (json)
|
||||
{
|
||||
const char *errTxt = nx_json_get(json, "error")->text_value;
|
||||
result = errTxt != NULL;
|
||||
if (result)
|
||||
{
|
||||
printf("ERROR: %s\n", errTxt);
|
||||
}
|
||||
}
|
||||
nx_json_free(json);
|
||||
free(jsonStr);
|
||||
return result;
|
||||
}
|
||||
|
||||
int getIntValue(char *input)
|
||||
{
|
||||
char *jsonStr = malloc(strlen(input) + 1);
|
||||
strcpy(jsonStr, input);
|
||||
const nx_json *json = nx_json_parse(jsonStr, 0);
|
||||
int result = -1;
|
||||
if (json)
|
||||
{
|
||||
result = nx_json_get(json, "result")->int_value;
|
||||
}
|
||||
nx_json_free(json);
|
||||
free(jsonStr);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
char* getStrValue(char *input)
|
||||
{
|
||||
char *jsonStr = malloc(strlen(input) + 1);
|
||||
strcpy(jsonStr, input);
|
||||
const nx_json *json = nx_json_parse(jsonStr, 0);
|
||||
char* result = "";
|
||||
if (json)
|
||||
{
|
||||
const char* text_value = nx_json_get(json, "result")->text_value;
|
||||
result = strdup(text_value);
|
||||
}
|
||||
|
||||
nx_json_free(json);
|
||||
free(jsonStr);
|
||||
|
||||
return result;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef MAIN_H
|
||||
#define MAIN_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void callBack(char *signal);
|
||||
|
||||
bool isError(char *input);
|
||||
|
||||
int getIntValue(char *input);
|
||||
|
||||
char* getStrValue(char *input);
|
||||
|
||||
#endif /* MAIN_H */
|
|
@ -0,0 +1,387 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Yaroslav Stavnichiy <yarosla@gmail.com>
|
||||
*
|
||||
* This file is part of NXJSON.
|
||||
*
|
||||
* NXJSON is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* NXJSON is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with NXJSON. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// this file can be #included in your code
|
||||
#ifndef NXJSON_C
|
||||
#define NXJSON_C
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "nxjson.h"
|
||||
|
||||
// redefine NX_JSON_CALLOC & NX_JSON_FREE to use custom allocator
|
||||
#ifndef NX_JSON_CALLOC
|
||||
#define NX_JSON_CALLOC() calloc(1, sizeof(nx_json))
|
||||
#define NX_JSON_FREE(json) free((void*)(json))
|
||||
#endif
|
||||
|
||||
// redefine NX_JSON_REPORT_ERROR to use custom error reporting
|
||||
#ifndef NX_JSON_REPORT_ERROR
|
||||
#define NX_JSON_REPORT_ERROR(msg, p) fprintf(stderr, "NXJSON PARSE ERROR (%d): " msg " at %s\n", __LINE__, p)
|
||||
#endif
|
||||
|
||||
#define IS_WHITESPACE(c) ((unsigned char)(c)<=(unsigned char)' ')
|
||||
|
||||
static const nx_json dummy={ NX_JSON_NULL };
|
||||
|
||||
static nx_json* create_json(nx_json_type type, const char* key, nx_json* parent) {
|
||||
nx_json* js=NX_JSON_CALLOC();
|
||||
assert(js);
|
||||
js->type=type;
|
||||
js->key=key;
|
||||
if (!parent->last_child) {
|
||||
parent->child=parent->last_child=js;
|
||||
}
|
||||
else {
|
||||
parent->last_child->next=js;
|
||||
parent->last_child=js;
|
||||
}
|
||||
parent->length++;
|
||||
return js;
|
||||
}
|
||||
|
||||
void nx_json_free(const nx_json* js) {
|
||||
nx_json* p=js->child;
|
||||
nx_json* p1;
|
||||
while (p) {
|
||||
p1=p->next;
|
||||
nx_json_free(p);
|
||||
p=p1;
|
||||
}
|
||||
NX_JSON_FREE(js);
|
||||
}
|
||||
|
||||
static int unicode_to_utf8(unsigned int codepoint, char* p, char** endp) {
|
||||
// code from http://stackoverflow.com/a/4609989/697313
|
||||
if (codepoint<0x80) *p++=codepoint;
|
||||
else if (codepoint<0x800) *p++=192+codepoint/64, *p++=128+codepoint%64;
|
||||
else if (codepoint-0xd800u<0x800) return 0; // surrogate must have been treated earlier
|
||||
else if (codepoint<0x10000) *p++=224+codepoint/4096, *p++=128+codepoint/64%64, *p++=128+codepoint%64;
|
||||
else if (codepoint<0x110000) *p++=240+codepoint/262144, *p++=128+codepoint/4096%64, *p++=128+codepoint/64%64, *p++=128+codepoint%64;
|
||||
else return 0; // error
|
||||
*endp=p;
|
||||
return 1;
|
||||
}
|
||||
|
||||
nx_json_unicode_encoder nx_json_unicode_to_utf8=unicode_to_utf8;
|
||||
|
||||
static inline int hex_val(char c) {
|
||||
if (c>='0' && c<='9') return c-'0';
|
||||
if (c>='a' && c<='f') return c-'a'+10;
|
||||
if (c>='A' && c<='F') return c-'A'+10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char* unescape_string(char* s, char** end, nx_json_unicode_encoder encoder) {
|
||||
char* p=s;
|
||||
char* d=s;
|
||||
char c;
|
||||
while ((c=*p++)) {
|
||||
if (c=='"') {
|
||||
*d='\0';
|
||||
*end=p;
|
||||
return s;
|
||||
}
|
||||
else if (c=='\\') {
|
||||
switch (*p) {
|
||||
case '\\':
|
||||
case '/':
|
||||
case '"':
|
||||
*d++=*p++;
|
||||
break;
|
||||
case 'b':
|
||||
*d++='\b'; p++;
|
||||
break;
|
||||
case 'f':
|
||||
*d++='\f'; p++;
|
||||
break;
|
||||
case 'n':
|
||||
*d++='\n'; p++;
|
||||
break;
|
||||
case 'r':
|
||||
*d++='\r'; p++;
|
||||
break;
|
||||
case 't':
|
||||
*d++='\t'; p++;
|
||||
break;
|
||||
case 'u': // unicode
|
||||
if (!encoder) {
|
||||
// leave untouched
|
||||
*d++=c;
|
||||
break;
|
||||
}
|
||||
char* ps=p-1;
|
||||
int h1, h2, h3, h4;
|
||||
if ((h1=hex_val(p[1]))<0 || (h2=hex_val(p[2]))<0 || (h3=hex_val(p[3]))<0 || (h4=hex_val(p[4]))<0) {
|
||||
NX_JSON_REPORT_ERROR("invalid unicode escape", p-1);
|
||||
return 0;
|
||||
}
|
||||
unsigned int codepoint=h1<<12|h2<<8|h3<<4|h4;
|
||||
if ((codepoint & 0xfc00)==0xd800) { // high surrogate; need one more unicode to succeed
|
||||
p+=6;
|
||||
if (p[-1]!='\\' || *p!='u' || (h1=hex_val(p[1]))<0 || (h2=hex_val(p[2]))<0 || (h3=hex_val(p[3]))<0 || (h4=hex_val(p[4]))<0) {
|
||||
NX_JSON_REPORT_ERROR("invalid unicode surrogate", ps);
|
||||
return 0;
|
||||
}
|
||||
unsigned int codepoint2=h1<<12|h2<<8|h3<<4|h4;
|
||||
if ((codepoint2 & 0xfc00)!=0xdc00) {
|
||||
NX_JSON_REPORT_ERROR("invalid unicode surrogate", ps);
|
||||
return 0;
|
||||
}
|
||||
codepoint=0x10000+((codepoint-0xd800)<<10)+(codepoint2-0xdc00);
|
||||
}
|
||||
if (!encoder(codepoint, d, &d)) {
|
||||
NX_JSON_REPORT_ERROR("invalid codepoint", ps);
|
||||
return 0;
|
||||
}
|
||||
p+=5;
|
||||
break;
|
||||
default:
|
||||
// leave untouched
|
||||
*d++=c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
*d++=c;
|
||||
}
|
||||
}
|
||||
NX_JSON_REPORT_ERROR("no closing quote for string", s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char* skip_block_comment(char* p) {
|
||||
// assume p[-2]=='/' && p[-1]=='*'
|
||||
char* ps=p-2;
|
||||
if (!*p) {
|
||||
NX_JSON_REPORT_ERROR("endless comment", ps);
|
||||
return 0;
|
||||
}
|
||||
REPEAT:
|
||||
p=strchr(p+1, '/');
|
||||
if (!p) {
|
||||
NX_JSON_REPORT_ERROR("endless comment", ps);
|
||||
return 0;
|
||||
}
|
||||
if (p[-1]!='*') goto REPEAT;
|
||||
return p+1;
|
||||
}
|
||||
|
||||
static char* parse_key(const char** key, char* p, nx_json_unicode_encoder encoder) {
|
||||
// on '}' return with *p=='}'
|
||||
char c;
|
||||
while ((c=*p++)) {
|
||||
if (c=='"') {
|
||||
*key=unescape_string(p, &p, encoder);
|
||||
if (!*key) return 0; // propagate error
|
||||
while (*p && IS_WHITESPACE(*p)) p++;
|
||||
if (*p==':') return p+1;
|
||||
NX_JSON_REPORT_ERROR("unexpected chars", p);
|
||||
return 0;
|
||||
}
|
||||
else if (IS_WHITESPACE(c) || c==',') {
|
||||
// continue
|
||||
}
|
||||
else if (c=='}') {
|
||||
return p-1;
|
||||
}
|
||||
else if (c=='/') {
|
||||
if (*p=='/') { // line comment
|
||||
char* ps=p-1;
|
||||
p=strchr(p+1, '\n');
|
||||
if (!p) {
|
||||
NX_JSON_REPORT_ERROR("endless comment", ps);
|
||||
return 0; // error
|
||||
}
|
||||
p++;
|
||||
}
|
||||
else if (*p=='*') { // block comment
|
||||
p=skip_block_comment(p+1);
|
||||
if (!p) return 0;
|
||||
}
|
||||
else {
|
||||
NX_JSON_REPORT_ERROR("unexpected chars", p-1);
|
||||
return 0; // error
|
||||
}
|
||||
}
|
||||
else {
|
||||
NX_JSON_REPORT_ERROR("unexpected chars", p-1);
|
||||
return 0; // error
|
||||
}
|
||||
}
|
||||
NX_JSON_REPORT_ERROR("unexpected chars", p-1);
|
||||
return 0; // error
|
||||
}
|
||||
|
||||
static char* parse_value(nx_json* parent, const char* key, char* p, nx_json_unicode_encoder encoder) {
|
||||
nx_json* js;
|
||||
while (1) {
|
||||
switch (*p) {
|
||||
case '\0':
|
||||
NX_JSON_REPORT_ERROR("unexpected end of text", p);
|
||||
return 0; // error
|
||||
case ' ': case '\t': case '\n': case '\r':
|
||||
case ',':
|
||||
// skip
|
||||
p++;
|
||||
break;
|
||||
case '{':
|
||||
js=create_json(NX_JSON_OBJECT, key, parent);
|
||||
p++;
|
||||
while (1) {
|
||||
const char* new_key;
|
||||
p=parse_key(&new_key, p, encoder);
|
||||
if (!p) return 0; // error
|
||||
if (*p=='}') return p+1; // end of object
|
||||
p=parse_value(js, new_key, p, encoder);
|
||||
if (!p) return 0; // error
|
||||
}
|
||||
case '[':
|
||||
js=create_json(NX_JSON_ARRAY, key, parent);
|
||||
p++;
|
||||
while (1) {
|
||||
p=parse_value(js, 0, p, encoder);
|
||||
if (!p) return 0; // error
|
||||
if (*p==']') return p+1; // end of array
|
||||
}
|
||||
case ']':
|
||||
return p;
|
||||
case '"':
|
||||
p++;
|
||||
js=create_json(NX_JSON_STRING, key, parent);
|
||||
js->text_value=unescape_string(p, &p, encoder);
|
||||
if (!js->text_value) return 0; // propagate error
|
||||
return p;
|
||||
case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
|
||||
{
|
||||
js=create_json(NX_JSON_INTEGER, key, parent);
|
||||
char* pe;
|
||||
js->int_value=strtoll(p, &pe, 0);
|
||||
if (pe==p || errno==ERANGE) {
|
||||
NX_JSON_REPORT_ERROR("invalid number", p);
|
||||
return 0; // error
|
||||
}
|
||||
if (*pe=='.' || *pe=='e' || *pe=='E') { // double value
|
||||
js->type=NX_JSON_DOUBLE;
|
||||
js->dbl_value=strtod(p, &pe);
|
||||
if (pe==p || errno==ERANGE) {
|
||||
NX_JSON_REPORT_ERROR("invalid number", p);
|
||||
return 0; // error
|
||||
}
|
||||
}
|
||||
else {
|
||||
js->dbl_value=js->int_value;
|
||||
}
|
||||
return pe;
|
||||
}
|
||||
case 't':
|
||||
if (!strncmp(p, "true", 4)) {
|
||||
js=create_json(NX_JSON_BOOL, key, parent);
|
||||
js->int_value=1;
|
||||
return p+4;
|
||||
}
|
||||
NX_JSON_REPORT_ERROR("unexpected chars", p);
|
||||
return 0; // error
|
||||
case 'f':
|
||||
if (!strncmp(p, "false", 5)) {
|
||||
js=create_json(NX_JSON_BOOL, key, parent);
|
||||
js->int_value=0;
|
||||
return p+5;
|
||||
}
|
||||
NX_JSON_REPORT_ERROR("unexpected chars", p);
|
||||
return 0; // error
|
||||
case 'n':
|
||||
if (!strncmp(p, "null", 4)) {
|
||||
create_json(NX_JSON_NULL, key, parent);
|
||||
return p+4;
|
||||
}
|
||||
NX_JSON_REPORT_ERROR("unexpected chars", p);
|
||||
return 0; // error
|
||||
case '/': // comment
|
||||
if (p[1]=='/') { // line comment
|
||||
char* ps=p;
|
||||
p=strchr(p+2, '\n');
|
||||
if (!p) {
|
||||
NX_JSON_REPORT_ERROR("endless comment", ps);
|
||||
return 0; // error
|
||||
}
|
||||
p++;
|
||||
}
|
||||
else if (p[1]=='*') { // block comment
|
||||
p=skip_block_comment(p+2);
|
||||
if (!p) return 0;
|
||||
}
|
||||
else {
|
||||
NX_JSON_REPORT_ERROR("unexpected chars", p);
|
||||
return 0; // error
|
||||
}
|
||||
break;
|
||||
default:
|
||||
NX_JSON_REPORT_ERROR("unexpected chars", p);
|
||||
return 0; // error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const nx_json* nx_json_parse_utf8(char* text) {
|
||||
return nx_json_parse(text, unicode_to_utf8);
|
||||
}
|
||||
|
||||
const nx_json* nx_json_parse(char* text, nx_json_unicode_encoder encoder) {
|
||||
nx_json js={0};
|
||||
if (!parse_value(&js, 0, text, encoder)) {
|
||||
if (js.child) nx_json_free(js.child);
|
||||
return 0;
|
||||
}
|
||||
return js.child;
|
||||
}
|
||||
|
||||
const nx_json* nx_json_get(const nx_json* json, const char* key) {
|
||||
if (!json || !key) return &dummy; // never return null
|
||||
nx_json* js;
|
||||
for (js=json->child; js; js=js->next) {
|
||||
if (js->key && !strcmp(js->key, key)) return js;
|
||||
}
|
||||
return &dummy; // never return null
|
||||
}
|
||||
|
||||
const nx_json* nx_json_item(const nx_json* json, int idx) {
|
||||
if (!json) return &dummy; // never return null
|
||||
nx_json* js;
|
||||
for (js=json->child; js; js=js->next) {
|
||||
if (!idx--) return js;
|
||||
}
|
||||
return &dummy; // never return null
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NXJSON_C */
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Yaroslav Stavnichiy <yarosla@gmail.com>
|
||||
*
|
||||
* This file is part of NXJSON.
|
||||
*
|
||||
* NXJSON is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* NXJSON is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with NXJSON. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef NXJSON_H
|
||||
#define NXJSON_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
typedef enum nx_json_type {
|
||||
NX_JSON_NULL, // this is null value
|
||||
NX_JSON_OBJECT, // this is an object; properties can be found in child nodes
|
||||
NX_JSON_ARRAY, // this is an array; items can be found in child nodes
|
||||
NX_JSON_STRING, // this is a string; value can be found in text_value field
|
||||
NX_JSON_INTEGER, // this is an integer; value can be found in int_value field
|
||||
NX_JSON_DOUBLE, // this is a double; value can be found in dbl_value field
|
||||
NX_JSON_BOOL // this is a boolean; value can be found in int_value field
|
||||
} nx_json_type;
|
||||
|
||||
typedef struct nx_json {
|
||||
nx_json_type type; // type of json node, see above
|
||||
const char* key; // key of the property; for object's children only
|
||||
const char* text_value; // text value of STRING node
|
||||
long long int_value; // the value of INTEGER or BOOL node
|
||||
double dbl_value; // the value of DOUBLE node
|
||||
int length; // number of children of OBJECT or ARRAY
|
||||
struct nx_json* child; // points to first child
|
||||
struct nx_json* next; // points to next child
|
||||
struct nx_json* last_child;
|
||||
} nx_json;
|
||||
|
||||
typedef int (*nx_json_unicode_encoder)(unsigned int codepoint, char* p, char** endp);
|
||||
|
||||
extern nx_json_unicode_encoder nx_json_unicode_to_utf8;
|
||||
|
||||
const nx_json* nx_json_parse(char* text, nx_json_unicode_encoder encoder);
|
||||
const nx_json* nx_json_parse_utf8(char* text);
|
||||
void nx_json_free(const nx_json* js);
|
||||
const nx_json* nx_json_get(const nx_json* json, const char* key); // get object's property by key
|
||||
const nx_json* nx_json_item(const nx_json* json, int idx); // get array element by index
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NXJSON_H */
|
1
go.mod
1
go.mod
|
@ -11,6 +11,7 @@ require (
|
|||
github.com/ethereum/go-ethereum v1.10.13
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/gorilla/rpc v1.2.0
|
||||
github.com/ipfs/go-ds-sql v0.2.0
|
||||
github.com/ipfs/go-log v1.0.5
|
||||
|
|
|
@ -0,0 +1,703 @@
|
|||
package main
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct {
|
||||
size_t len;
|
||||
char* data;
|
||||
} ByteArray;
|
||||
|
||||
#define SYMMETRIC "Symmetric"
|
||||
#define ASYMMETRIC "Asymmetric"
|
||||
#define NONE "None"
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||
"github.com/google/uuid"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
p2pproto "github.com/libp2p/go-libp2p-core/protocol"
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
"github.com/status-im/go-waku/waku/v2/node"
|
||||
"github.com/status-im/go-waku/waku/v2/protocol"
|
||||
"github.com/status-im/go-waku/waku/v2/protocol/pb"
|
||||
"github.com/status-im/go-waku/waku/v2/protocol/relay"
|
||||
)
|
||||
|
||||
var nodes map[int]*node.WakuNode = make(map[int]*node.WakuNode)
|
||||
var subscriptions map[string]*relay.Subscription = make(map[string]*relay.Subscription)
|
||||
var mutex sync.Mutex
|
||||
|
||||
var ErrWakuNodeNotReady = errors.New("go-waku not initialized")
|
||||
|
||||
func randomHex(n int) (string, error) {
|
||||
bytes := make([]byte, n)
|
||||
if _, err := rand.Read(bytes); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(bytes), nil
|
||||
}
|
||||
|
||||
func main() {}
|
||||
|
||||
type WakuConfig struct {
|
||||
Host *string `json:"host,omitempty"`
|
||||
Port *int `json:"port,omitempty"`
|
||||
AdvertiseAddress *string `json:"advertiseAddr,omitempty"`
|
||||
NodeKey *string `json:"nodeKey,omitempty"`
|
||||
KeepAliveInterval *int `json:"keepAliveInterval,omitempty"`
|
||||
EnableRelay *bool `json:"relay"`
|
||||
}
|
||||
|
||||
var DefaultHost = "0.0.0.0"
|
||||
var DefaultPort = 60000
|
||||
var DefaultKeepAliveInterval = 20
|
||||
var DefaultEnableRelay = true
|
||||
|
||||
func getConfig(configJSON *C.char) (WakuConfig, error) {
|
||||
var config WakuConfig
|
||||
if configJSON != nil {
|
||||
err := json.Unmarshal([]byte(C.GoString(configJSON)), &config)
|
||||
if err != nil {
|
||||
return WakuConfig{}, err
|
||||
}
|
||||
}
|
||||
|
||||
if config.Host == nil {
|
||||
config.Host = &DefaultHost
|
||||
}
|
||||
|
||||
if config.EnableRelay == nil {
|
||||
config.EnableRelay = &DefaultEnableRelay
|
||||
}
|
||||
|
||||
if config.Host == nil {
|
||||
config.Host = &DefaultHost
|
||||
}
|
||||
|
||||
if config.Port == nil {
|
||||
config.Port = &DefaultPort
|
||||
}
|
||||
|
||||
if config.KeepAliveInterval == nil {
|
||||
config.KeepAliveInterval = &DefaultKeepAliveInterval
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
//export gowaku_new
|
||||
// Initialize a waku node. Receives a JSON string containing the configuration
|
||||
// for the node. It can be NULL. Example configuration:
|
||||
// ```
|
||||
// {"host": "0.0.0.0", "port": 60000, "advertiseAddr": "1.2.3.4", "nodeKey": "0x123...567", "keepAliveInterval": 20, "relay": true}
|
||||
// ```
|
||||
// All keys are optional. If not specified a default value will be set:
|
||||
// - host: IP address. Default 0.0.0.0
|
||||
// - port: TCP port to listen. Default 60000. Use 0 for random
|
||||
// - advertiseAddr: External IP
|
||||
// - nodeKey: secp256k1 private key. Default random
|
||||
// - keepAliveInterval: interval in seconds to ping all peers
|
||||
// - relay: Enable WakuRelay. Default `true`
|
||||
// This function will return a nodeID which should be used in all calls from this API that require
|
||||
// interacting with the node.
|
||||
func gowaku_new(configJSON *C.char) *C.char {
|
||||
config, err := getConfig(configJSON)
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
hostAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", *config.Host, *config.Port))
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
var prvKey *ecdsa.PrivateKey
|
||||
if config.NodeKey != nil {
|
||||
prvKey, err = crypto.HexToECDSA(*config.NodeKey)
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
} else {
|
||||
key, err := randomHex(32)
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
prvKey, err = crypto.HexToECDSA(key)
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
}
|
||||
|
||||
opts := []node.WakuNodeOption{
|
||||
node.WithPrivateKey(prvKey),
|
||||
node.WithHostAddress(hostAddr),
|
||||
node.WithKeepAlive(time.Duration(*config.KeepAliveInterval) * time.Second),
|
||||
}
|
||||
|
||||
if *config.EnableRelay {
|
||||
opts = append(opts, node.WithWakuRelay())
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
wakuNode, err := node.New(ctx, opts...)
|
||||
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
id := len(nodes) + 1
|
||||
nodes[id] = wakuNode
|
||||
|
||||
return prepareJSONResponse(id, nil)
|
||||
}
|
||||
|
||||
//export gowaku_start
|
||||
// Starts the waku node
|
||||
func gowaku_start(nodeID C.int) *C.char {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
wakuNode, ok := nodes[int(nodeID)]
|
||||
if !ok || wakuNode == nil {
|
||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
||||
}
|
||||
|
||||
if err := wakuNode.Start(); err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
return makeJSONResponse(nil)
|
||||
}
|
||||
|
||||
//export gowaku_stop
|
||||
// Stops a waku node
|
||||
func gowaku_stop(nodeID C.int) *C.char {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
wakuNode, ok := nodes[int(nodeID)]
|
||||
if !ok || wakuNode == nil {
|
||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
||||
}
|
||||
|
||||
wakuNode.Stop()
|
||||
nodes[int(nodeID)] = nil
|
||||
|
||||
return makeJSONResponse(nil)
|
||||
}
|
||||
|
||||
//export gowaku_id
|
||||
// Obtain the peer ID of the waku node
|
||||
func gowaku_id(nodeID C.int) *C.char {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
wakuNode, ok := nodes[int(nodeID)]
|
||||
if !ok || wakuNode == nil {
|
||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
||||
}
|
||||
|
||||
return prepareJSONResponse(wakuNode.ID(), nil)
|
||||
}
|
||||
|
||||
//export gowaku_listen_addresses
|
||||
// Obtain the multiaddresses the wakunode is listening to
|
||||
func gowaku_listen_addresses(nodeID C.int) *C.char {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
wakuNode, ok := nodes[int(nodeID)]
|
||||
if !ok || wakuNode == nil {
|
||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
||||
}
|
||||
|
||||
addrs, err := json.Marshal(wakuNode.ListenAddresses())
|
||||
return prepareJSONResponse(addrs, err)
|
||||
}
|
||||
|
||||
//export gowaku_add_peer
|
||||
// Add node multiaddress and protocol to the wakunode peerstore
|
||||
func gowaku_add_peer(nodeID C.int, address *C.char, protocolID *C.char) *C.char {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
wakuNode, ok := nodes[int(nodeID)]
|
||||
if !ok || wakuNode == nil {
|
||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
||||
}
|
||||
|
||||
ma, err := multiaddr.NewMultiaddr(C.GoString(address))
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
peerID, err := wakuNode.AddPeer(ma, p2pproto.ID(C.GoString(protocolID)))
|
||||
return prepareJSONResponse(peerID, err)
|
||||
}
|
||||
|
||||
//export gowaku_dial_peer
|
||||
// Dial peer at multiaddress. if ms > 0, cancel the function execution if it takes longer than N milliseconds
|
||||
func gowaku_dial_peer(nodeID C.int, address *C.char, ms C.int) *C.char {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
wakuNode, ok := nodes[int(nodeID)]
|
||||
if !ok || 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.DialPeer(ctx, C.GoString(address))
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
//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 {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
wakuNode, ok := nodes[int(nodeID)]
|
||||
if !ok || wakuNode == nil {
|
||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
||||
}
|
||||
|
||||
var ctx context.Context
|
||||
var cancel context.CancelFunc
|
||||
|
||||
peerID, err := peer.Decode(C.GoString(id))
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
if ms > 0 {
|
||||
ctx, cancel = context.WithTimeout(context.Background(), time.Duration(int(ms))*time.Millisecond)
|
||||
defer cancel()
|
||||
} else {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
err = wakuNode.DialPeerByID(ctx, peerID)
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
//export gowaku_close_peer
|
||||
// Close connection to peer at multiaddress
|
||||
func gowaku_close_peer(nodeID C.int, address *C.char) *C.char {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
wakuNode, ok := nodes[int(nodeID)]
|
||||
if !ok || wakuNode == nil {
|
||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
||||
}
|
||||
|
||||
err := wakuNode.ClosePeerByAddress(C.GoString(address))
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
//export gowaku_close_peerid
|
||||
// Close connection to a known peer by peerID
|
||||
func gowaku_close_peerid(nodeID C.int, id *C.char) *C.char {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
wakuNode, ok := nodes[int(nodeID)]
|
||||
if !ok || wakuNode == nil {
|
||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
||||
}
|
||||
|
||||
peerID, err := peer.Decode(C.GoString(id))
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
err = wakuNode.ClosePeerById(peerID)
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
//export gowaku_peer_cnt
|
||||
// Get number of connected peers
|
||||
func gowaku_peer_cnt(nodeID C.int) *C.char {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
wakuNode, ok := nodes[int(nodeID)]
|
||||
if !ok || wakuNode == nil {
|
||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
||||
}
|
||||
|
||||
return prepareJSONResponse(wakuNode.PeerCount(), nil)
|
||||
}
|
||||
|
||||
//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)
|
||||
}
|
||||
|
||||
//export gowaku_pubsub_topic
|
||||
// Create a pubsub topic string according to RFC 23
|
||||
func gowaku_pubsub_topic(name *C.char, encoding *C.char) *C.char {
|
||||
return prepareJSONResponse(protocol.NewPubsubTopic(C.GoString(name), C.GoString(encoding)).String(), nil)
|
||||
}
|
||||
|
||||
//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)
|
||||
}
|
||||
|
||||
func publish(nodeID int, message string, pubsubTopic string, ms int) (string, error) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
wakuNode, ok := nodes[nodeID]
|
||||
if !ok || wakuNode == nil {
|
||||
return "", ErrWakuNodeNotReady
|
||||
}
|
||||
|
||||
var msg pb.WakuMessage
|
||||
err := json.Unmarshal([]byte(message), &msg)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
hash, err := wakuNode.Relay().PublishToTopic(ctx, &msg, pubsubTopic)
|
||||
return hexutil.Encode(hash), err
|
||||
}
|
||||
|
||||
//export gowaku_relay_publish
|
||||
// Publish a message using waku relay. Use NULL for topic to use the default pubsub topic
|
||||
// If ms is greater than 0, the broadcast of the message must happen before the timeout
|
||||
// (in milliseconds) is reached, or an error will be returned
|
||||
func gowaku_relay_publish(nodeID C.int, messageJSON *C.char, topic *C.char, ms C.int) *C.char {
|
||||
topicToPublish := ""
|
||||
if topic != nil {
|
||||
topicToPublish = C.GoString(topic)
|
||||
} else {
|
||||
topicToPublish = protocol.DefaultPubsubTopic().String()
|
||||
}
|
||||
|
||||
hash, err := publish(int(nodeID), C.GoString(messageJSON), topicToPublish, int(ms))
|
||||
return prepareJSONResponse(hash, err)
|
||||
}
|
||||
|
||||
//export gowaku_enough_peers
|
||||
// Determine if there are enough peers to publish a message on a topic. Use NULL
|
||||
// to verify the number of peers in the default pubsub topic
|
||||
func gowaku_enough_peers(nodeID C.int, topic *C.char) *C.char {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
wakuNode, ok := nodes[int(nodeID)]
|
||||
if !ok || wakuNode == nil {
|
||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
||||
}
|
||||
|
||||
topicToCheck := protocol.DefaultPubsubTopic().String()
|
||||
if topic != nil {
|
||||
topicToCheck = C.GoString(topic)
|
||||
}
|
||||
|
||||
return prepareJSONResponse(wakuNode.Relay().EnoughPeersToPublishToTopic(topicToCheck), nil)
|
||||
}
|
||||
|
||||
//export gowaku_set_event_callback
|
||||
// Register callback to act as signal handler and receive application signal
|
||||
// (in JSON) which are used o react to asyncronous events in waku. The function
|
||||
// signature for the callback should be `void myCallback(char* signalJSON)`
|
||||
func gowaku_set_event_callback(cb unsafe.Pointer) {
|
||||
setEventCallback(cb)
|
||||
}
|
||||
|
||||
type SubscriptionMsg struct {
|
||||
MessageID string `json:"messageID"`
|
||||
PubsubTopic string `json:"pubsubTopic"`
|
||||
Message *pb.WakuMessage `json:"wakuMessage"`
|
||||
}
|
||||
|
||||
func toSubscriptionMessage(msg *protocol.Envelope) *SubscriptionMsg {
|
||||
return &SubscriptionMsg{
|
||||
MessageID: hexutil.Encode(msg.Hash()),
|
||||
PubsubTopic: msg.PubsubTopic(),
|
||||
Message: msg.Message(),
|
||||
}
|
||||
}
|
||||
|
||||
//export gowaku_relay_subscribe
|
||||
// Subscribe to a WakuRelay topic. Set the topic to NULL to subscribe
|
||||
// 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 {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
wakuNode, ok := nodes[int(nodeID)]
|
||||
if !ok || wakuNode == nil {
|
||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
||||
}
|
||||
|
||||
topicToSubscribe := protocol.DefaultPubsubTopic().String()
|
||||
if topic != nil {
|
||||
topicToSubscribe = C.GoString(topic)
|
||||
}
|
||||
|
||||
subscription, err := wakuNode.Relay().SubscribeToTopic(context.Background(), topicToSubscribe)
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
subsID := uuid.New().String()
|
||||
subscriptions[subsID] = subscription
|
||||
|
||||
go func() {
|
||||
for envelope := range subscription.C {
|
||||
send(nodeID, "message", toSubscriptionMessage(envelope))
|
||||
}
|
||||
}()
|
||||
|
||||
return prepareJSONResponse(subsID, nil)
|
||||
}
|
||||
|
||||
//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 {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
wakuNode, ok := nodes[int(nodeID)]
|
||||
if !ok || wakuNode == nil {
|
||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
||||
}
|
||||
|
||||
topicToUnsubscribe := protocol.DefaultPubsubTopic().String()
|
||||
if topic != nil {
|
||||
topicToUnsubscribe = C.GoString(topic)
|
||||
}
|
||||
|
||||
err := wakuNode.Relay().Unsubscribe(context.Background(), topicToUnsubscribe)
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
return makeJSONResponse(nil)
|
||||
}
|
||||
|
||||
//export gowaku_relay_close_subscription
|
||||
// Closes a waku relay subscription
|
||||
func gowaku_relay_close_subscription(nodeID int, subsID *C.char) *C.char {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
wakuNode, ok := nodes[int(nodeID)]
|
||||
if !ok || wakuNode == nil {
|
||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
||||
}
|
||||
|
||||
subscription, ok := subscriptions[C.GoString(subsID)]
|
||||
if !ok {
|
||||
return makeJSONResponse(errors.New("Subscription does not exist"))
|
||||
}
|
||||
|
||||
subscription.Unsubscribe()
|
||||
|
||||
delete(subscriptions, C.GoString(subsID))
|
||||
|
||||
return makeJSONResponse(nil)
|
||||
}
|
||||
|
||||
//export gowaku_peers
|
||||
// Retrieve the list of peers connected to the waku node
|
||||
func gowaku_peers(nodeID int) *C.char {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
wakuNode, ok := nodes[int(nodeID)]
|
||||
if !ok || wakuNode == nil {
|
||||
return makeJSONResponse(ErrWakuNodeNotReady)
|
||||
}
|
||||
|
||||
peers, err := wakuNode.Peers()
|
||||
return prepareJSONResponse(peers, err)
|
||||
}
|
||||
|
||||
func unmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) {
|
||||
x, y := elliptic.Unmarshal(secp256k1.S256(), pub)
|
||||
if x == nil {
|
||||
return nil, errors.New("invalid public key")
|
||||
}
|
||||
return &ecdsa.PublicKey{Curve: secp256k1.S256(), X: x, Y: y}, nil
|
||||
}
|
||||
|
||||
//export gowaku_encode_data
|
||||
// Encode a byte array. `keyType` defines the type of key to use: `NONE`,
|
||||
// `ASYMMETRIC` and `SYMMETRIC`. `version` is used to define the type of
|
||||
// payload encryption:
|
||||
// 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.
|
||||
// The `signingKey` can contain an optional secp256k1 private key to sign the
|
||||
// encoded message, otherwise NULL can be used.
|
||||
func gowaku_encode_data(data *C.char, keyType *C.char, key *C.char, signingKey *C.char, version C.int) *C.char {
|
||||
keyInfo := &node.KeyInfo{
|
||||
Kind: node.KeyKind(C.GoString(keyType)),
|
||||
}
|
||||
|
||||
keyBytes, err := hexutil.Decode(C.GoString(key))
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
if signingKey != nil {
|
||||
signingKeyBytes, err := hexutil.Decode(C.GoString(signingKey))
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
privK, err := crypto.ToECDSA(signingKeyBytes)
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
keyInfo.PrivKey = privK
|
||||
}
|
||||
|
||||
switch keyInfo.Kind {
|
||||
case node.Symmetric:
|
||||
keyInfo.SymKey = keyBytes
|
||||
case node.Asymmetric:
|
||||
pubK, err := unmarshalPubkey(keyBytes)
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
keyInfo.PubKey = *pubK
|
||||
}
|
||||
|
||||
payload := node.Payload{
|
||||
Data: []byte(C.GoString(data)),
|
||||
Key: keyInfo,
|
||||
}
|
||||
|
||||
response, err := payload.Encode(uint32(version))
|
||||
return prepareJSONResponse(response, err)
|
||||
}
|
||||
|
||||
//export gowaku_decode_data
|
||||
// Decode a byte array. `keyType` defines the type of key used: `NONE`,
|
||||
// `ASYMMETRIC` and `SYMMETRIC`. `version` is used to define the type of
|
||||
// encryption that was used in the payload:
|
||||
// When `version` is 0
|
||||
// - No encryption was used. It will return the original message payload
|
||||
// When `version` is 1
|
||||
// - If using `ASYMMETRIC` encoding, `key` must contain a secp256k1 public key
|
||||
// to decrypt the data with,
|
||||
// - If using `SYMMETRIC` encoding, `key` must contain a 32 bytes symmetric key.
|
||||
func gowaku_decode_data(data *C.char, keyType *C.char, key *C.char, version C.int) *C.char {
|
||||
b, err := base64.StdEncoding.DecodeString(C.GoString(data))
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
keyInfo := &node.KeyInfo{
|
||||
Kind: node.KeyKind(C.GoString(keyType)),
|
||||
}
|
||||
|
||||
keyBytes, err := hexutil.Decode(C.GoString(key))
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
switch keyInfo.Kind {
|
||||
case node.Symmetric:
|
||||
keyInfo.SymKey = keyBytes
|
||||
case node.Asymmetric:
|
||||
privK, err := crypto.ToECDSA(keyBytes)
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
keyInfo.PrivKey = privK
|
||||
}
|
||||
|
||||
msg := pb.WakuMessage{
|
||||
Payload: b,
|
||||
Version: uint32(version),
|
||||
}
|
||||
payload, err := node.DecodePayload(&msg, keyInfo)
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
response := struct {
|
||||
PubKey string `json:"pubkey"`
|
||||
Signature string `json:"signature"`
|
||||
Data []byte `json:"data"`
|
||||
Padding []byte `json:"padding"`
|
||||
}{
|
||||
PubKey: hexutil.Encode(crypto.FromECDSAPub(payload.PubKey)),
|
||||
Signature: hexutil.Encode(payload.Signature),
|
||||
Data: payload.Data,
|
||||
Padding: payload.Padding,
|
||||
}
|
||||
|
||||
return prepareJSONResponse(response, err)
|
||||
}
|
||||
|
||||
//export gowaku_utils_base64_decode
|
||||
// Decode a base64 string (useful for reading the payload from waku messages)
|
||||
func gowaku_utils_base64_decode(data *C.char) *C.char {
|
||||
b, err := base64.StdEncoding.DecodeString(C.GoString(data))
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
return prepareJSONResponse(string(b), nil)
|
||||
}
|
||||
|
||||
//export gowaku_utils_base64_encode
|
||||
// Encode data to base64 (useful for creating the payload of a waku message in the
|
||||
// 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)
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// connected/disconnected
|
||||
// dns discovery
|
||||
// func gowaku_relay_publish_msg(msg C.WakuMessage, pubsubTopic *C.char, ms C.int) *C.char
|
||||
// getFastestPeer(protocol)
|
||||
// getRandomPeer(protocol)
|
||||
// func (wakuLP *WakuLightPush) PublishToTopic(ctx context.Context, message *pb.WakuMessage, topic string, peer, requestId nil) ([]byte, error) {
|
||||
// func (wakuLP *WakuLightPush) Publish(ctx context.Context, message *pb.WakuMessage, peer, requestId nil) ([]byte, error) {
|
||||
// func (query)
|
|
@ -0,0 +1,12 @@
|
|||
// +build darwin,cgo
|
||||
|
||||
package main
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
extern bool StatusServiceSignalEvent( const char *jsonEvent );
|
||||
*/
|
||||
import "C"
|
|
@ -0,0 +1,70 @@
|
|||
package main
|
||||
|
||||
import "C"
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
const (
|
||||
codeUnknown int = iota
|
||||
// special codes
|
||||
codeFailedParseResponse
|
||||
// codeFailedParseParams
|
||||
)
|
||||
|
||||
var errToCodeMap = map[error]int{
|
||||
//transactions.ErrInvalidTxSender: codeErrInvalidTxSender,
|
||||
}
|
||||
|
||||
type jsonrpcSuccessfulResponse struct {
|
||||
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()},
|
||||
}
|
||||
response, _ := json.Marshal(&errResponse)
|
||||
return C.CString(string(response))
|
||||
}
|
||||
|
||||
data, err := json.Marshal(jsonrpcSuccessfulResponse{result})
|
||||
if err != nil {
|
||||
return prepareJSONResponseWithCode(nil, err, codeFailedParseResponse)
|
||||
}
|
||||
return C.CString(string(data))
|
||||
}
|
||||
|
||||
func makeJSONResponse(err error) *C.char {
|
||||
var errString *string = nil
|
||||
if err != nil {
|
||||
errStr := err.Error()
|
||||
errString = &errStr
|
||||
}
|
||||
|
||||
out := APIResponse{
|
||||
Error: errString,
|
||||
}
|
||||
outBytes, _ := json.Marshal(out)
|
||||
|
||||
return C.CString(string(outBytes))
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// ======================================================================================
|
||||
// cgo compilation (for desktop platforms and local tests)
|
||||
// ======================================================================================
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include "_cgo_export.h"
|
||||
|
||||
typedef void (*callback)(const char *jsonEvent);
|
||||
callback gCallback = 0;
|
||||
|
||||
bool StatusServiceSignalEvent(const char *jsonEvent) {
|
||||
if (gCallback) {
|
||||
gCallback(jsonEvent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetEventCallback(void *cb) {
|
||||
gCallback = (callback)cb;
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package main
|
||||
|
||||
/*
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
extern bool StatusServiceSignalEvent(const char *jsonEvent);
|
||||
extern void SetEventCallback(void *cb);
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// SignalHandler is a simple callback function that gets called when any signal is received
|
||||
type MobileSignalHandler func([]byte)
|
||||
|
||||
// storing the current mobile signal handler here
|
||||
var mobileSignalHandler MobileSignalHandler
|
||||
|
||||
// SignalEnvelope is a general signal sent upward from node to app
|
||||
type SignalEnvelope struct {
|
||||
NodeID int `json:"nodeId"`
|
||||
Type string `json:"type"`
|
||||
Event interface{} `json:"event"`
|
||||
}
|
||||
|
||||
// NewEnvelope creates new envlope of given type and event payload.
|
||||
func NewEnvelope(nodeId int, typ string, event interface{}) *SignalEnvelope {
|
||||
return &SignalEnvelope{
|
||||
NodeID: nodeId,
|
||||
Type: typ,
|
||||
Event: event,
|
||||
}
|
||||
}
|
||||
|
||||
// send sends application signal (in JSON) upwards to application (via default notification handler)
|
||||
func send(node int, typ string, event interface{}) {
|
||||
signal := NewEnvelope(node, typ, event)
|
||||
data, err := json.Marshal(&signal)
|
||||
if err != nil {
|
||||
fmt.Println("marshal signal error", err)
|
||||
return
|
||||
}
|
||||
// If a Go implementation of signal handler is set, let's use it.
|
||||
if mobileSignalHandler != nil {
|
||||
mobileSignalHandler(data)
|
||||
} else {
|
||||
// ...and fallback to C implementation otherwise.
|
||||
str := C.CString(string(data))
|
||||
C.StatusServiceSignalEvent(str)
|
||||
C.free(unsafe.Pointer(str))
|
||||
}
|
||||
}
|
||||
|
||||
// SetMobileSignalHandler setup geth callback to notify about new signal
|
||||
// used for gomobile builds
|
||||
//nolint
|
||||
func SetMobileSignalHandler(handler SignalHandler) {
|
||||
mobileSignalHandler = func(data []byte) {
|
||||
if len(data) > 0 {
|
||||
handler.HandleSignal(string(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setEventCallback(cb unsafe.Pointer) {
|
||||
C.SetEventCallback(cb)
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
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)
|
||||
}
|
|
@ -45,7 +45,6 @@ type KeyInfo struct {
|
|||
SymKey []byte // If the encryption is Symmetric, a Symmetric key must be specified
|
||||
PubKey ecdsa.PublicKey // If the encryption is Asymmetric, the public key of the message receptor must be specified
|
||||
PrivKey *ecdsa.PrivateKey // Set a privkey if the message requires a signature
|
||||
|
||||
}
|
||||
|
||||
// Encode encodes a payload depending on the version parameter.
|
||||
|
|
|
@ -27,7 +27,7 @@ import (
|
|||
const clientId string = "Go Waku v2 node"
|
||||
|
||||
// Default minRelayPeersToPublish
|
||||
const defaultMinRelayPeersToPublish = 1
|
||||
const defaultMinRelayPeersToPublish = 0
|
||||
|
||||
type WakuNodeParameters struct {
|
||||
hostAddr *net.TCPAddr
|
||||
|
|
Loading…
Reference in New Issue