mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-02-24 17:58:30 +00:00
Whisper: Remove C and Go wrappers and Nimbus as a library
Remove the C and Go example wrappers that call Nimbus as a library, by removing the entire `wrappers/` directory. They are removed because they only wrap Whisper protocol support, which has been removed as it is obsolete. The only thing wrapped were Whisper functions, even though there were separate `go_wrapper_example` and `go_wrapper_whisper_example` programs. The wrappers don't build without Whisper in Nimbus, and without it, there isn't enough left for them to be useful examples. Also remove support for building the whole of Nimbus as a library, because there is nothing left using it. These targets are gone from the Makefile: - `wrappers` - `wrappers-static` - `libnimbus.so` - `libnimbus.a` The code isn't really gone, because it remains available in Git history. It may be useful someday, so a comment has been left in the Makefile for future generations: > 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/*`. Also worth a note, the library support was not tested on Windows, and the shared library support was only tested on Linux. Signed-off-by: Jamie Lokier <jamie@shareable.org>
This commit is contained in:
parent
cf36bdb801
commit
ecb0654da7
57
Makefile
57
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
|
||||
|
||||
|
@ -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.
|
@ -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"
|
@ -1,132 +0,0 @@
|
||||
#ifndef __LIBNIMBUS_H__
|
||||
#define __LIBNIMBUS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#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__
|
@ -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)
|
@ -1,46 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
|
||||
// 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")
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// 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")
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user