diff --git a/Makefile b/Makefile index db1c72d02..43afa7f65 100644 --- a/Makefile +++ b/Makefile @@ -34,8 +34,6 @@ TOOLS_CSV := $(subst $(SPACE),$(COMMA),$(TOOLS)) clean \ libnimbus.so \ libnimbus.a \ - wrappers \ - wrappers-static \ libbacktrace ifeq ($(NIM_PARAMS),) @@ -135,52 +133,21 @@ test-reproducibility: # usual cleaning clean: | clean-common - rm -rf build/{nimbus,$(TOOLS_CSV),all_tests,test_rpc,*_wrapper_test} + rm -rf build/{nimbus,$(TOOLS_CSV),all_tests,test_rpc} ifneq ($(USE_LIBBACKTRACE), 0) + $(MAKE) -C vendor/nim-libbacktrace clean $(HANDLE_OUTPUT) endif -libnimbus.so: | build deps - echo -e $(BUILD_MSG) "build/$@" && \ - $(ENV_SCRIPT) nim c --app:lib --noMain --nimcache:nimcache/libnimbus $(NIM_PARAMS) -o:build/$@.0 wrappers/libnimbus.nim && \ - rm -f build/$@ && \ - ln -s $@.0 build/$@ - -# libraries for dynamic linking of non-Nim objects -EXTRA_LIBS_DYNAMIC := -L"$(CURDIR)/build" -lnimbus -lm -wrappers: | build deps libnimbus.so - echo -e $(BUILD_MSG) "build/C_wrapper_example" && \ - $(CC) wrappers/wrapper_example.c -Wl,-rpath,'$$ORIGIN' $(EXTRA_LIBS_DYNAMIC) -g -o build/C_wrapper_example - echo -e $(BUILD_MSG) "build/go_wrapper_example" && \ - go build -ldflags "-linkmode external -extldflags '$(EXTRA_LIBS_DYNAMIC)'" -o build/go_wrapper_example wrappers/wrapper_example.go wrappers/cfuncs.go - echo -e $(BUILD_MSG) "build/go_wrapper_whisper_example" && \ - go build -ldflags "-linkmode external -extldflags '$(EXTRA_LIBS_DYNAMIC)'" -o build/go_wrapper_whisper_example wrappers/wrapper_whisper_example.go wrappers/cfuncs.go - -libnimbus.a: | build deps - echo -e $(BUILD_MSG) "build/$@" && \ - rm -f build/$@ && \ - $(ENV_SCRIPT) nim c --app:staticlib --noMain --nimcache:nimcache/libnimbus_static $(NIM_PARAMS) -o:build/$@ wrappers/libnimbus.nim && \ - [[ -e "$@" ]] && mv "$@" build/ # workaround for https://github.com/nim-lang/Nim/issues/12745 - -# These libraries are for statically linking non-Nim objects to libnimbus.a -# (where "vendor/nim-libbacktrace/libbacktrace.nim" doesn't get to set its LDFLAGS) -EXTRA_LIBS_STATIC := -L"$(CURDIR)/build" -lnimbus -L"$(CURDIR)/vendor/nim-libbacktrace/install/usr/lib" -lbacktracenim -lbacktrace -lm -ldl -lpcre -ifeq ($(shell uname), Darwin) -USE_VENDORED_LIBUNWIND := 1 -endif # macOS -ifeq ($(OS), Windows_NT) -USE_VENDORED_LIBUNWIND := 1 -endif # Windows -ifeq ($(USE_VENDORED_LIBUNWIND), 1) -EXTRA_LIBS_STATIC := $(EXTRA_LIBS_STATIC) -lunwind -endif # USE_VENDORED_LIBUNWIND -wrappers-static: | build deps libnimbus.a - echo -e $(BUILD_MSG) "build/C_wrapper_example_static" && \ - $(CC) wrappers/wrapper_example.c -static -pthread $(EXTRA_LIBS_STATIC) -g -o build/C_wrapper_example_static - echo -e $(BUILD_MSG) "build/go_wrapper_example_static" && \ - go build -ldflags "-linkmode external -extldflags '-static $(EXTRA_LIBS_STATIC)'" -o build/go_wrapper_example_static wrappers/wrapper_example.go wrappers/cfuncs.go - echo -e $(BUILD_MSG) "build/go_wrapper_whisper_example_static" && \ - go build -ldflags "-linkmode external -extldflags '-static $(EXTRA_LIBS_STATIC)'" -o build/go_wrapper_whisper_example_static wrappers/wrapper_whisper_example.go wrappers/cfuncs.go +# Note about building Nimbus as a library: +# +# There were `wrappers`, `wrappers-static`, `libnimbus.so` and `libnimbus.a` +# target scripts here, and C and Go examples for calling the Nimbus library in +# directory `wrappers/`. They have been removed because they only wrapped +# Whisper protocol support, which has been removed as it is obsolete. +# +# This note is kept so that anyone wanting to build Nimbus as a library or call +# from C or Go will know it has been done before. The previous working version +# can be found in Git history. Look for the `nimbus-eth1` commit that adds +# this comment and removes `wrappers/*`. endif # "variables.mk" was not included - diff --git a/wrappers/README.md b/wrappers/README.md deleted file mode 100644 index 68eab17f3..000000000 --- a/wrappers/README.md +++ /dev/null @@ -1,71 +0,0 @@ -This folder contains an experimental C wrapper for using parts of the Nimbus -code from C/Go in the Status console client: - -https://github.com/status-im/status-console-client/ - -It serves mainly as a proof-of-concept for now - there are several unresolved -issues surrounding threading, inter-language communication, callbacks etc. - -To build the wrappers and the example programs, run from the top level directory: - -```bash -make wrappers -``` - -Now you can run the example programs: - -```bash -build/C_wrapper_example -build/go_wrapper_example -build/go_wrapper_whisper_example -``` - -To build statically linked versions: - -```bash -make wrappers-static -``` - -You can also build the wrappers with the Nix build file (Need to -[install](https://nixos.org/nix/download.html) Nix first): - -```bash -nix-build -A wrappers -``` - -# Notes on usage -Before any of the API calls are done, `NimMain()` needs to be run. - -After this any of the asymmetric or symmetric keys API calls can be done. - -Before any actual Whisper calls are done, `nimbus_start()` needs to be called. -This will setup the Whisper node, start discovery and connect to the Status -fleet. - -After this call you can use the Whisper related calls such as -`nimbus_subscribe_filter` and `nimbus_post`. - -`nimbus_subscribe_filter` allows you to pass a pointer to user data. This will -just pass the pointer to the, by you provided, callback. You can use this to -have a certain context in the callback. - -Be aware that when using this with cgo, you will have a problem when passing a -Go pointer that contains other Go pointers, as this is no longer allowed. -See also: https://golang.org/cmd/cgo/#hdr-Passing_pointers - -This can be resolved by refering to it indirectly, e.g. using a map in Go -containing the pointers. See here for an example solution: -https://github.com/mattn/go-pointer/blob/master/pointer.go - -# Caveat -Two items to take into consideration: -1. All API calls must be called from the same thread where the initial -`NimMain()` was called from. While there are -[ways](https://nim-lang.org/docs/backends.html#memory-management-thread-coordination) -of thread coordination, this will not work for calls involving `async` work -with Chronos. Thus to be extra clear & consistent, the above mentioned ways are -not used at all. -2. For most of the API calls it is assumed that when passing a pointer as -parameter, that the data that this pointer refers to remains valid during the -duration of the call. If this is not the case, issues might occur as copying -might happen to late. diff --git a/wrappers/cfuncs.go b/wrappers/cfuncs.go deleted file mode 100644 index 87372c776..000000000 --- a/wrappers/cfuncs.go +++ /dev/null @@ -1,14 +0,0 @@ -package main - -/* - -#include "libnimbus.h" - -// receiveHandler gateway function -void receiveHandler_cgo(received_message * msg, void* udata) -{ - void receiveHandler(received_message* msg, void* udata); - receiveHandler(msg, udata); -} -*/ -import "C" \ No newline at end of file diff --git a/wrappers/libnimbus.h b/wrappers/libnimbus.h deleted file mode 100644 index 13471a491..000000000 --- a/wrappers/libnimbus.h +++ /dev/null @@ -1,132 +0,0 @@ -#ifndef __LIBNIMBUS_H__ -#define __LIBNIMBUS_H__ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - const uint8_t* decoded; /* Decoded payload */ - size_t decodedLen; /* Decoded payload length */ - const uint8_t* source; /* 64 bytes public key, can be nil */ - const uint8_t* recipientPublicKey; /* 64 bytes public key, can be nil */ - uint32_t timestamp; /* Timestamp of creation message, expiry - ttl */ - uint32_t ttl; /* TTL of message */ - uint8_t topic[4]; /* Topic of message */ - double pow; /* PoW value of received message */ - uint8_t hash[32]; /* Hash of message */ -} received_message; - -typedef struct { - const uint8_t* symKeyID; /* 32 bytes identifier for symmetric key, set to nil if none */ - const uint8_t* privateKeyID; /* 32 bytes identifier for asymmetric key, set to nil if none */ - const uint8_t* source; /* 64 bytes public key, set to nil if none */ - double minPow; /* Minimum PoW that message must have */ - uint8_t topic[4]; /* Will default to 0x00000000 if not provided */ - int allowP2P; -} filter_options; - -typedef struct { - const uint8_t* symKeyID; /* 32 bytes identifier for symmetric key, set to nil if none */ - const uint8_t* pubKey; /* 64 bytes public key, set to nil if none */ - const uint8_t* sourceID; /* 32 bytes identifier for asymmetric key, set to nil if none */ - uint32_t ttl; /* TTL of message */ - uint8_t topic[4]; /* Will default to 0x00000000 if not provided */ - uint8_t* payload; /* Payload to be send, can be len=0 but can not be nil */ - size_t payloadLen; /* Payload length */ - uint8_t* padding; /* Custom padding, can be set to nil */ - size_t paddingLen; /* Padding length */ - double powTime; /* Maximum time to calculate PoW */ - double powTarget; /* Minimum PoW target to reach before stopping */ -} post_message; - -typedef struct { - uint8_t topic[4]; -} topic; - -typedef void (*received_msg_handler)(received_message* msg, void* udata); - -/** Buffer lengths, can be used in go for convenience */ -#define ID_LEN 32 -#define SYMKEY_LEN 32 -#define PRIVKEY_LEN 32 -#define BLOOM_LEN 64 - -/** Initialize Nim and the Status library. Must be called before anything else - * of the API. Also, all following calls must come from the same thread as from - * which this call was done. - */ -void NimMain(); - -/** Start Ethereum node with Whisper capability and connect to Status fleet. - * Optionally start discovery and listen for incoming connections. - * The minPow value is the minimum required PoW that this node will allow. - * When privkey is null, a new keypair will be generated. - */ -bool nimbus_start(uint16_t port, bool startListening, bool enableDiscovery, - double minPow, const uint8_t* privkey, bool staging); - -/** Add peers to connect to - must be called after nimbus_start */ -bool nimbus_add_peer(const char* nodeId); - -/** - * Should be called in regularly - for example in a busy loop (beautiful!) on - * dedicated thread. - */ -void nimbus_poll(); - -/** Asymmetric Keys API */ - -/** Raw 32 byte arrays are passed as IDs. The caller needs to provide a pointer - * to 32 bytes allocation for this. */ -bool nimbus_new_keypair(uint8_t id[ID_LEN]); -bool nimbus_add_keypair(const uint8_t privkey[PRIVKEY_LEN], uint8_t id[ID_LEN]); -bool nimbus_delete_keypair(const uint8_t id[ID_LEN]); -bool nimbus_get_private_key(const uint8_t id[ID_LEN], - uint8_t privkey[PRIVKEY_LEN]); - -/** Symmetric Keys API */ - -/** Raw 32 byte arrays are passed as IDs. The caller needs to provide a pointer - * to 32 bytes allocation for this. */ -bool nimbus_add_symkey(const uint8_t symkey[SYMKEY_LEN], uint8_t id[ID_LEN]); -bool nimbus_add_symkey_from_password(const char* password, uint8_t id[ID_LEN]); -bool nimbus_delete_symkey(const uint8_t id[ID_LEN]); -bool nimbus_get_symkey(const uint8_t id[ID_LEN], uint8_t symkey[SYMKEY_LEN]); - -/** Whisper message posting and receiving API */ - -/* Post Whisper message to the queue */ -bool nimbus_post(post_message* msg); -/** Subscribe to given filter. The void pointer udata will be passed to the - * received_msg_handler callback. - */ -bool nimbus_subscribe_filter(filter_options* filter_options, - received_msg_handler msg, void* udata, uint8_t id[ID_LEN]); -bool nimbus_unsubscribe_filter(const uint8_t id[ID_LEN]); - -/** Get the minimum required PoW of this node */ -double nimbus_get_min_pow(); - -/** Get the currently set bloom filter of this node. This will automatically - *update for each filter subsribed to. - */ -void nimbus_get_bloom_filter(uint8_t bloomfilter[BLOOM_LEN]); - -/** Example helper, can be removed */ -topic nimbus_channel_to_topic(const char* channel); - -/** Very limited Status chat API */ - -void nimbus_post_public(const char* channel, const char* payload); -void nimbus_join_public_chat(const char* channel, received_msg_handler msg); - -#ifdef __cplusplus -} -#endif - -#endif //__LIBNIMBUS_H__ diff --git a/wrappers/libnimbus.nim b/wrappers/libnimbus.nim deleted file mode 100644 index 0537d05c7..000000000 --- a/wrappers/libnimbus.nim +++ /dev/null @@ -1,493 +0,0 @@ -# -# Nimbus -# (c) Copyright 2019 -# Status Research & Development GmbH -# -# Licensed under either of -# Apache License, version 2.0, (LICENSE-APACHEv2) -# MIT license (LICENSE-MIT) - -import - chronos, chronicles, nimcrypto/[utils, hmac, pbkdf2, hash, sysrand], tables, - stew/ranges/ptr_arith, eth/[keys, rlp, p2p, async_utils], - eth/p2p/rlpx_protocols/whisper_protocol, - eth/p2p/[peer_pool, bootnodes, whispernodes], ../nimbus/rpc/key_storage, - ../nimbus/random_keys - -# TODO: lots of overlap with Nimbus Whisper RPC here, however not all -# the same due to type conversion (no use of Option and such). Perhaps some -# parts can be refactored in sharing some of the code. - -const idLen = 32 - -type - Identifier = array[idLen, byte] - - CReceivedMessage* = object - decoded*: ptr byte - decodedLen*: int # csize_t - source*: ptr byte - recipientPublicKey*: ptr byte - timestamp*: uint32 - ttl*: uint32 - topic*: Topic - pow*: float64 - hash*: Hash - - CFilterOptions* = object - symKeyID*: ptr byte - privateKeyID*: ptr byte - source*: ptr byte - minPow*: float64 - topic*: Topic # lets go with one topic for now unless more are required - allowP2P*: bool - - CPostMessage* = object - symKeyID*: ptr byte - pubKey*: ptr byte - sourceID*: ptr byte - ttl*: uint32 - topic*: Topic - payload*: ptr byte - payloadLen*: int # csize_t - padding*: ptr byte - paddingLen*: int # csize_t - powTime*: float64 - powTarget*: float64 - - CTopic* = object - topic*: Topic - -# Don't do this at home, you'll never get rid of ugly globals like this! -var - node: EthereumNode -# You will only add more instead! -let whisperKeys = newKeyStorage() - -proc generateRandomID(): Identifier = - while true: # TODO: error instead of looping? - if randomBytes(result) == idLen: - break - -proc setBootNodes(nodes: openArray[string]): seq[ENode] = - result = newSeqOfCap[ENode](nodes.len) - for nodeId in nodes: - # For now we can just do assert as we only pass our own const arrays. - let enode = ENode.fromString(nodeId).expect("correct enode") - result.add(enode) - -proc connectToNodes(nodes: openArray[string]) = - for nodeId in nodes: - # For now we can just do assert as we only pass our own const arrays. - let enode = ENode.fromString(nodeId).expect("correct enode") - - traceAsyncErrors node.peerPool.connectToNode(newNode(enode)) - -# Setting up the node - -proc nimbus_start(port: uint16, startListening: bool, enableDiscovery: bool, - minPow: float64, privateKey: ptr byte, staging: bool): bool - {.exportc, dynlib.} = - # TODO: any async calls can still create `Exception`, why? - let address = Address( - udpPort: port.Port, tcpPort: port.Port, ip: parseIpAddress("0.0.0.0")) - - var keypair: KeyPair - if privateKey.isNil: - #var kp = KeyPair.random() - #if kp.isErr: - #error "Can't generate keypair", err = kp.error - #return false - #keypair = kp[] - keypair = randomKeyPair() - else: - let - privKey = PrivateKey.fromRaw(makeOpenArray(privateKey, 32)) - if privKey.isErr: - error "Passed an invalid private key." - return false - - keypair = privKey[].toKeyPair() - - node = newEthereumNode(keypair, address, 1.NetworkId, nil, addAllCapabilities = false) - node.addCapability Whisper - - node.protocolState(Whisper).config.powRequirement = minPow - # TODO: should we start the node with an empty bloomfilter? - # var bloom: Bloom - # node.protocolState(Whisper).config.bloom = bloom - - let bootnodes = if staging: setBootNodes(StatusBootNodesStaging) - else: setBootNodes(StatusBootNodes) - - traceAsyncErrors node.connectToNetwork(bootnodes, startListening, - enableDiscovery) - - # Connect to known Status Whisper fleet directly - if staging: connectToNodes(WhisperNodesStaging) - else: connectToNodes(WhisperNodes) - - result = true - -proc nimbus_poll() {.exportc, dynlib.} = - poll() - -proc nimbus_add_peer(nodeId: cstring): bool {.exportc, dynlib.} = - var - whisperNode: Node - let enode = ENode.fromString($nodeId) - if enode.isErr: - return false - try: - whisperNode = newNode(enode[]) - except CatchableError: - return false - - # TODO: call can create `Exception`, why? - traceAsyncErrors node.peerPool.connectToNode(whisperNode) - result = true - -# Whisper API (Similar to Whisper JSON-RPC API) - -proc nimbus_channel_to_topic(channel: cstring): CTopic - {.exportc, dynlib, raises: [Defect].} = - # Only used for the example, to conveniently convert channel to topic. - doAssert(not channel.isNil, "Channel cannot be nil.") - - let hash = digest(keccak256, $channel) - for i in 0..<4: - result.topic[i] = hash.data[i] - -# Asymmetric Keys - -proc nimbus_new_keypair(id: var Identifier): bool - {.exportc, dynlib, raises: [Defect].} = - ## Caller needs to provide as id a pointer to 32 bytes allocation. - doAssert(not (unsafeAddr id).isNil, "Key id cannot be nil.") - - id = generateRandomID() - try: - whisperKeys.asymKeys.add(id.toHex(), randomKeyPair()) - result = true - except CatchableError: - # Don't think this can actually happen, comes from the `getPublicKey` part - # in `newKeyPair` - discard - -proc nimbus_add_keypair(privateKey: ptr byte, id: var Identifier): - bool {.exportc, dynlib, raises: [Defect, Exception].} = - ## Caller needs to provide as id a pointer to 32 bytes allocation. - doAssert(not (unsafeAddr id).isNil, "Key id cannot be nil.") - doAssert(not privateKey.isNil, "Private key cannot be nil.") - - var keypair: KeyPair - if privateKey.isNil: - #var kp = KeyPair.random() - #if kp.isErr: - #error "Can't generate keypair", err = kp.error - #return false - #keypair = kp[] - keypair = randomKeyPair() - else: - let - privKey = PrivateKey.fromRaw(makeOpenArray(privateKey, 32)) - if privKey.isErr: - error "Passed an invalid private key." - return false - - keypair = privKey[].toKeyPair() - - result = true - id = generateRandomID() - whisperKeys.asymKeys.add(id.toHex(), keypair) - -proc nimbus_delete_keypair(id: Identifier): bool - {.exportc, dynlib, raises: [].} = - doAssert(not (unsafeAddr id).isNil, "Key id cannot be nil.") - - var unneeded: KeyPair - result = whisperKeys.asymKeys.take(id.toHex(), unneeded) - -proc nimbus_get_private_key(id: Identifier, privateKey: var PrivateKey): - bool {.exportc, dynlib, raises: [OSError, IOError, ValueError, Exception].} = - doAssert(not (unsafeAddr id).isNil, "Key id cannot be nil.") - doAssert(not (unsafeAddr privateKey).isNil, "Private key cannot be nil.") - - try: - privateKey = whisperKeys.asymkeys[id.toHex()].seckey - result = true - except KeyError: - error "Private key not found." - -# Symmetric Keys - -proc nimbus_add_symkey(symKey: ptr SymKey, id: var Identifier): bool - {.exportc, dynlib, raises: [Defect].} = - ## Caller needs to provide as id a pointer to 32 bytes allocation. - doAssert(not (unsafeAddr id).isNil, "Key id cannot be nil.") - doAssert(not symKey.isNil, "Symmetric key cannot be nil.") - - id = generateRandomID() - result = true - - # Copy of key happens at add - whisperKeys.symKeys.add(id.toHex, symKey[]) - -proc nimbus_add_symkey_from_password(password: cstring, id: var Identifier): - bool {.exportc, dynlib, raises: [Defect].} = - ## Caller needs to provide as id a pointer to 32 bytes allocation. - doAssert(not (unsafeAddr id).isNil, "Key id cannot be nil.") - doAssert(not password.isNil, "Password cannot be nil.") - - var ctx: HMAC[sha256] - var symKey: SymKey - if pbkdf2(ctx, $password, "", 65356, symKey) != sizeof(SymKey): - return false - - id = generateRandomID() - result = true - - whisperKeys.symKeys.add(id.toHex(), symKey) - -proc nimbus_delete_symkey(id: Identifier): bool - {.exportc, dynlib, raises: [Defect].} = - doAssert(not (unsafeAddr id).isNil, "Key id cannot be nil.") - - var unneeded: SymKey - result = whisperKeys.symKeys.take(id.toHex(), unneeded) - -proc nimbus_get_symkey(id: Identifier, symKey: var SymKey): - bool {.exportc, dynlib, raises: [Defect, Exception].} = - doAssert(not (unsafeAddr id).isNil, "Key id cannot be nil.") - doAssert(not (unsafeAddr symKey).isNil, "Symmetric key cannot be nil.") - - try: - symKey = whisperKeys.symkeys[id.toHex()] - result = true - except KeyError: - error "Symmetric key not found." - -# Whisper message posting and receiving - -proc nimbus_post(message: ptr CPostMessage): bool {.exportc, dynlib.} = - ## Encryption is mandatory. - ## A symmetric key or an asymmetric key must be provided. Both is not allowed. - ## Providing a payload is mandatory, it cannot be nil, but can be of length 0. - doAssert(not message.isNil, "Message pointer cannot be nil.") - - var - sigPrivKey: Option[PrivateKey] - asymKey: Option[PublicKey] - symKey: Option[SymKey] - padding: Option[seq[byte]] - payload: seq[byte] - - if not message.pubKey.isNil() and not message.symKeyID.isNil(): - warn "Both symmetric and asymmetric keys are provided, choose one." - return false - - if message.pubKey.isNil() and message.symKeyID.isNil(): - warn "Both symmetric and asymmetric keys are nil, provide one." - return false - - if not message.pubKey.isNil(): - let pubkey = PublicKey.fromRaw(makeOpenArray(message.pubKey, 64)) - if pubkey.isErr: - error "Passed an invalid public key for encryption." - return false - asymKey = some(pubkey[]) - - try: - if not message.symKeyID.isNil(): - let symKeyId = makeOpenArray(message.symKeyID, idLen).toHex() - symKey = some(whisperKeys.symKeys[symKeyId]) - if not message.sourceID.isNil(): - let sourceId = makeOpenArray(message.sourceID, idLen).toHex() - sigPrivKey = some(whisperKeys.asymKeys[sourceId].seckey) - except KeyError: - warn "No key found with provided key id." - return false - - if not message.payload.isNil(): - # This will make a copy - payload = @(makeOpenArray(message.payload, message.payloadLen)) - else: - warn "Message payload was nil, post aborted." - return false - - if not message.padding.isNil(): - # This will make a copy - padding = some(@(makeOpenArray(message.padding, message.paddingLen))) - - # TODO: call can create `Exception`, why? - result = node.postMessage(asymKey, - symKey, - sigPrivKey, - ttl = message.ttl, - topic = message.topic, - payload = payload, - padding = padding, - powTime = message.powTime, - powTarget = message.powTarget) - -proc nimbus_subscribe_filter(options: ptr CFilterOptions, - handler: proc (msg: ptr CReceivedMessage, udata: pointer) - {.gcsafe, cdecl, raises: [Defect].}, - udata: pointer = nil, id: var Identifier): bool {.exportc, dynlib.} = - ## Encryption is mandatory. - ## A symmetric key or an asymmetric key must be provided. Both is not allowed. - ## The received message needs to be copied before the passed handler ends. - doAssert(not (unsafeAddr id).isNil, "Key id cannot be nil.") - doAssert(not options.isNil, "Filter options pointer cannot be nil.") - doAssert(not handler.isNil, "Filter handler cannot be nil." ) - - var - src: Option[PublicKey] - symKey: Option[SymKey] - privateKey: Option[PrivateKey] - - if not options.privateKeyID.isNil() and not options.symKeyID.isNil(): - warn "Both symmetric and asymmetric keys are provided, choose one." - return false - - if options.privateKeyID.isNil() and options.symKeyID.isNil(): - warn "Both symmetric and asymmetric keys are nil, provide one." - return false - - if not options.source.isNil(): - let pubkey = PublicKey.fromRaw(makeOpenArray(options.source, 64)) - if pubkey.isErr: - error "Passed an invalid public key as source." - return false - src = some(pubkey[]) - - try: - if not options.symKeyID.isNil(): - let symKeyId = makeOpenArray(options.symKeyID, idLen).toHex() - symKey = some(whisperKeys.symKeys[symKeyId]) - if not options.privateKeyID.isNil(): - let privKeyId = makeOpenArray(options.privateKeyID, idLen).toHex() - privateKey = some(whisperKeys.asymKeys[privKeyId].seckey) - except KeyError: - return false - - let filter = initFilter(src, privateKey, symKey, @[options.topic], - options.minPow, options.allowP2P) - - proc c_handler(msg: ReceivedMessage) {.gcsafe, raises: [Defect].} = - var cmsg = CReceivedMessage( - decoded: unsafeAddr msg.decoded.payload[0], - decodedLen: msg.decoded.payload.len(), - timestamp: msg.timestamp, - ttl: msg.ttl, - topic: msg.topic, - pow: msg.pow, - hash: msg.hash - ) - - # Could also allocate here, but this should stay in scope until handler - # finishes so it should be fine. - var - source: array[RawPublicKeySize, byte] - recipientPublicKey: array[RawPublicKeySize, byte] - if msg.decoded.src.isSome(): - # Need to pass the serialized form - source = msg.decoded.src.get().toRaw() - cmsg.source = addr source[0] - if msg.dst.isSome(): - # Need to pass the serialized form - recipientPublicKey = msg.decoded.src.get().toRaw() - cmsg.recipientPublicKey = addr recipientPublicKey[0] - - handler(addr cmsg, udata) - - # TODO: call can create `Exception`, why? - # TODO: if we decide to internally also work with other IDs, we don't need - # to do this hex conversion back and forth. - hexToBytes(node.subscribeFilter(filter, c_handler), id) - - # Bloom filter has to follow only the subscribed topics - # TODO: better to have an "adding" proc here - # TODO: call can create `Exception`, why? - traceAsyncErrors node.setBloomFilter(node.filtersToBloom()) - result = true - -proc nimbus_unsubscribe_filter(id: Identifier): bool - {.exportc, dynlib, raises: [].} = - doAssert(not(unsafeAddr id).isNil, "Filter id cannot be nil.") - - result = node.unsubscribeFilter(id.toHex()) - -proc nimbus_get_min_pow(): float64 {.exportc, dynlib, raises: [].} = - result = node.protocolState(Whisper).config.powRequirement - -proc nimbus_get_bloom_filter(bloom: var Bloom) {.exportc, dynlib, raises: [].} = - doAssert(not (unsafeAddr bloom).isNil, "Bloom pointer cannot be nil.") - - bloom = node.protocolState(Whisper).config.bloom - -# Nimbus limited Status chat API - -# TODO: Return filter ID if we ever want to unsubscribe -proc subscribeChannel( - channel: string, handler: proc (msg: ReceivedMessage) - {.gcsafe, raises: [Defect].}) = - var ctx: HMAC[sha256] - var symKey: SymKey - discard ctx.pbkdf2(channel, "", 65356, symKey) - - let channelHash = digest(keccak256, channel) - var topic: array[4, byte] - for i in 0..<4: - topic[i] = channelHash.data[i] - - info "Subscribing to channel", channel, topic, symKey - - discard node.subscribeFilter(initFilter(symKey = some(symKey), - topics = @[topic]), - handler) - -proc nimbus_join_public_chat(channel: cstring, - handler: proc (msg: ptr CReceivedMessage) - {.gcsafe, cdecl, raises: [Defect].}) {.exportc, dynlib.} = - if handler.isNil: - subscribeChannel($channel, nil) - else: - proc c_handler(msg: ReceivedMessage) {.raises: [Defect].} = - var cmsg = CReceivedMessage( - decoded: unsafeAddr msg.decoded.payload[0], - decodedLen: msg.decoded.payload.len(), - timestamp: msg.timestamp, - ttl: msg.ttl, - topic: msg.topic, - pow: msg.pow, - hash: msg.hash - ) - - handler(addr cmsg) - - subscribeChannel($channel, c_handler) - -# TODO: Add signing key as parameter -# TODO: How would we do key management? In nimbus (like in rpc) or in status go? -proc nimbus_post_public(channel: cstring, payload: cstring) - {.exportc, dynlib.} = - let encPrivateKey = PrivateKey.fromHex("5dc5381cae54ba3174dc0d46040fe11614d0cc94d41185922585198b4fcef9d3")[] - - var ctx: HMAC[sha256] - var symKey: SymKey - var npayload = cast[seq[byte]]($payload) - discard ctx.pbkdf2($channel, "", 65356, symKey) - - let channelHash = digest(keccak256, $channel) - var topic: array[4, byte] - for i in 0..<4: - topic[i] = channelHash.data[i] - - # TODO: Handle error case - discard node.postMessage(symKey = some(symKey), - src = some(encPrivateKey), - ttl = 20, - topic = topic, - payload = npayload, - powTarget = 0.002) diff --git a/wrappers/wrapper_example.c b/wrappers/wrapper_example.c deleted file mode 100644 index 74fcf9e22..000000000 --- a/wrappers/wrapper_example.c +++ /dev/null @@ -1,46 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "libnimbus.h" - -void NimMain(); - -void print_msg(received_message* msg, void* udata) { - // Note: early null chars will terminate string early - printf("received message %.*s\n", (int)msg->decodedLen, msg->decoded); -} - -const char* channel = "status-test-c"; - -const char* msg = "testing message"; - -int main(int argc, char* argv[]) { - time_t lastmsg; - - NimMain(); - nimbus_start(30303, true, false, 0.002, NULL, false); - - nimbus_join_public_chat(channel, print_msg); - - lastmsg = time(NULL); - - while(1) { - usleep(1); - - if (lastmsg + 1 <= time(NULL)) { - lastmsg = time(NULL); - char buf[4096]; - snprintf(buf, 4095, - "[\"~#c4\",[\"%s\",\"text/plain\",\"~:public-group-user-message\",%ld,%ld,[\"^ \",\"~:chat-id\",\"%s\",\"~:text\",\"%s\"]]]", - msg, lastmsg * 1000 * 100, lastmsg * 1000, channel, msg); - - printf("Posting %s\n", buf); - nimbus_post_public(channel, buf); - } - nimbus_poll(); - } -} diff --git a/wrappers/wrapper_example.go b/wrappers/wrapper_example.go deleted file mode 100644 index a7b680009..000000000 --- a/wrappers/wrapper_example.go +++ /dev/null @@ -1,85 +0,0 @@ -package main - -import ( - "fmt" - "runtime" - "time" - "unsafe" -) - -/* -#include - -// Passing "-lnimbus" 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 -lnimbus -#include "libnimbus.h" - -void receiveHandler_cgo(received_message * msg); // Forward declaration. -*/ -import "C" - -// Arrange that main.main runs on main thread. -func init() { - runtime.LockOSThread() -} - -func poll() { - - for { - fmt.Println("POLLING") - time.Sleep(1 * time.Microsecond) - C.nimbus_poll() - } -} - -//export receiveHandler -func receiveHandler(msg *C.received_message) { - receivedMsg := C.GoBytes(unsafe.Pointer(msg.decoded), C.int(msg.decodedLen)) - fmt.Printf("[nim-status] received message %s\n", string(receivedMsg)) -} - -func Start() { - C.NimMain() - fmt.Println("[nim-status] Start Nimbus") - if C.nimbus_start(30306, true, false, 0.002, nil, false) == false { - panic("Can't start nimbus") - } - - peer1 := C.CString("enode://2d3e27d7846564f9b964308038dfadd4076e4373ac938e020708ad8819fd4fd90e5eb8314140768f782db704cb313b60707b968f8b61108a6fecd705b041746d@192.168.0.33:30303") - defer C.free(unsafe.Pointer(peer1)) - C.nimbus_add_peer(peer1) -} - -func StatusListenAndPost(channel string) { - fmt.Println("[nim-status] Status Public ListenAndPost") - channelC := C.CString(channel) - defer C.free(unsafe.Pointer(channelC)) - - C.nimbus_join_public_chat(channelC, - (C.received_msg_handler)(unsafe.Pointer(C.receiveHandler_cgo))) - i := 0 - for { - //fmt.Println("[nim-status] ListenAndPost (post @i==1000) i= ", i) - C.nimbus_poll() - t := time.Now().UnixNano() / int64(time.Millisecond) - i = i + 1 - time.Sleep(1 * time.Microsecond) - message := fmt.Sprintf("[\"~#c4\",[\"Message:%d\",\"text/plain\",\"~:public-group-user-message\",%d,%d,[\"^ \",\"~:chat-id\",\"%s\",\"~:text\",\"Message:%d\"]]]", i, t*100, t, channel, i) - if i%1000 == 0 { - fmt.Println("[nim-status] posting", message) - messageC := C.CString(message) - C.nimbus_post_public(channelC, messageC) - C.free(unsafe.Pointer(messageC)) - } - } -} - -func main() { - fmt.Println("Hi main") - - nprocs := runtime.GOMAXPROCS(0) - fmt.Println("GOMAXPROCS ", nprocs) - - Start() - StatusListenAndPost("status-test-go") -} diff --git a/wrappers/wrapper_whisper_example.go b/wrappers/wrapper_whisper_example.go deleted file mode 100644 index 333cbf4ed..000000000 --- a/wrappers/wrapper_whisper_example.go +++ /dev/null @@ -1,138 +0,0 @@ -package main - -import ( - "encoding/hex" - "fmt" - "runtime" - "time" - "unsafe" -) - -/* -#include -#include - -// Passing "-lnimbus" 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 -lnimbus -#include "libnimbus.h" - -void receiveHandler_cgo(received_message * msg, void* udata); // Forward declaration. -*/ -import "C" - -// Arrange that main.main runs on main thread. -func init() { - runtime.LockOSThread() -} - -//export receiveHandler -func receiveHandler(msg *C.received_message, udata unsafe.Pointer) { - receivedMsg := C.GoBytes(unsafe.Pointer(msg.decoded), C.int(msg.decodedLen)) - fmt.Printf("[nim-status] received message %s\n", string(receivedMsg)) - if msg.source != nil { - source := C.GoBytes(unsafe.Pointer(msg.source), 64) - fmt.Printf("[nim-status] source public key %x\n", string(source)) - } - msgCount := (*int)(udata) - *msgCount += 1 - fmt.Printf("[nim-status] message count %d\n", *msgCount) -} - -func Start() { - C.NimMain() - fmt.Println("[nim-status] Start Nimbus") - - privKeyHex := "a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a617" - data, err := hex.DecodeString(privKeyHex) - if err != nil { - panic(err) - } - privKey := (*C.uint8_t)(C.CBytes(data)) - defer C.free(unsafe.Pointer(privKey)) - - if C.nimbus_start(30306, true, false, 0.002, privKey, false) == false { - panic("Can't start nimbus") - } -} - -func StatusListenAndPost(channel string) { - fmt.Println("[nim-status] Status Public ListenAndPost") - - channelC := C.CString(channel) - defer C.free(unsafe.Pointer(channelC)) - - tmp := C.malloc(C.size_t(C.ID_LEN)) - if C.nimbus_add_symkey_from_password(channelC, (*C.uint8_t)(tmp)) == false { - C.free(unsafe.Pointer(tmp)) - panic("Cannot create symmetric key") - } - // No need to do this back and forth GO <-> C, just showing how it might work - // in implementations (when wrapped in calls passing Go Bytes or Strings). - symKeyId := C.GoBytes(tmp, C.ID_LEN) - C.free(unsafe.Pointer(tmp)) - symKeyIdC := (*C.uint8_t)(C.CBytes(symKeyId)) - defer C.free(unsafe.Pointer(symKeyIdC)) - - tmp = C.malloc(C.size_t(C.ID_LEN)) - if C.nimbus_new_keypair((*C.uint8_t)(tmp)) == false { - C.free(unsafe.Pointer(tmp)) - panic("Cannot create asymmetric keypair") - } - // No need to do this back and forth GO <-> C, just showing how it might work - // in implementations (when wrapped in calls passing Go Bytes or Strings). - asymKeyId := C.GoBytes(tmp, C.ID_LEN) - C.free(unsafe.Pointer(tmp)) - asymKeyIdC := (*C.uint8_t)(C.CBytes(asymKeyId)) - defer C.free(unsafe.Pointer(asymKeyIdC)) - - var msgCount int = 0 - - options := C.filter_options{symKeyID: symKeyIdC, - minPow: 0.002, - topic: C.nimbus_channel_to_topic(channelC).topic} - - tmp = C.malloc(C.size_t(C.ID_LEN)) - if C.nimbus_subscribe_filter(&options, - (C.received_msg_handler)(unsafe.Pointer(C.receiveHandler_cgo)), - unsafe.Pointer(&msgCount), (*C.uint8_t)(tmp)) == false { - C.free(unsafe.Pointer(tmp)) - panic("Cannot subscribe filter") - } - filterId := C.GoBytes(tmp, C.ID_LEN) - C.free(unsafe.Pointer(tmp)) - fmt.Printf("[nim-status] filter subscribed, id: %s\n", - hex.EncodeToString(filterId)) - - postMessage := C.post_message{symKeyID: symKeyIdC, - sourceID: asymKeyIdC, - ttl: 20, - topic: C.nimbus_channel_to_topic(channelC).topic, - powTarget: 0.002, - powTime: 1.0} - - i := 0 - for { - C.nimbus_poll() - t := time.Now().UnixNano() / int64(time.Millisecond) - i = i + 1 - time.Sleep(1 * time.Microsecond) - message := fmt.Sprintf("[\"~#c4\",[\"Message:%d\",\"text/plain\",\"~:public-group-user-message\",%d,%d,[\"^ \",\"~:chat-id\",\"%s\",\"~:text\",\"Message:%d\"]]]", i, t*100, t, channel, i) - if i%1000 == 0 { - fmt.Printf("[nim-status] posting msg number %d: %s\n", msgCount, message) - postMessage.payload = (*C.uint8_t)(C.CBytes([]byte(message))) - postMessage.payloadLen = (C.size_t)(len([]byte(message))) - defer C.free(unsafe.Pointer(postMessage.payload)) - if C.nimbus_post(&postMessage) == false { - fmt.Println("[nim-status] message could not be added to queue") - } - } - } -} - -func main() { - nprocs := runtime.GOMAXPROCS(0) - fmt.Println("GOMAXPROCS ", nprocs) - - Start() - StatusListenAndPost("status-test-go") -}