mirror of https://github.com/waku-org/nwaku.git
* feat(cbindings): first commit - waku relay (#1632) * test_app.nim: fix compilation issue. App.init(..) -> App.new(..) * Simplifying library name (libwaku) and standardizing function names (waku_*) * Proper wrapper of the waku_node API and creation of the libwaku.a * Rolling back changes that are not needed * Rolling back changes that are out of the scope of this task * wakunode.nim: Removing unnecessary import * Aplying PR suggestions * Renaming 'waku.h' -> 'libwaku.h' * Use of 'isNil' instead of '== nil' * libwaku.nim: explicitly setting waku_poll() as gcsafe
This commit is contained in:
parent
2ec9809cf3
commit
2defbd2301
|
@ -53,3 +53,6 @@ nimbus-build-system.paths
|
||||||
*.sqlite3
|
*.sqlite3
|
||||||
*.sqlite3-shm
|
*.sqlite3-shm
|
||||||
*.sqlite3-wal
|
*.sqlite3-wal
|
||||||
|
|
||||||
|
# Ignore autogenerated C-bingings compilation files
|
||||||
|
/examples/cbindings/libwaku.h
|
||||||
|
|
39
Makefile
39
Makefile
|
@ -280,24 +280,31 @@ docker-push:
|
||||||
docker push $(DOCKER_IMAGE_NAME)
|
docker push $(DOCKER_IMAGE_NAME)
|
||||||
|
|
||||||
|
|
||||||
##############
|
################
|
||||||
## Wrappers ##
|
## C Bindings ##
|
||||||
##############
|
################
|
||||||
# TODO: Remove unused target
|
.PHONY: cbindings cwaku_example libwaku.a
|
||||||
libwaku.so: | build deps deps2
|
|
||||||
|
libwaku.a: | build deps
|
||||||
echo -e $(BUILD_MSG) "build/$@" && \
|
echo -e $(BUILD_MSG) "build/$@" && \
|
||||||
$(ENV_SCRIPT) nim c --app:lib --noMain --nimcache:nimcache/libwaku $(NIM_PARAMS) -o:build/$@.0 wrappers/libwaku.nim && \
|
$(ENV_SCRIPT) nim libwaku $(NIM_PARAMS) $(EXPERIMENTAL_PARAMS) waku.nims
|
||||||
rm -f build/$@ && \
|
|
||||||
ln -s $@.0 build/$@
|
|
||||||
|
|
||||||
# libraries for dynamic linking of non-Nim objects
|
libwaku.so: | build deps
|
||||||
EXTRA_LIBS_DYNAMIC := -L"$(CURDIR)/build" -lwaku -lm
|
# TODO: pending to enhance this part. Kindly use the static approach.
|
||||||
|
echo -e $(BUILD_MSG) "build/$@" && \
|
||||||
|
$(ENV_SCRIPT) nim c --app:lib --opt:size --noMain --header -o:build/$@ library/cwakunode.nim
|
||||||
|
|
||||||
# TODO: Remove unused target
|
cbindings: | build libwaku.a
|
||||||
wrappers: | build deps librln libwaku.so
|
|
||||||
echo -e $(BUILD_MSG) "build/C_wrapper_example" && \
|
cwaku_example: | build cbindings
|
||||||
$(CC) wrappers/wrapper_example.c -Wl,-rpath,'$$ORIGIN' $(EXTRA_LIBS_DYNAMIC) -g -o build/C_wrapper_example
|
echo -e $(BUILD_MSG) "build/$@" && \
|
||||||
echo -e $(BUILD_MSG) "build/go_wrapper_example" && \
|
cp nimcache/release/libwaku/libwaku.h ./examples/cbindings/ && \
|
||||||
go build -ldflags "-linkmode external -extldflags '$(EXTRA_LIBS_DYNAMIC)'" -o build/go_wrapper_example wrappers/wrapper_example.go #wrappers/cfuncs.go
|
cc -o "build/$@" \
|
||||||
|
./examples/cbindings/waku_example.c \
|
||||||
|
-lwaku -Lbuild/ -pthread -ldl -lm \
|
||||||
|
-lminiupnpc -Lvendor/nim-nat-traversal/vendor/miniupnp/miniupnpc/build/ \
|
||||||
|
-lnatpmp -Lvendor/nim-nat-traversal/vendor/libnatpmp-upstream/ \
|
||||||
|
vendor/nim-libbacktrace/libbacktrace_wrapper.o \
|
||||||
|
vendor/nim-libbacktrace/install/usr/lib/libbacktrace.a
|
||||||
|
|
||||||
endif # "variables.mk" was not included
|
endif # "variables.mk" was not included
|
||||||
|
|
|
@ -0,0 +1,602 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Nim's Runtime Library
|
||||||
|
(c) Copyright 2015 Andreas Rumpf
|
||||||
|
|
||||||
|
See the file "copying.txt", included in this
|
||||||
|
distribution, for details about the copyright.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* compiler symbols:
|
||||||
|
__BORLANDC__
|
||||||
|
_MSC_VER
|
||||||
|
__WATCOMC__
|
||||||
|
__LCC__
|
||||||
|
__GNUC__
|
||||||
|
__DMC__
|
||||||
|
__POCC__
|
||||||
|
__TINYC__
|
||||||
|
__clang__
|
||||||
|
__AVR__
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef NIMBASE_H
|
||||||
|
#define NIMBASE_H
|
||||||
|
|
||||||
|
/*------------ declaring a custom attribute to support using LLVM's Address Sanitizer ------------ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
This definition exists to provide support for using the LLVM ASAN (Address SANitizer) tooling with Nim. This
|
||||||
|
should only be used to mark implementations of the GC system that raise false flags with the ASAN tooling, or
|
||||||
|
for functions that are hot and need to be disabled for performance reasons. Based on the official ASAN
|
||||||
|
documentation, both the clang and gcc compilers are supported. In addition to that, a check is performed to
|
||||||
|
verify that the necessary attribute is supported by the compiler.
|
||||||
|
|
||||||
|
To flag a proc as ignored, append the following code pragma to the proc declaration:
|
||||||
|
{.codegenDecl: "CLANG_NO_SANITIZE_ADDRESS $# $#$#".}
|
||||||
|
|
||||||
|
For further information, please refer to the official documentation:
|
||||||
|
https://github.com/google/sanitizers/wiki/AddressSanitizer
|
||||||
|
*/
|
||||||
|
#define CLANG_NO_SANITIZE_ADDRESS
|
||||||
|
#if defined(__clang__)
|
||||||
|
# if __has_attribute(no_sanitize_address)
|
||||||
|
# undef CLANG_NO_SANITIZE_ADDRESS
|
||||||
|
# define CLANG_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------ ignore typical warnings in Nim-generated files ------------- */
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
# pragma GCC diagnostic ignored "-Wpragmas"
|
||||||
|
# pragma GCC diagnostic ignored "-Wwritable-strings"
|
||||||
|
# pragma GCC diagnostic ignored "-Winvalid-noreturn"
|
||||||
|
# pragma GCC diagnostic ignored "-Wformat"
|
||||||
|
# pragma GCC diagnostic ignored "-Wlogical-not-parentheses"
|
||||||
|
# pragma GCC diagnostic ignored "-Wlogical-op-parentheses"
|
||||||
|
# pragma GCC diagnostic ignored "-Wshadow"
|
||||||
|
# pragma GCC diagnostic ignored "-Wunused-function"
|
||||||
|
# pragma GCC diagnostic ignored "-Wunused-variable"
|
||||||
|
# pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
||||||
|
# pragma GCC diagnostic ignored "-Wtautological-compare"
|
||||||
|
# pragma GCC diagnostic ignored "-Wswitch-bool"
|
||||||
|
# pragma GCC diagnostic ignored "-Wmacro-redefined"
|
||||||
|
# pragma GCC diagnostic ignored "-Wincompatible-pointer-types-discards-qualifiers"
|
||||||
|
# pragma GCC diagnostic ignored "-Wpointer-bool-conversion"
|
||||||
|
# pragma GCC diagnostic ignored "-Wconstant-conversion"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
# pragma warning(disable: 4005 4100 4101 4189 4191 4200 4244 4293 4296 4309)
|
||||||
|
# pragma warning(disable: 4310 4365 4456 4477 4514 4574 4611 4668 4702 4706)
|
||||||
|
# pragma warning(disable: 4710 4711 4774 4800 4809 4820 4996 4090 4297)
|
||||||
|
#endif
|
||||||
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
#if defined(__GNUC__) && !defined(__ZEPHYR__)
|
||||||
|
/* Zephyr does some magic in it's headers that override the GCC stdlib. This breaks that. */
|
||||||
|
# define _GNU_SOURCE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__TINYC__)
|
||||||
|
/*# define __GNUC__ 3
|
||||||
|
# define GCC_MAJOR 4
|
||||||
|
# define __GNUC_MINOR__ 4
|
||||||
|
# define __GNUC_PATCHLEVEL__ 5 */
|
||||||
|
# define __DECLSPEC_SUPPORTED 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* calling convention mess ----------------------------------------------- */
|
||||||
|
#if defined(__GNUC__) || defined(__LCC__) || defined(__POCC__) \
|
||||||
|
|| defined(__TINYC__)
|
||||||
|
/* these should support C99's inline */
|
||||||
|
/* the test for __POCC__ has to come before the test for _MSC_VER,
|
||||||
|
because PellesC defines _MSC_VER too. This is brain-dead. */
|
||||||
|
# define N_INLINE(rettype, name) inline rettype name
|
||||||
|
#elif defined(__BORLANDC__) || defined(_MSC_VER)
|
||||||
|
/* Borland's compiler is really STRANGE here; note that the __fastcall
|
||||||
|
keyword cannot be before the return type, but __inline cannot be after
|
||||||
|
the return type, so we do not handle this mess in the code generator
|
||||||
|
but rather here. */
|
||||||
|
# define N_INLINE(rettype, name) __inline rettype name
|
||||||
|
#elif defined(__DMC__)
|
||||||
|
# define N_INLINE(rettype, name) inline rettype name
|
||||||
|
#elif defined(__WATCOMC__)
|
||||||
|
# define N_INLINE(rettype, name) __inline rettype name
|
||||||
|
#else /* others are less picky: */
|
||||||
|
# define N_INLINE(rettype, name) rettype __inline name
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define N_INLINE_PTR(rettype, name) rettype (*name)
|
||||||
|
|
||||||
|
#if defined(__POCC__)
|
||||||
|
# define NIM_CONST /* PCC is really picky with const modifiers */
|
||||||
|
# undef _MSC_VER /* Yeah, right PCC defines _MSC_VER even if it is
|
||||||
|
not that compatible. Well done. */
|
||||||
|
#elif defined(__cplusplus)
|
||||||
|
# define NIM_CONST /* C++ is picky with const modifiers */
|
||||||
|
#else
|
||||||
|
# define NIM_CONST const
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
NIM_THREADVAR declaration based on
|
||||||
|
http://stackoverflow.com/questions/18298280/how-to-declare-a-variable-as-thread-local-portably
|
||||||
|
*/
|
||||||
|
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__
|
||||||
|
# define NIM_THREADVAR _Thread_local
|
||||||
|
#elif defined _WIN32 && ( \
|
||||||
|
defined _MSC_VER || \
|
||||||
|
defined __ICL || \
|
||||||
|
defined __DMC__ || \
|
||||||
|
defined __BORLANDC__ )
|
||||||
|
# define NIM_THREADVAR __declspec(thread)
|
||||||
|
#elif defined(__TINYC__) || defined(__GENODE__)
|
||||||
|
# define NIM_THREADVAR
|
||||||
|
/* note that ICC (linux) and Clang are covered by __GNUC__ */
|
||||||
|
#elif defined __GNUC__ || \
|
||||||
|
defined __SUNPRO_C || \
|
||||||
|
defined __xlC__
|
||||||
|
# define NIM_THREADVAR __thread
|
||||||
|
#else
|
||||||
|
# error "Cannot define NIM_THREADVAR"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
#define NIM_THREAD_LOCAL thread_local
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* --------------- how int64 constants should be declared: ----------- */
|
||||||
|
#if defined(__GNUC__) || defined(__LCC__) || \
|
||||||
|
defined(__POCC__) || defined(__DMC__) || defined(_MSC_VER)
|
||||||
|
# define IL64(x) x##LL
|
||||||
|
#else /* works only without LL */
|
||||||
|
# define IL64(x) ((NI64)x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ---------------- casting without correct aliasing rules ----------- */
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
# define NIM_CAST(type, ptr) (((union{type __x__;}*)(ptr))->__x__)
|
||||||
|
#else
|
||||||
|
# define NIM_CAST(type, ptr) ((type)(ptr))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------- */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
# define NIM_EXTERNC extern "C"
|
||||||
|
#else
|
||||||
|
# define NIM_EXTERNC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(WIN32) || defined(_WIN32) /* only Windows has this mess... */
|
||||||
|
# define N_LIB_PRIVATE
|
||||||
|
# define N_CDECL(rettype, name) rettype __cdecl name
|
||||||
|
# define N_STDCALL(rettype, name) rettype __stdcall name
|
||||||
|
# define N_SYSCALL(rettype, name) rettype __syscall name
|
||||||
|
# define N_FASTCALL(rettype, name) rettype __fastcall name
|
||||||
|
# define N_THISCALL(rettype, name) rettype __thiscall name
|
||||||
|
# define N_SAFECALL(rettype, name) rettype __stdcall name
|
||||||
|
/* function pointers with calling convention: */
|
||||||
|
# define N_CDECL_PTR(rettype, name) rettype (__cdecl *name)
|
||||||
|
# define N_STDCALL_PTR(rettype, name) rettype (__stdcall *name)
|
||||||
|
# define N_SYSCALL_PTR(rettype, name) rettype (__syscall *name)
|
||||||
|
# define N_FASTCALL_PTR(rettype, name) rettype (__fastcall *name)
|
||||||
|
# define N_THISCALL_PTR(rettype, name) rettype (__thiscall *name)
|
||||||
|
# define N_SAFECALL_PTR(rettype, name) rettype (__stdcall *name)
|
||||||
|
|
||||||
|
# ifdef __cplusplus
|
||||||
|
# define N_LIB_EXPORT NIM_EXTERNC __declspec(dllexport)
|
||||||
|
# else
|
||||||
|
# define N_LIB_EXPORT NIM_EXTERNC __declspec(dllexport)
|
||||||
|
# endif
|
||||||
|
# define N_LIB_EXPORT_VAR __declspec(dllexport)
|
||||||
|
# define N_LIB_IMPORT extern __declspec(dllimport)
|
||||||
|
#else
|
||||||
|
# define N_LIB_PRIVATE __attribute__((visibility("hidden")))
|
||||||
|
# if defined(__GNUC__)
|
||||||
|
# define N_CDECL(rettype, name) rettype name
|
||||||
|
# define N_STDCALL(rettype, name) rettype name
|
||||||
|
# define N_SYSCALL(rettype, name) rettype name
|
||||||
|
# define N_FASTCALL(rettype, name) __attribute__((fastcall)) rettype name
|
||||||
|
# define N_SAFECALL(rettype, name) rettype name
|
||||||
|
/* function pointers with calling convention: */
|
||||||
|
# define N_CDECL_PTR(rettype, name) rettype (*name)
|
||||||
|
# define N_STDCALL_PTR(rettype, name) rettype (*name)
|
||||||
|
# define N_SYSCALL_PTR(rettype, name) rettype (*name)
|
||||||
|
# define N_FASTCALL_PTR(rettype, name) __attribute__((fastcall)) rettype (*name)
|
||||||
|
# define N_SAFECALL_PTR(rettype, name) rettype (*name)
|
||||||
|
# else
|
||||||
|
# define N_CDECL(rettype, name) rettype name
|
||||||
|
# define N_STDCALL(rettype, name) rettype name
|
||||||
|
# define N_SYSCALL(rettype, name) rettype name
|
||||||
|
# define N_FASTCALL(rettype, name) rettype name
|
||||||
|
# define N_SAFECALL(rettype, name) rettype name
|
||||||
|
/* function pointers with calling convention: */
|
||||||
|
# define N_CDECL_PTR(rettype, name) rettype (*name)
|
||||||
|
# define N_STDCALL_PTR(rettype, name) rettype (*name)
|
||||||
|
# define N_SYSCALL_PTR(rettype, name) rettype (*name)
|
||||||
|
# define N_FASTCALL_PTR(rettype, name) rettype (*name)
|
||||||
|
# define N_SAFECALL_PTR(rettype, name) rettype (*name)
|
||||||
|
# endif
|
||||||
|
# define N_LIB_EXPORT NIM_EXTERNC __attribute__((visibility("default")))
|
||||||
|
# define N_LIB_EXPORT_VAR __attribute__((visibility("default")))
|
||||||
|
# define N_LIB_IMPORT extern
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define N_NOCONV(rettype, name) rettype name
|
||||||
|
/* specify no calling convention */
|
||||||
|
#define N_NOCONV_PTR(rettype, name) rettype (*name)
|
||||||
|
|
||||||
|
#if defined(__GNUC__) || defined(__ICC__)
|
||||||
|
# define N_NOINLINE(rettype, name) rettype __attribute__((__noinline__)) name
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
# define N_NOINLINE(rettype, name) __declspec(noinline) rettype name
|
||||||
|
#else
|
||||||
|
# define N_NOINLINE(rettype, name) rettype name
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define N_NOINLINE_PTR(rettype, name) rettype (*name)
|
||||||
|
|
||||||
|
#if defined(__BORLANDC__) || defined(__WATCOMC__) || \
|
||||||
|
defined(__POCC__) || defined(_MSC_VER) || defined(WIN32) || defined(_WIN32)
|
||||||
|
/* these compilers have a fastcall so use it: */
|
||||||
|
# ifdef __TINYC__
|
||||||
|
# define N_NIMCALL(rettype, name) rettype __attribute((__fastcall)) name
|
||||||
|
# define N_NIMCALL_PTR(rettype, name) rettype (__attribute((__fastcall)) *name)
|
||||||
|
# define N_RAW_NIMCALL __attribute((__fastcall))
|
||||||
|
# else
|
||||||
|
# define N_NIMCALL(rettype, name) rettype __fastcall name
|
||||||
|
# define N_NIMCALL_PTR(rettype, name) rettype (__fastcall *name)
|
||||||
|
# define N_RAW_NIMCALL __fastcall
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# define N_NIMCALL(rettype, name) rettype name /* no modifier */
|
||||||
|
# define N_NIMCALL_PTR(rettype, name) rettype (*name)
|
||||||
|
# define N_RAW_NIMCALL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define N_CLOSURE(rettype, name) N_NIMCALL(rettype, name)
|
||||||
|
#define N_CLOSURE_PTR(rettype, name) N_NIMCALL_PTR(rettype, name)
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
#define COMMA ,
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
// define NIM_STATIC_ASSERT
|
||||||
|
// example use case: CT sizeof for importc types verification
|
||||||
|
// where we have {.completeStruct.} (or lack of {.incompleteStruct.})
|
||||||
|
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L)
|
||||||
|
#define NIM_STATIC_ASSERT(x, msg) _Static_assert((x), msg)
|
||||||
|
#elif defined(__cplusplus)
|
||||||
|
#define NIM_STATIC_ASSERT(x, msg) static_assert((x), msg)
|
||||||
|
#else
|
||||||
|
#define NIM_STATIC_ASSERT(x, msg) typedef int NIM_STATIC_ASSERT_AUX[(x) ? 1 : -1];
|
||||||
|
// On failure, your C compiler will say something like:
|
||||||
|
// "error: 'NIM_STATIC_ASSERT_AUX' declared as an array with a negative size"
|
||||||
|
// we could use a better fallback to also show line number, using:
|
||||||
|
// http://www.pixelbeat.org/programming/gcc/static_assert.html
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* C99 compiler? */
|
||||||
|
#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901))
|
||||||
|
# define HAVE_STDINT_H
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Known compiler with stdint.h that doesn't fit the general pattern? */
|
||||||
|
#if defined(__LCC__) || defined(__DMC__) || defined(__POCC__) || \
|
||||||
|
defined(__AVR__) || (defined(__cplusplus) && (__cplusplus < 201103))
|
||||||
|
# define HAVE_STDINT_H
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (!defined(HAVE_STDINT_H) && defined(__cplusplus) && (__cplusplus >= 201103))
|
||||||
|
# define HAVE_CSTDINT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* wrap all Nim typedefs into namespace Nim */
|
||||||
|
#ifdef USE_NIM_NAMESPACE
|
||||||
|
#ifdef HAVE_CSTDINT
|
||||||
|
#include <cstdint>
|
||||||
|
#else
|
||||||
|
#include <stdint.h>
|
||||||
|
#endif
|
||||||
|
namespace USE_NIM_NAMESPACE {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// preexisting check, seems paranoid, maybe remove
|
||||||
|
#if defined(NIM_TRUE) || defined(NIM_FALSE) || defined(NIM_BOOL)
|
||||||
|
#error "nim reserved preprocessor macros clash"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* bool types (C++ has it): */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define NIM_BOOL bool
|
||||||
|
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901)
|
||||||
|
// see #13798: to avoid conflicts for code emitting `#include <stdbool.h>`
|
||||||
|
#define NIM_BOOL _Bool
|
||||||
|
#else
|
||||||
|
typedef unsigned char NIM_BOOL; // best effort
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NIM_STATIC_ASSERT(sizeof(NIM_BOOL) == 1, ""); // check whether really needed
|
||||||
|
NIM_STATIC_ASSERT(CHAR_BIT == 8, "");
|
||||||
|
// fail fast for (rare) environments where this doesn't hold, as some implicit
|
||||||
|
// assumptions would need revisiting (e.g. `uint8` or https://github.com/nim-lang/Nim/pull/18505)
|
||||||
|
|
||||||
|
#define NIM_TRUE true
|
||||||
|
#define NIM_FALSE false
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
# if __cplusplus >= 201103L
|
||||||
|
# /* nullptr is more type safe (less implicit conversions than 0) */
|
||||||
|
# define NIM_NIL nullptr
|
||||||
|
# else
|
||||||
|
# // both `((void*)0)` and `NULL` would cause codegen to emit
|
||||||
|
# // error: assigning to 'Foo *' from incompatible type 'void *'
|
||||||
|
# // but codegen could be fixed if need. See also potential caveat regarding
|
||||||
|
# // NULL.
|
||||||
|
# // However, `0` causes other issues, see #13798
|
||||||
|
# define NIM_NIL 0
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# include <stdbool.h>
|
||||||
|
# define NIM_NIL ((void*)0) /* C's NULL is fucked up in some C compilers, so
|
||||||
|
the generated code does not rely on it anymore */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__BORLANDC__) || defined(__DMC__) \
|
||||||
|
|| defined(__WATCOMC__) || defined(_MSC_VER)
|
||||||
|
typedef signed char NI8;
|
||||||
|
typedef signed short int NI16;
|
||||||
|
typedef signed int NI32;
|
||||||
|
typedef __int64 NI64;
|
||||||
|
/* XXX: Float128? */
|
||||||
|
typedef unsigned char NU8;
|
||||||
|
typedef unsigned short int NU16;
|
||||||
|
typedef unsigned int NU32;
|
||||||
|
typedef unsigned __int64 NU64;
|
||||||
|
#elif defined(HAVE_STDINT_H)
|
||||||
|
#ifndef USE_NIM_NAMESPACE
|
||||||
|
# include <stdint.h>
|
||||||
|
#endif
|
||||||
|
typedef int8_t NI8;
|
||||||
|
typedef int16_t NI16;
|
||||||
|
typedef int32_t NI32;
|
||||||
|
typedef int64_t NI64;
|
||||||
|
typedef uint8_t NU8;
|
||||||
|
typedef uint16_t NU16;
|
||||||
|
typedef uint32_t NU32;
|
||||||
|
typedef uint64_t NU64;
|
||||||
|
#elif defined(HAVE_CSTDINT)
|
||||||
|
#ifndef USE_NIM_NAMESPACE
|
||||||
|
# include <cstdint>
|
||||||
|
#endif
|
||||||
|
typedef std::int8_t NI8;
|
||||||
|
typedef std::int16_t NI16;
|
||||||
|
typedef std::int32_t NI32;
|
||||||
|
typedef std::int64_t NI64;
|
||||||
|
typedef std::uint8_t NU8;
|
||||||
|
typedef std::uint16_t NU16;
|
||||||
|
typedef std::uint32_t NU32;
|
||||||
|
typedef std::uint64_t NU64;
|
||||||
|
#else
|
||||||
|
/* Unknown compiler/version, do our best */
|
||||||
|
#ifdef __INT8_TYPE__
|
||||||
|
typedef __INT8_TYPE__ NI8;
|
||||||
|
#else
|
||||||
|
typedef signed char NI8;
|
||||||
|
#endif
|
||||||
|
#ifdef __INT16_TYPE__
|
||||||
|
typedef __INT16_TYPE__ NI16;
|
||||||
|
#else
|
||||||
|
typedef signed short int NI16;
|
||||||
|
#endif
|
||||||
|
#ifdef __INT32_TYPE__
|
||||||
|
typedef __INT32_TYPE__ NI32;
|
||||||
|
#else
|
||||||
|
typedef signed int NI32;
|
||||||
|
#endif
|
||||||
|
#ifdef __INT64_TYPE__
|
||||||
|
typedef __INT64_TYPE__ NI64;
|
||||||
|
#else
|
||||||
|
typedef long long int NI64;
|
||||||
|
#endif
|
||||||
|
/* XXX: Float128? */
|
||||||
|
#ifdef __UINT8_TYPE__
|
||||||
|
typedef __UINT8_TYPE__ NU8;
|
||||||
|
#else
|
||||||
|
typedef unsigned char NU8;
|
||||||
|
#endif
|
||||||
|
#ifdef __UINT16_TYPE__
|
||||||
|
typedef __UINT16_TYPE__ NU16;
|
||||||
|
#else
|
||||||
|
typedef unsigned short int NU16;
|
||||||
|
#endif
|
||||||
|
#ifdef __UINT32_TYPE__
|
||||||
|
typedef __UINT32_TYPE__ NU32;
|
||||||
|
#else
|
||||||
|
typedef unsigned int NU32;
|
||||||
|
#endif
|
||||||
|
#ifdef __UINT64_TYPE__
|
||||||
|
typedef __UINT64_TYPE__ NU64;
|
||||||
|
#else
|
||||||
|
typedef unsigned long long int NU64;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef NIM_INTBITS
|
||||||
|
# if NIM_INTBITS == 64
|
||||||
|
typedef NI64 NI;
|
||||||
|
typedef NU64 NU;
|
||||||
|
# elif NIM_INTBITS == 32
|
||||||
|
typedef NI32 NI;
|
||||||
|
typedef NU32 NU;
|
||||||
|
# elif NIM_INTBITS == 16
|
||||||
|
typedef NI16 NI;
|
||||||
|
typedef NU16 NU;
|
||||||
|
# elif NIM_INTBITS == 8
|
||||||
|
typedef NI8 NI;
|
||||||
|
typedef NU8 NU;
|
||||||
|
# else
|
||||||
|
# error "invalid bit width for int"
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// for now there isn't an easy way for C code to reach the program result
|
||||||
|
// when hot code reloading is ON - users will have to:
|
||||||
|
// load the nimhcr.dll, get the hcrGetGlobal proc from there and use it
|
||||||
|
#ifndef NIM_HOT_CODE_RELOADING
|
||||||
|
extern NI nim_program_result;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef float NF32;
|
||||||
|
typedef double NF64;
|
||||||
|
typedef double NF;
|
||||||
|
|
||||||
|
typedef char NIM_CHAR;
|
||||||
|
typedef char* NCSTRING;
|
||||||
|
|
||||||
|
#ifdef NIM_BIG_ENDIAN
|
||||||
|
# define NIM_IMAN 1
|
||||||
|
#else
|
||||||
|
# define NIM_IMAN 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NIM_STRLIT_FLAG ((NU)(1) << ((NIM_INTBITS) - 2)) /* This has to be the same as system.strlitFlag! */
|
||||||
|
|
||||||
|
#define STRING_LITERAL(name, str, length) \
|
||||||
|
static const struct { \
|
||||||
|
TGenericSeq Sup; \
|
||||||
|
NIM_CHAR data[(length) + 1]; \
|
||||||
|
} name = {{length, (NI) ((NU)length | NIM_STRLIT_FLAG)}, str}
|
||||||
|
|
||||||
|
/* declared size of a sequence/variable length array: */
|
||||||
|
#if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER)
|
||||||
|
# define SEQ_DECL_SIZE /* empty is correct! */
|
||||||
|
#else
|
||||||
|
# define SEQ_DECL_SIZE 1000000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ALLOC_0(size) calloc(1, size)
|
||||||
|
#define DL_ALLOC_0(size) dlcalloc(1, size)
|
||||||
|
|
||||||
|
#define paramCount() cmdCount
|
||||||
|
|
||||||
|
// NAN definition copied from math.h included in the Windows SDK version 10.0.14393.0
|
||||||
|
#ifndef NAN
|
||||||
|
# ifndef _HUGE_ENUF
|
||||||
|
# define _HUGE_ENUF 1e+300 // _HUGE_ENUF*_HUGE_ENUF must overflow
|
||||||
|
# endif
|
||||||
|
# define NAN_INFINITY ((float)(_HUGE_ENUF * _HUGE_ENUF))
|
||||||
|
# define NAN ((float)(NAN_INFINITY * 0.0F))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef INF
|
||||||
|
# ifdef INFINITY
|
||||||
|
# define INF INFINITY
|
||||||
|
# elif defined(HUGE_VAL)
|
||||||
|
# define INF HUGE_VAL
|
||||||
|
# elif defined(_MSC_VER)
|
||||||
|
# include <float.h>
|
||||||
|
# define INF (DBL_MAX+DBL_MAX)
|
||||||
|
# else
|
||||||
|
# define INF (1.0 / 0.0)
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct TFrame_ TFrame;
|
||||||
|
struct TFrame_ {
|
||||||
|
TFrame* prev;
|
||||||
|
NCSTRING procname;
|
||||||
|
NI line;
|
||||||
|
NCSTRING filename;
|
||||||
|
NI16 len;
|
||||||
|
NI16 calldepth;
|
||||||
|
NI frameMsgLen;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NIM_POSIX_INIT __attribute__((constructor))
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
# define NIM_LIKELY(x) __builtin_expect(x, 1)
|
||||||
|
# define NIM_UNLIKELY(x) __builtin_expect(x, 0)
|
||||||
|
/* We need the following for the posix wrapper. In particular it will give us
|
||||||
|
POSIX_SPAWN_USEVFORK: */
|
||||||
|
# ifndef _GNU_SOURCE
|
||||||
|
# define _GNU_SOURCE
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# define NIM_LIKELY(x) (x)
|
||||||
|
# define NIM_UNLIKELY(x) (x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0 // defined(__GNUC__) || defined(__clang__)
|
||||||
|
// not needed anymore because the stack marking cares about
|
||||||
|
// interior pointers now
|
||||||
|
static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); }
|
||||||
|
# define GC_GUARD __attribute__ ((cleanup(GCGuard)))
|
||||||
|
#else
|
||||||
|
# define GC_GUARD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Test to see if Nim and the C compiler agree on the size of a pointer.
|
||||||
|
NIM_STATIC_ASSERT(sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8, "");
|
||||||
|
|
||||||
|
#ifdef USE_NIM_NAMESPACE
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
# define NIM_ALIGN(x) __declspec(align(x))
|
||||||
|
# define NIM_ALIGNOF(x) __alignof(x)
|
||||||
|
#else
|
||||||
|
# define NIM_ALIGN(x) __attribute__((aligned(x)))
|
||||||
|
# define NIM_ALIGNOF(x) __alignof__(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ---------------- platform specific includes ----------------------- */
|
||||||
|
|
||||||
|
/* VxWorks related includes */
|
||||||
|
#if defined(__VXWORKS__)
|
||||||
|
# include <sys/types.h>
|
||||||
|
# include <types/vxWind.h>
|
||||||
|
# include <tool/gnu/toolMacros.h>
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
# include <sys/types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* these exist to make the codegen logic simpler */
|
||||||
|
#define nimModInt(a, b, res) (((*res) = (a) % (b)), 0)
|
||||||
|
#define nimModInt64(a, b, res) (((*res) = (a) % (b)), 0)
|
||||||
|
|
||||||
|
#if (!defined(_MSC_VER) || defined(__clang__)) && !defined(NIM_EmulateOverflowChecks)
|
||||||
|
/* these exist because we cannot have .compilerProcs that are importc'ed
|
||||||
|
by a different name */
|
||||||
|
|
||||||
|
#define nimAddInt64(a, b, res) __builtin_saddll_overflow(a, b, (long long int*)res)
|
||||||
|
#define nimSubInt64(a, b, res) __builtin_ssubll_overflow(a, b, (long long int*)res)
|
||||||
|
#define nimMulInt64(a, b, res) __builtin_smulll_overflow(a, b, (long long int*)res)
|
||||||
|
|
||||||
|
#if NIM_INTBITS == 32
|
||||||
|
#define nimAddInt(a, b, res) __builtin_sadd_overflow(a, b, res)
|
||||||
|
#define nimSubInt(a, b, res) __builtin_ssub_overflow(a, b, res)
|
||||||
|
#define nimMulInt(a, b, res) __builtin_smul_overflow(a, b, res)
|
||||||
|
#else
|
||||||
|
/* map it to the 'long long' variant */
|
||||||
|
#define nimAddInt(a, b, res) __builtin_saddll_overflow(a, b, (long long int*)res)
|
||||||
|
#define nimSubInt(a, b, res) __builtin_ssubll_overflow(a, b, (long long int*)res)
|
||||||
|
#define nimMulInt(a, b, res) __builtin_smulll_overflow(a, b, (long long int*)res)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NIM_NOALIAS __restrict
|
||||||
|
/* __restrict is said to work for all the C(++) compilers out there that we support */
|
||||||
|
|
||||||
|
#endif /* NIMBASE_H */
|
|
@ -0,0 +1,289 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <argp.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
|
||||||
|
#include "libwaku.h"
|
||||||
|
|
||||||
|
// Keep a global string to store the waku call responses
|
||||||
|
static NimStringDesc wakuString;
|
||||||
|
NimStringDesc* mResp = &wakuString;
|
||||||
|
|
||||||
|
struct ConfigNode {
|
||||||
|
NCSTRING host;
|
||||||
|
NU port;
|
||||||
|
NCSTRING key;
|
||||||
|
NIM_BOOL relay;
|
||||||
|
NCSTRING peers;
|
||||||
|
};
|
||||||
|
|
||||||
|
static ConfigNode cfgNode;
|
||||||
|
|
||||||
|
// Arguments parsing
|
||||||
|
static char doc[] = "\nC example that shows how to use the waku library.";
|
||||||
|
static char args_doc[] = "";
|
||||||
|
|
||||||
|
static struct argp_option options[] = {
|
||||||
|
{ "host", 'h', "HOST", 0, "IP to listen for for LibP2P traffic. (default: \"0.0.0.0\")"},
|
||||||
|
{ "port", 'p', "PORT", 0, "TCP listening port. (default: \"60000\")"},
|
||||||
|
{ "key", 'k', "KEY", 0, "P2P node private key as 64 char hex string."},
|
||||||
|
{ "relay", 'r', "RELAY", 0, "Enable relay protocol: 1 or 0. (default: 1)"},
|
||||||
|
{ "peers", 'a', "PEERS", 0, "Comma-separated list of peer-multiaddress to connect\
|
||||||
|
to. (default: \"\") e.g. \"/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAmVFXtAfSj4EiR7mL2KvL4EE2wztuQgUSBoj2Jx2KeXFLN\""},
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static error_t parse_opt(int key, char *arg, struct argp_state *state) {
|
||||||
|
|
||||||
|
struct ConfigNode *cfgNode = state->input;
|
||||||
|
switch (key) {
|
||||||
|
case 'h':
|
||||||
|
cfgNode->host = arg;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
cfgNode->port = atoi(arg);
|
||||||
|
break;
|
||||||
|
case 'k':
|
||||||
|
cfgNode->key = arg;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
cfgNode->relay = atoi(arg);
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
cfgNode->peers = arg;
|
||||||
|
break;
|
||||||
|
case ARGP_KEY_ARG:
|
||||||
|
if (state->arg_num >= 1) /* Too many arguments. */
|
||||||
|
argp_usage(state);
|
||||||
|
break;
|
||||||
|
case ARGP_KEY_END:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return ARGP_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct argp argp = { options, parse_opt, args_doc, doc, 0, 0, 0 };
|
||||||
|
|
||||||
|
// Base64 encoding
|
||||||
|
// source: https://nachtimwald.com/2017/11/18/base64-encode-and-decode-in-c/
|
||||||
|
size_t b64_encoded_size(size_t inlen)
|
||||||
|
{
|
||||||
|
size_t ret;
|
||||||
|
|
||||||
|
ret = inlen;
|
||||||
|
if (inlen % 3 != 0)
|
||||||
|
ret += 3 - (inlen % 3);
|
||||||
|
ret /= 3;
|
||||||
|
ret *= 4;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
|
||||||
|
char *b64_encode(const unsigned char *in, size_t len)
|
||||||
|
{
|
||||||
|
char *out;
|
||||||
|
size_t elen;
|
||||||
|
size_t i;
|
||||||
|
size_t j;
|
||||||
|
size_t v;
|
||||||
|
|
||||||
|
if (in == NULL || len == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
elen = b64_encoded_size(len);
|
||||||
|
out = malloc(elen+1);
|
||||||
|
out[elen] = '\0';
|
||||||
|
|
||||||
|
for (i=0, j=0; i<len; i+=3, j+=4) {
|
||||||
|
v = in[i];
|
||||||
|
v = i+1 < len ? v << 8 | in[i+1] : v << 8;
|
||||||
|
v = i+2 < len ? v << 8 | in[i+2] : v << 8;
|
||||||
|
|
||||||
|
out[j] = b64chars[(v >> 18) & 0x3F];
|
||||||
|
out[j+1] = b64chars[(v >> 12) & 0x3F];
|
||||||
|
if (i+1 < len) {
|
||||||
|
out[j+2] = b64chars[(v >> 6) & 0x3F];
|
||||||
|
} else {
|
||||||
|
out[j+2] = '=';
|
||||||
|
}
|
||||||
|
if (i+2 < len) {
|
||||||
|
out[j+3] = b64chars[v & 0x3F];
|
||||||
|
} else {
|
||||||
|
out[j+3] = '=';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// End of Base64 encoding
|
||||||
|
|
||||||
|
// Beginning of UI program logic
|
||||||
|
|
||||||
|
enum PROGRAM_STATE {
|
||||||
|
MAIN_MENU,
|
||||||
|
SUBSCRIBE_TOPIC_MENU,
|
||||||
|
CONNECT_TO_OTHER_NODE_MENU,
|
||||||
|
PUBLISH_MESSAGE_MENU
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PROGRAM_STATE current_state = MAIN_MENU;
|
||||||
|
|
||||||
|
void show_main_menu() {
|
||||||
|
printf("\nPlease, select an option:\n");
|
||||||
|
printf("\t1.) Subscribe to topic\n");
|
||||||
|
printf("\t2.) Connect to other node\n");
|
||||||
|
printf("\t3.) Publish a message\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_scanf_to_not_block() {
|
||||||
|
fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_scanf_to_block() {
|
||||||
|
fcntl(0, F_SETFL, fcntl(0, F_GETFL) ^ O_NONBLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_user_input() {
|
||||||
|
char cmd[1024];
|
||||||
|
memset(cmd, 0, 1024);
|
||||||
|
int numRead = read(0, cmd, 1024);
|
||||||
|
if (numRead <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int c;
|
||||||
|
while ( (c = getchar()) != '\n' && c != EOF ) { }
|
||||||
|
|
||||||
|
switch (atoi(cmd))
|
||||||
|
{
|
||||||
|
case SUBSCRIBE_TOPIC_MENU:
|
||||||
|
{
|
||||||
|
printf("Indicate the Pubsubtopic to subscribe:\n");
|
||||||
|
set_scanf_to_block();
|
||||||
|
char pubsubTopic[128];
|
||||||
|
scanf("%127s", pubsubTopic);
|
||||||
|
if (!waku_relay_subscribe(pubsubTopic, &mResp)) {
|
||||||
|
printf("Error subscribing to PubsubTopic: %s\n", mResp->data);
|
||||||
|
}
|
||||||
|
printf("Waku Relay subscription response: %s\n", mResp->data);
|
||||||
|
|
||||||
|
set_scanf_to_not_block();
|
||||||
|
show_main_menu();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONNECT_TO_OTHER_NODE_MENU:
|
||||||
|
printf("Connecting to a node. Please indicate the peer Multiaddress:\n");
|
||||||
|
printf("e.g.: /ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAmVFXtAfSj4EiR7mL2KvL4EE2wztuQgUSBoj2Jx2KeXFLN\n");
|
||||||
|
set_scanf_to_block();
|
||||||
|
char peerAddr[512];
|
||||||
|
scanf("%511s", peerAddr);
|
||||||
|
if (!waku_connect(peerAddr, 10000 /* timeoutMs */, &mResp)) {
|
||||||
|
printf("Couldn't connect to the remote peer: %s\n", mResp->data);
|
||||||
|
}
|
||||||
|
set_scanf_to_not_block();
|
||||||
|
show_main_menu();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PUBLISH_MESSAGE_MENU:
|
||||||
|
{
|
||||||
|
set_scanf_to_block();
|
||||||
|
printf("Indicate the Pubsubtopic:\n");
|
||||||
|
char pubsubTopic[128];
|
||||||
|
scanf("%127s", pubsubTopic);
|
||||||
|
|
||||||
|
printf("Type the message tp publish:\n");
|
||||||
|
char msg[1024];
|
||||||
|
scanf("%1023s", msg);
|
||||||
|
|
||||||
|
char jsonWakuMsg[1024];
|
||||||
|
char *msgPayload = b64_encode(msg, strlen(msg));
|
||||||
|
|
||||||
|
waku_content_topic("appName",
|
||||||
|
1,
|
||||||
|
"contentTopicName",
|
||||||
|
"encoding",
|
||||||
|
&mResp);
|
||||||
|
|
||||||
|
snprintf(jsonWakuMsg, 1024, "{\"payload\":\"%s\",\"content_topic\":\"%s\"}", msgPayload, mResp->data);
|
||||||
|
free(msgPayload);
|
||||||
|
|
||||||
|
waku_relay_publish(pubsubTopic, jsonWakuMsg, 10000 /*timeout ms*/, &mResp);
|
||||||
|
printf("waku relay response [%s]\n", mResp->data);
|
||||||
|
set_scanf_to_not_block();
|
||||||
|
show_main_menu();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MAIN_MENU:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// End of UI program logic
|
||||||
|
|
||||||
|
void show_help_and_exit() {
|
||||||
|
printf("Wrong parameters\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void event_handler(char* msg) {
|
||||||
|
printf("Receiving message [%s]\n", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
// default values
|
||||||
|
cfgNode.host = "0.0.0.0";
|
||||||
|
cfgNode.port = 60000;
|
||||||
|
cfgNode.relay = 1;
|
||||||
|
cfgNode.peers = NULL;
|
||||||
|
|
||||||
|
if (argp_parse(&argp, argc, argv, 0, 0, &cfgNode)
|
||||||
|
== ARGP_ERR_UNKNOWN) {
|
||||||
|
show_help_and_exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// To allow non-blocking 'reads' from stdin
|
||||||
|
fcntl(0, F_SETFL, fcntl(0, F_GETFL) ^ O_NONBLOCK);
|
||||||
|
|
||||||
|
NimMain(); // initialize the Nim runtime
|
||||||
|
|
||||||
|
waku_default_pubsub_topic(&mResp);
|
||||||
|
printf("Default pubsub topic: %s\n", mResp->data);
|
||||||
|
printf("Git Version: %s\n", waku_version());
|
||||||
|
printf("Bind addr: %s:%u\n", cfgNode.host, cfgNode.port);
|
||||||
|
printf("Waku Relay enabled: %s\n", cfgNode.relay == 1 ? "YES": "NO");
|
||||||
|
|
||||||
|
if (!waku_new(&cfgNode, &mResp)) {
|
||||||
|
printf("Error creating WakuNode: %s\n", mResp->data);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
waku_set_event_callback(event_handler);
|
||||||
|
waku_start();
|
||||||
|
|
||||||
|
printf("Establishing connection with: %s\n", cfgNode.peers);
|
||||||
|
if (!waku_connect(cfgNode.peers, 10000 /* timeoutMs */, &mResp)) {
|
||||||
|
printf("Couldn't connect to the remote peer: %s\n", mResp->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
show_main_menu();
|
||||||
|
while(1) {
|
||||||
|
handle_user_input();
|
||||||
|
waku_poll();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
import
|
||||||
|
std/json
|
||||||
|
import
|
||||||
|
json_signal_event
|
||||||
|
|
||||||
|
type JsonErrorEvent* = ref object of JsonSignal
|
||||||
|
message*: string
|
||||||
|
|
||||||
|
proc new*(T: type JsonErrorEvent,
|
||||||
|
message: string): T =
|
||||||
|
return JsonErrorEvent(
|
||||||
|
eventType: "error",
|
||||||
|
message: message)
|
||||||
|
|
||||||
|
method `$`*(jsonError: JsonErrorEvent): string =
|
||||||
|
$( %* jsonError )
|
|
@ -0,0 +1,42 @@
|
||||||
|
|
||||||
|
import
|
||||||
|
std/json
|
||||||
|
import
|
||||||
|
../../waku/v2/waku_core/message/message,
|
||||||
|
json_signal_event
|
||||||
|
|
||||||
|
type JsonMessage = ref object
|
||||||
|
# https://rfc.vac.dev/spec/36/#jsonmessage-type
|
||||||
|
payload: string
|
||||||
|
contentTopic: string
|
||||||
|
version: uint
|
||||||
|
timestamp: int64
|
||||||
|
|
||||||
|
type JsonMessageEvent* = ref object of JsonSignal
|
||||||
|
pubsubTopic*: string
|
||||||
|
messageId*: string
|
||||||
|
wakuMessage*: JsonMessage
|
||||||
|
|
||||||
|
proc new*(T: type JsonMessageEvent,
|
||||||
|
pubSubTopic: string,
|
||||||
|
msg: WakuMessage): T =
|
||||||
|
# Returns a WakuMessage event as indicated in
|
||||||
|
# https://rfc.vac.dev/spec/36/#jsonmessageevent-type
|
||||||
|
|
||||||
|
var payload = newString(len(msg.payload))
|
||||||
|
copyMem(addr payload[0], unsafeAddr msg.payload[0], len(msg.payload))
|
||||||
|
|
||||||
|
return JsonMessageEvent(
|
||||||
|
eventType: "message",
|
||||||
|
pubSubTopic: pubSubTopic,
|
||||||
|
messageId: "TODO",
|
||||||
|
wakuMessage: JsonMessage(
|
||||||
|
payload: payload,
|
||||||
|
contentTopic: msg.contentTopic,
|
||||||
|
version: msg.version,
|
||||||
|
timestamp: int64(msg.timestamp)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
method `$`*(jsonMessage: JsonMessageEvent): string =
|
||||||
|
$( %* jsonMessage )
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
type JsonSignal* = ref object of RootObj
|
||||||
|
# https://rfc.vac.dev/spec/36/#jsonsignal-type
|
||||||
|
eventType* {.requiresInit.}: string
|
||||||
|
|
||||||
|
method `$`*(jsonSignal: JsonSignal): string {.base.} = discard
|
||||||
|
# All events should implement this
|
||||||
|
|
|
@ -0,0 +1,400 @@
|
||||||
|
|
||||||
|
import
|
||||||
|
std/[sequtils,times,strformat,json,options],
|
||||||
|
strutils,
|
||||||
|
os
|
||||||
|
import
|
||||||
|
chronicles,
|
||||||
|
chronos,
|
||||||
|
libp2p/crypto/secp,
|
||||||
|
stew/shims/net,
|
||||||
|
eth/net/nat as todo_delete_this_module
|
||||||
|
import
|
||||||
|
../vendor/nim-libp2p/libp2p/crypto/crypto,
|
||||||
|
../../waku/common/utils/nat,
|
||||||
|
../../waku/v2/waku_enr/capabilities,
|
||||||
|
../../waku/v2/waku_core/message/codec,
|
||||||
|
../../waku/v2/waku_core/message/message,
|
||||||
|
../../waku/v2/waku_core/topics/pubsub_topic,
|
||||||
|
../../waku/v2/node/peer_manager/peer_manager,
|
||||||
|
../../waku/v2/node/waku_node,
|
||||||
|
../../waku/v2/node/builder,
|
||||||
|
../../waku/v2/node/config,
|
||||||
|
../../waku/v2/waku_relay/protocol,
|
||||||
|
events/[json_error_event,json_message_event,json_signal_event]
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
### Wrapper around the waku node
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
### Exported types
|
||||||
|
type
|
||||||
|
ConfigNode* {.exportc.} = object
|
||||||
|
# Struct exported to C
|
||||||
|
host*: cstring
|
||||||
|
port*: uint
|
||||||
|
key*: cstring
|
||||||
|
relay*: bool
|
||||||
|
|
||||||
|
### End of exported types
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
### Not-exported components
|
||||||
|
|
||||||
|
type
|
||||||
|
EventCallback = proc(signal: cstring) {.cdecl, gcsafe, raises: [Defect].}
|
||||||
|
|
||||||
|
var eventCallback:EventCallback = nil
|
||||||
|
|
||||||
|
proc relayEventCallback(pubsubTopic: string, data: seq[byte]): Future[void] {.gcsafe, raises: [Defect].} =
|
||||||
|
# Callback that hadles the Waku Relay events. i.e. messages or errors.
|
||||||
|
if not isNil(eventCallback):
|
||||||
|
let msg = WakuMessage.decode(data)
|
||||||
|
var event: JsonSignal
|
||||||
|
if msg.isOk():
|
||||||
|
event = JsonMessageEvent.new(pubsubTopic, msg.value)
|
||||||
|
else:
|
||||||
|
let errorMsg = string("Error decoding message.") & $msg.error
|
||||||
|
event = JsonErrorEvent.new(errorMsg)
|
||||||
|
|
||||||
|
try:
|
||||||
|
eventCallback(cstring($event))
|
||||||
|
except Exception:
|
||||||
|
error "Exception when calling 'eventCallBack': " &
|
||||||
|
getCurrentExceptionMsg()
|
||||||
|
else:
|
||||||
|
error "eventCallback is nil"
|
||||||
|
|
||||||
|
var retFut = newFuture[void]()
|
||||||
|
retFut.complete()
|
||||||
|
return retFut
|
||||||
|
|
||||||
|
proc okResp(message: string): string =
|
||||||
|
$(%* { "result": message })
|
||||||
|
|
||||||
|
proc errResp(message: string): string =
|
||||||
|
# Convers an error message into an error-JsonResponse
|
||||||
|
# {
|
||||||
|
# error: string;
|
||||||
|
# }
|
||||||
|
return $(%* { "error": message })
|
||||||
|
|
||||||
|
proc setupNat(natConf, clientId: string, tcpPort, udpPort: Port):
|
||||||
|
Result[tuple[ip: Option[ValidIpAddress], tcpPort: Option[Port], udpPort: Option[Port]], string] {.gcsafe.} =
|
||||||
|
# TODO reorganize all setupNat calls and use only one commom proc
|
||||||
|
|
||||||
|
let strategy = case natConf.toLowerAscii():
|
||||||
|
of "any": NatAny
|
||||||
|
of "none": NatNone
|
||||||
|
of "upnp": NatUpnp
|
||||||
|
of "pmp": NatPmp
|
||||||
|
else: NatNone
|
||||||
|
|
||||||
|
var endpoint: tuple[ip: Option[ValidIpAddress], tcpPort: Option[Port], udpPort: Option[Port]]
|
||||||
|
|
||||||
|
if strategy != NatNone:
|
||||||
|
let extIp = getExternalIP(strategy)
|
||||||
|
if extIP.isSome():
|
||||||
|
endpoint.ip = some(ValidIpAddress.init(extIp.get()))
|
||||||
|
# RedirectPorts in considered a gcsafety violation
|
||||||
|
# because it obtains the address of a non-gcsafe proc?
|
||||||
|
var extPorts: Option[(Port, Port)]
|
||||||
|
try:
|
||||||
|
extPorts = ({.gcsafe.}: redirectPorts(tcpPort = tcpPort,
|
||||||
|
udpPort = udpPort,
|
||||||
|
description = clientId))
|
||||||
|
except CatchableError:
|
||||||
|
# TODO: nat.nim Error: can raise an unlisted exception: Exception. Isolate here for now.
|
||||||
|
error "unable to determine external ports"
|
||||||
|
extPorts = none((Port, Port))
|
||||||
|
|
||||||
|
if extPorts.isSome():
|
||||||
|
let (extTcpPort, extUdpPort) = extPorts.get()
|
||||||
|
endpoint.tcpPort = some(extTcpPort)
|
||||||
|
endpoint.udpPort = some(extUdpPort)
|
||||||
|
|
||||||
|
else: # NatNone
|
||||||
|
if not natConf.startsWith("extip:"):
|
||||||
|
return err("not a valid NAT mechanism: " & $natConf)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# any required port redirection is assumed to be done by hand
|
||||||
|
endpoint.ip = some(ValidIpAddress.init(natConf[6..^1]))
|
||||||
|
except ValueError:
|
||||||
|
return err("not a valid IP address: " & $natConf[6..^1])
|
||||||
|
|
||||||
|
return ok(endpoint)
|
||||||
|
|
||||||
|
proc parseConfig(config: ConfigNode,
|
||||||
|
privateKey: var PrivateKey,
|
||||||
|
netConfig: var NetConfig,
|
||||||
|
jsonResp: var string
|
||||||
|
): bool =
|
||||||
|
if len(config.key) == 0:
|
||||||
|
jsonResp = errResp("The node key is missing.");
|
||||||
|
return false
|
||||||
|
|
||||||
|
try:
|
||||||
|
let key = SkPrivateKey.init(crypto.fromHex($config.key)).tryGet()
|
||||||
|
privateKey = crypto.PrivateKey(scheme: Secp256k1, skkey: key)
|
||||||
|
except CatchableError:
|
||||||
|
let msg = string("Invalid node key: ") & getCurrentExceptionMsg()
|
||||||
|
jsonResp = errResp(msg)
|
||||||
|
return false
|
||||||
|
|
||||||
|
if len(config.host) == 0:
|
||||||
|
jsonResp = errResp("host attribute is required")
|
||||||
|
return false
|
||||||
|
|
||||||
|
var listenAddr = ValidIpAddress.init("127.0.0.1")
|
||||||
|
try:
|
||||||
|
listenAddr = ValidIpAddress.init($config.host)
|
||||||
|
except CatchableError:
|
||||||
|
let msg = string("Invalid host IP address: ") & getCurrentExceptionMsg()
|
||||||
|
jsonResp = errResp(msg)
|
||||||
|
return false
|
||||||
|
|
||||||
|
if config.port == 0:
|
||||||
|
jsonResp = errResp("Please set a valid port number")
|
||||||
|
return false
|
||||||
|
|
||||||
|
## `udpPort` is only supplied to satisfy underlying APIs but is not
|
||||||
|
## actually a supported transport for libp2p traffic.
|
||||||
|
let udpPort = config.port
|
||||||
|
|
||||||
|
let natRes = setupNat("any", clientId,
|
||||||
|
Port(uint16(config.port)),
|
||||||
|
Port(uint16(udpPort)))
|
||||||
|
if natRes.isErr():
|
||||||
|
jsonResp = errResp(fmt"failed to setup NAT: {$natRes.error}")
|
||||||
|
return false
|
||||||
|
|
||||||
|
let (extIp, extTcpPort, _) = natRes.get()
|
||||||
|
|
||||||
|
let extPort = if extIp.isSome() and extTcpPort.isNone():
|
||||||
|
some(Port(uint16(config.port)))
|
||||||
|
else:
|
||||||
|
extTcpPort
|
||||||
|
|
||||||
|
let wakuFlags = CapabilitiesBitfield.init(
|
||||||
|
lightpush = false,
|
||||||
|
filter = false,
|
||||||
|
store = false,
|
||||||
|
relay = config.relay
|
||||||
|
)
|
||||||
|
|
||||||
|
let netConfigRes = NetConfig.init(
|
||||||
|
bindIp = listenAddr,
|
||||||
|
bindPort = Port(uint16(config.port)),
|
||||||
|
extIp = extIp,
|
||||||
|
extPort = extPort,
|
||||||
|
wakuFlags = some(wakuFlags))
|
||||||
|
|
||||||
|
if netConfigRes.isErr():
|
||||||
|
let msg = string("Error creating NetConfig: ") & $netConfigRes.error
|
||||||
|
jsonResp = errResp(msg)
|
||||||
|
return false
|
||||||
|
|
||||||
|
netConfig = netConfigRes.value
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
|
# WakuNode instance
|
||||||
|
var node {.threadvar.}: WakuNode
|
||||||
|
|
||||||
|
### End of not-exported components
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
### Exported procs
|
||||||
|
|
||||||
|
proc waku_new(config: ConfigNode,
|
||||||
|
jsonResp: var string): bool
|
||||||
|
{.dynlib, exportc.} =
|
||||||
|
# Creates a new instance of the WakuNode.
|
||||||
|
# Notice that the ConfigNode type is also exported and available for users.
|
||||||
|
var privateKey: PrivateKey
|
||||||
|
var netConfig = NetConfig.init(ValidIpAddress.init("127.0.0.1"), Port(60000'u16)).value
|
||||||
|
if not parseConfig(config,
|
||||||
|
privateKey, netConfig,
|
||||||
|
jsonResp):
|
||||||
|
return false
|
||||||
|
|
||||||
|
var builder = WakuNodeBuilder.init()
|
||||||
|
builder.withRng(crypto.newRng())
|
||||||
|
builder.withNodeKey(privateKey)
|
||||||
|
builder.withNetworkConfiguration(netConfig)
|
||||||
|
builder.withSwitchConfiguration(
|
||||||
|
maxConnections = some(50.int)
|
||||||
|
)
|
||||||
|
|
||||||
|
let wakuNodeRes = builder.build()
|
||||||
|
if wakuNodeRes.isErr():
|
||||||
|
let errorMsg = string("failed to create waku node instance: ") & wakuNodeRes.error
|
||||||
|
jsonResp = errResp(errorMsg)
|
||||||
|
return false
|
||||||
|
|
||||||
|
node = wakuNodeRes.value
|
||||||
|
|
||||||
|
if config.relay:
|
||||||
|
waitFor node.mountRelay()
|
||||||
|
node.peerManager.start()
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc waku_version(): cstring {.dynlib, exportc.} =
|
||||||
|
return WakuNodeVersionString
|
||||||
|
|
||||||
|
proc waku_set_event_callback(callback: EventCallback) {.dynlib, exportc.} =
|
||||||
|
eventCallback = callback
|
||||||
|
|
||||||
|
proc waku_content_topic(appName: cstring,
|
||||||
|
appVersion: uint,
|
||||||
|
contentTopicName: cstring,
|
||||||
|
encoding: cstring,
|
||||||
|
outContentTopic: var string) {.dynlib, exportc.} =
|
||||||
|
# https://rfc.vac.dev/spec/36/#extern-char-waku_content_topicchar-applicationname-unsigned-int-applicationversion-char-contenttopicname-char-encoding
|
||||||
|
outContentTopic = fmt"{appName}/{appVersion}/{contentTopicName}/{encoding}"
|
||||||
|
|
||||||
|
proc waku_pubsub_topic(topicName: cstring, outPubsubTopic: var string) {.dynlib, exportc.} =
|
||||||
|
# https://rfc.vac.dev/spec/36/#extern-char-waku_pubsub_topicchar-name-char-encoding
|
||||||
|
outPubsubTopic = fmt"/waku/2/{topicName}"
|
||||||
|
|
||||||
|
proc waku_default_pubsub_topic(defPubsubTopic: var string) {.dynlib, exportc.} =
|
||||||
|
# https://rfc.vac.dev/spec/36/#extern-char-waku_default_pubsub_topic
|
||||||
|
defPubsubTopic = DefaultPubsubTopic
|
||||||
|
|
||||||
|
proc waku_relay_publish(pubSubTopic: cstring,
|
||||||
|
jsonWakuMessage: cstring,
|
||||||
|
timeoutMs: int,
|
||||||
|
jsonResp: var string): bool
|
||||||
|
|
||||||
|
{.dynlib, exportc, cdecl.} =
|
||||||
|
# https://rfc.vac.dev/spec/36/#extern-char-waku_relay_publishchar-messagejson-char-pubsubtopic-int-timeoutms
|
||||||
|
|
||||||
|
var jsonContent:JsonNode
|
||||||
|
try:
|
||||||
|
jsonContent = parseJson($jsonWakuMessage)
|
||||||
|
except JsonParsingError:
|
||||||
|
jsonResp = errResp (fmt"Problem parsing json message. {getCurrentExceptionMsg()}")
|
||||||
|
return false
|
||||||
|
|
||||||
|
var wakuMessage: WakuMessage
|
||||||
|
try:
|
||||||
|
var version = 0'u32
|
||||||
|
if jsonContent.hasKey("version"):
|
||||||
|
version = (uint32) jsonContent["version"].getInt()
|
||||||
|
|
||||||
|
wakuMessage = WakuMessage(
|
||||||
|
# Visit https://rfc.vac.dev/spec/14/ for further details
|
||||||
|
payload: jsonContent["payload"].getStr().toSeq().mapIt(byte (it)),
|
||||||
|
contentTopic: $jsonContent["content_topic"].getStr(),
|
||||||
|
version: version,
|
||||||
|
timestamp: getTime().toUnix(),
|
||||||
|
ephemeral: false
|
||||||
|
)
|
||||||
|
except KeyError:
|
||||||
|
jsonResp = errResp(fmt"Problem building the WakuMessage. {getCurrentExceptionMsg()}")
|
||||||
|
return false
|
||||||
|
|
||||||
|
let targetPubSubTopic = if $pubSubTopic == "":
|
||||||
|
DefaultPubsubTopic
|
||||||
|
else:
|
||||||
|
$pubSubTopic
|
||||||
|
|
||||||
|
if node.wakuRelay.isNil():
|
||||||
|
jsonResp = errResp("Can't publish. WakuRelay is not enabled.")
|
||||||
|
return false
|
||||||
|
|
||||||
|
let pubMsgFut = node.wakuRelay.publish(targetPubSubTopic, wakuMessage)
|
||||||
|
|
||||||
|
# With the next loop we convert an asynchronous call into a synchronous one
|
||||||
|
for i in 0 .. timeoutMs:
|
||||||
|
if pubMsgFut.finished():
|
||||||
|
break
|
||||||
|
sleep(1)
|
||||||
|
|
||||||
|
if pubMsgFut.finished():
|
||||||
|
let numPeers = pubMsgFut.read()
|
||||||
|
if numPeers == 0:
|
||||||
|
jsonResp = errResp("Message not sent because no peers found")
|
||||||
|
elif numPeers > 0:
|
||||||
|
# TODO: pending to return a valid message Id (response when all is correct)
|
||||||
|
jsonResp = okResp("hard-coded-message-id")
|
||||||
|
|
||||||
|
else:
|
||||||
|
jsonResp = errResp("Timeout expired")
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc waku_start() {.dynlib, exportc.} =
|
||||||
|
waitFor node.start()
|
||||||
|
|
||||||
|
proc waku_stop() {.dynlib, exportc.} =
|
||||||
|
waitFor node.stop()
|
||||||
|
|
||||||
|
proc waku_relay_subscribe(
|
||||||
|
pubSubTopic: cstring,
|
||||||
|
jsonResp: var string): bool
|
||||||
|
{.dynlib, exportc.} =
|
||||||
|
# @params
|
||||||
|
# topic: Pubsub topic to subscribe to. If empty, it subscribes to the default pubsub topic.
|
||||||
|
if isNil(eventCallback):
|
||||||
|
jsonResp = errResp("""Cannot subscribe without a callback.
|
||||||
|
Kindly set it with the 'waku_set_event_callback' function""")
|
||||||
|
return false
|
||||||
|
|
||||||
|
if node.wakuRelay.isNil():
|
||||||
|
jsonResp = errResp("Cannot subscribe without Waku Relay enabled.")
|
||||||
|
return false
|
||||||
|
|
||||||
|
node.wakuRelay.subscribe(PubsubTopic($pubSubTopic), PubsubRawHandler(relayEventCallback))
|
||||||
|
|
||||||
|
jsonResp = okResp("true")
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc waku_relay_unsubscribe(
|
||||||
|
pubSubTopic: cstring,
|
||||||
|
jsonResp: var string): bool
|
||||||
|
{.dynlib, exportc.} =
|
||||||
|
# @params
|
||||||
|
# topic: Pubsub topic to subscribe to. If empty, it unsubscribes to the default pubsub topic.
|
||||||
|
if isNil(eventCallback):
|
||||||
|
jsonResp = errResp("""Cannot unsubscribe without a callback.
|
||||||
|
Kindly set it with the 'waku_set_event_callback' function""")
|
||||||
|
return false
|
||||||
|
|
||||||
|
if node.wakuRelay.isNil():
|
||||||
|
jsonResp = errResp("Cannot unsubscribe without Waku Relay enabled.")
|
||||||
|
return false
|
||||||
|
|
||||||
|
node.wakuRelay.unsubscribeAll(PubsubTopic($pubSubTopic))
|
||||||
|
|
||||||
|
jsonResp = okResp("true")
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc waku_connect(peerMultiAddr: cstring,
|
||||||
|
timeoutMs: uint = 10000,
|
||||||
|
jsonResp: var string): bool
|
||||||
|
{.dynlib, exportc.} =
|
||||||
|
# peerMultiAddr: comma-separated list of fully-qualified multiaddresses.
|
||||||
|
let peers = ($peerMultiAddr).split(",").mapIt(strip(it))
|
||||||
|
|
||||||
|
# TODO: the timeoutMs is not being used at all!
|
||||||
|
let connectFut = node.connectToNodes(peers, source="static")
|
||||||
|
while not connectFut.finished():
|
||||||
|
poll()
|
||||||
|
|
||||||
|
if not connectFut.completed():
|
||||||
|
jsonResp = errResp("Timeout expired.")
|
||||||
|
return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc waku_poll() {.dynlib, exportc, gcsafe.} =
|
||||||
|
poll()
|
||||||
|
|
||||||
|
### End of exported procs
|
||||||
|
################################################################################
|
21
waku.nimble
21
waku.nimble
|
@ -34,6 +34,18 @@ proc buildBinary(name: string, srcDir = "./", params = "", lang = "c") =
|
||||||
extra_params &= " " & paramStr(i)
|
extra_params &= " " & paramStr(i)
|
||||||
exec "nim " & lang & " --out:build/" & name & " " & extra_params & " " & srcDir & name & ".nim"
|
exec "nim " & lang & " --out:build/" & name & " " & extra_params & " " & srcDir & name & ".nim"
|
||||||
|
|
||||||
|
proc buildLibrary(name: string, srcDir = "./", params = "", lang = "c", isStatic = true) =
|
||||||
|
if not dirExists "build":
|
||||||
|
mkDir "build"
|
||||||
|
# allow something like "nim nimbus --verbosity:0 --hints:off nimbus.nims"
|
||||||
|
var extra_params = params
|
||||||
|
for i in 2..<paramCount():
|
||||||
|
extra_params &= " " & paramStr(i)
|
||||||
|
if isStatic:
|
||||||
|
exec "nim " & lang & " --out:build/" & name & ".a --app:staticlib --opt:size --noMain --header " & extra_params & " " & srcDir & name & ".nim"
|
||||||
|
else:
|
||||||
|
exec "nim " & lang & " --out:build/" & name & ".a --app:lib --opt:size --noMain --header " & extra_params & " " & srcDir & name & ".nim"
|
||||||
|
|
||||||
proc test(name: string, params = "-d:chronicles_log_level=DEBUG", lang = "c") =
|
proc test(name: string, params = "-d:chronicles_log_level=DEBUG", lang = "c") =
|
||||||
# XXX: When running `> NIM_PARAMS="-d:chronicles_log_level=INFO" make test2`
|
# XXX: When running `> NIM_PARAMS="-d:chronicles_log_level=INFO" make test2`
|
||||||
# I expect compiler flag to be overridden, however it stays with whatever is
|
# I expect compiler flag to be overridden, however it stays with whatever is
|
||||||
|
@ -41,13 +53,10 @@ proc test(name: string, params = "-d:chronicles_log_level=DEBUG", lang = "c") =
|
||||||
buildBinary name, "tests/", params
|
buildBinary name, "tests/", params
|
||||||
exec "build/" & name
|
exec "build/" & name
|
||||||
|
|
||||||
|
|
||||||
### Waku common tasks
|
### Waku common tasks
|
||||||
task testcommon, "Build & run common tests":
|
task testcommon, "Build & run common tests":
|
||||||
test "all_tests_common", "-d:chronicles_log_level=WARN -d:chronosStrictException"
|
test "all_tests_common", "-d:chronicles_log_level=WARN -d:chronosStrictException"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Waku v2 tasks
|
### Waku v2 tasks
|
||||||
task wakunode2, "Build Waku v2 cli node":
|
task wakunode2, "Build Waku v2 cli node":
|
||||||
let name = "wakunode2"
|
let name = "wakunode2"
|
||||||
|
@ -65,7 +74,6 @@ task networkmonitor, "Build network monitor tool":
|
||||||
let name = "networkmonitor"
|
let name = "networkmonitor"
|
||||||
buildBinary name, "apps/networkmonitor/", "-d:chronicles_log_level=TRACE"
|
buildBinary name, "apps/networkmonitor/", "-d:chronicles_log_level=TRACE"
|
||||||
|
|
||||||
|
|
||||||
task test2, "Build & run Waku v2 tests":
|
task test2, "Build & run Waku v2 tests":
|
||||||
test "all_tests_v2"
|
test "all_tests_v2"
|
||||||
|
|
||||||
|
@ -75,7 +83,6 @@ task testwakunode2, "Build & run wakunode2 app tests":
|
||||||
task testbridge, "Build & run wakubridge tests":
|
task testbridge, "Build & run wakubridge tests":
|
||||||
test "all_tests_wakubridge"
|
test "all_tests_wakubridge"
|
||||||
|
|
||||||
|
|
||||||
task example2, "Build Waku v2 example":
|
task example2, "Build Waku v2 example":
|
||||||
buildBinary "publisher", "examples/v2/"
|
buildBinary "publisher", "examples/v2/"
|
||||||
buildBinary "subscriber", "examples/v2/"
|
buildBinary "subscriber", "examples/v2/"
|
||||||
|
@ -92,6 +99,10 @@ task chat2bridge, "Build chat2bridge":
|
||||||
let name = "chat2bridge"
|
let name = "chat2bridge"
|
||||||
buildBinary name, "apps/chat2bridge/", "-d:chronicles_log_level=TRACE"
|
buildBinary name, "apps/chat2bridge/", "-d:chronicles_log_level=TRACE"
|
||||||
|
|
||||||
|
### C Bindings
|
||||||
|
task libwaku, "Build the cbindings waku node library":
|
||||||
|
let name = "libwaku"
|
||||||
|
buildLibrary name, "library/", "-d:chronicles_log_level=ERROR"
|
||||||
|
|
||||||
### Legacy: Whisper & Waku v1 tasks
|
### Legacy: Whisper & Waku v1 tasks
|
||||||
task testwhisper, "Build & run Whisper tests":
|
task testwhisper, "Build & run Whisper tests":
|
||||||
|
|
|
@ -67,3 +67,27 @@ proc finish3*(proto: var ProtoBuffer) =
|
||||||
|
|
||||||
proc `==`*(a: zint64, b: zint64): bool =
|
proc `==`*(a: zint64, b: zint64): bool =
|
||||||
int64(a) == int64(b)
|
int64(a) == int64(b)
|
||||||
|
|
||||||
|
proc `$`*(err: ProtobufError): string =
|
||||||
|
case err.kind:
|
||||||
|
of DecodeFailure:
|
||||||
|
case err.error:
|
||||||
|
of VarintDecode:
|
||||||
|
return "VarintDecode"
|
||||||
|
of MessageIncomplete:
|
||||||
|
return "MessageIncomplete"
|
||||||
|
of BufferOverflow:
|
||||||
|
return "BufferOverflow"
|
||||||
|
of MessageTooBig:
|
||||||
|
return "MessageTooBig"
|
||||||
|
of BadWireType:
|
||||||
|
return "BadWireType"
|
||||||
|
of IncorrectBlob:
|
||||||
|
return "IncorrectBlob"
|
||||||
|
of RequiredFieldMissing:
|
||||||
|
return "RequiredFieldMissing"
|
||||||
|
of MissingRequiredField:
|
||||||
|
return "MissingRequiredField " & err.field
|
||||||
|
of InvalidLengthField:
|
||||||
|
return "InvalidLengthField " & err.field
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,6 @@ declarePublicGauge waku_px_peers, "number of peers (in the node's peerManager) s
|
||||||
logScope:
|
logScope:
|
||||||
topics = "waku node"
|
topics = "waku node"
|
||||||
|
|
||||||
|
|
||||||
# TODO: Move to application instance (e.g., `WakuNode2`)
|
# TODO: Move to application instance (e.g., `WakuNode2`)
|
||||||
# Git version in git describe format (defined compile time)
|
# Git version in git describe format (defined compile time)
|
||||||
const git_version* {.strdefine.} = "n/a"
|
const git_version* {.strdefine.} = "n/a"
|
||||||
|
@ -71,6 +70,7 @@ const clientId* = "Nimbus Waku v2 node"
|
||||||
# Default Waku Filter Timeout
|
# Default Waku Filter Timeout
|
||||||
const WakuFilterTimeout: Duration = 1.days
|
const WakuFilterTimeout: Duration = 1.days
|
||||||
|
|
||||||
|
const WakuNodeVersionString* = "version / git commit hash: " & git_version
|
||||||
|
|
||||||
# key and crypto modules different
|
# key and crypto modules different
|
||||||
type
|
type
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
# libwaku
|
|
||||||
|
|
||||||
Exposes a C API that can be used by other environments other than C.
|
|
||||||
|
|
||||||
## Running
|
|
||||||
|
|
||||||
```
|
|
||||||
make wrappers
|
|
||||||
```
|
|
|
@ -1,14 +0,0 @@
|
||||||
#ifndef __LIBWAKU_H__
|
|
||||||
#define __LIBWAKU_H__
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
// Initialize Nim
|
|
||||||
void NimMain();
|
|
||||||
|
|
||||||
char* info(const char* wakuNode);
|
|
||||||
|
|
||||||
void echo();
|
|
||||||
|
|
||||||
#endif //__LIBWAKU_H__
|
|
|
@ -1,38 +0,0 @@
|
||||||
# libwaku
|
|
||||||
#
|
|
||||||
# Exposes a C API that can be used by other environment than C.
|
|
||||||
|
|
||||||
# TODO Start a node
|
|
||||||
# TODO Mock info call
|
|
||||||
# TODO Write header file
|
|
||||||
# TODO Write example C code file
|
|
||||||
# TODO Wrap info call
|
|
||||||
# TODO Init a node
|
|
||||||
|
|
||||||
# proc info*(node: WakuNode): WakuInfo =
|
|
||||||
proc info(foo: cstring): cstring {.exportc, dynlib.} =
|
|
||||||
echo "info about node"
|
|
||||||
echo foo
|
|
||||||
return foo
|
|
||||||
|
|
||||||
proc echo() {.exportc.} =
|
|
||||||
echo "echo"
|
|
||||||
|
|
||||||
# TODO Here at the moment, start the node
|
|
||||||
# Then do info call
|
|
||||||
# WIP
|
|
||||||
#proc main() {.async.} =
|
|
||||||
# let
|
|
||||||
# rng = crypto.newRng()
|
|
||||||
# conf = WakuNodeConf.load()
|
|
||||||
# (extIp, extTcpPort, extUdpPort) = setupNat(conf.nat, clientId,
|
|
||||||
# Port(uint16(conf.tcpPort) + conf.portsShift),
|
|
||||||
# Port(uint16(conf.udpPort) + conf.portsShift))
|
|
||||||
# node = WakuNode.new(conf.nodeKey, conf.listenAddress,
|
|
||||||
# Port(uint16(conf.tcpPort) + conf.portsShift), extIp, extTcpPort)
|
|
||||||
#
|
|
||||||
# await node.start()
|
|
||||||
#
|
|
||||||
#main()
|
|
||||||
|
|
||||||
# When main done stuff
|
|
|
@ -1,14 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#include "libwaku.h"
|
|
||||||
|
|
||||||
void NimMain();
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
char* string;
|
|
||||||
NimMain();
|
|
||||||
//echo();
|
|
||||||
string = info("hello there");
|
|
||||||
printf("Info: %s", string);
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
// Passing "-lwaku" to the Go linker through "-extldflags" is not enough. We need it in here, for some reason.
|
|
||||||
#cgo LDFLAGS: -Wl,-rpath,'$ORIGIN' -L${SRCDIR}/../build -lwaku
|
|
||||||
#include "libwaku.h"
|
|
||||||
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
// Arrange that main.main runs on main thread.
|
|
||||||
func init() {
|
|
||||||
runtime.LockOSThread()
|
|
||||||
}
|
|
||||||
|
|
||||||
func Start() {
|
|
||||||
C.NimMain()
|
|
||||||
|
|
||||||
messageC := C.CString("Calling info")
|
|
||||||
fmt.Println("Start nim-waku")
|
|
||||||
var str = C.info(messageC)
|
|
||||||
fmt.Println("Info", str)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
fmt.Println("Hi main")
|
|
||||||
Start()
|
|
||||||
}
|
|
Loading…
Reference in New Issue