Skip to content
This repository has been archived by the owner on Nov 21, 2019. It is now read-only.

Commit

Permalink
Working commit of Nimbus bridge implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Pedro Pombeiro committed Oct 28, 2019
1 parent 1a6c399 commit 30d6fa5
Show file tree
Hide file tree
Showing 10 changed files with 571 additions and 16 deletions.
9 changes: 8 additions & 1 deletion bridge/geth/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@ import (

type gethFilterWrapper struct {
filter *whisper.Filter
id string
}

// NewGethFilterWrapper returns an object that wraps Geth's Filter in a whispertypes interface
func NewGethFilterWrapper(f *whisper.Filter) whispertypes.Filter {
func NewGethFilterWrapper(f *whisper.Filter, id string) whispertypes.Filter {
if f.Messages == nil {
panic("Messages should not be nil")
}

return &gethFilterWrapper{
filter: f,
id: id,
}
}

Expand All @@ -27,6 +29,11 @@ func GetGethFilterFrom(f whispertypes.Filter) *whisper.Filter {
return f.(*gethFilterWrapper).filter
}

// ID returns the filter ID
func (w *gethFilterWrapper) ID() string {
return w.id
}

// KeyAsym returns the private Key of recipient
func (w *gethFilterWrapper) KeyAsym() *ecdsa.PrivateKey {
return w.filter.KeyAsym
Expand Down
21 changes: 15 additions & 6 deletions bridge/geth/whisper.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,20 +108,29 @@ func (w *gethWhisperWrapper) GetSymKey(id string) ([]byte, error) {
return w.whisper.GetSymKey(id)
}

func (w *gethWhisperWrapper) Subscribe(f whispertypes.Filter) (string, error) {
return w.whisper.Subscribe(GetGethFilterFrom(f))
func (w *gethWhisperWrapper) Subscribe(keyAsym *ecdsa.PrivateKey, keySym []byte, pow float64, topics [][]byte) (string, error) {
f, err := w.createFilterWrapper("", keyAsym, keySym, pow, topics)
if err != nil {
return "", err
}

id, err := w.whisper.Subscribe(GetGethFilterFrom(f))
if err != nil {
return "", err
}

f.(*gethFilterWrapper).id = id
return id, nil
}

func (w *gethWhisperWrapper) GetFilter(id string) whispertypes.Filter {
return NewGethFilterWrapper(w.whisper.GetFilter(id))
return NewGethFilterWrapper(w.whisper.GetFilter(id), id)
}

func (w *gethWhisperWrapper) Unsubscribe(id string) error {
return w.whisper.Unsubscribe(id)
}

func (w *gethWhisperWrapper) createFilterWrapper(id string, keyAsym *ecdsa.PrivateKey, keySym []byte, pow float64, topics [][]byte, messages whispertypes.MessageStore) (whispertypes.Filter, error) {
func (w *gethWhisperWrapper) CreateFilterWrapper(keyAsym *ecdsa.PrivateKey, keySym []byte, pow float64, topics [][]byte, messages whispertypes.MessageStore) whispertypes.Filter {
func (w *gethWhisperWrapper) createFilterWrapper(id string, keyAsym *ecdsa.PrivateKey, keySym []byte, pow float64, topics [][]byte) (whispertypes.Filter, error) {
return NewGethFilterWrapper(&whisper.Filter{
KeyAsym: keyAsym,
Expand All @@ -130,7 +139,7 @@ func (w *gethWhisperWrapper) createFilterWrapper(id string, keyAsym *ecdsa.Priva
AllowP2P: true,
Topics: topics,
Messages: whisper.NewMemoryMessageStore(),
})
}, id), nil
}

// RequestHistoricMessages sends a message with p2pRequestCode to a specific peer,
Expand Down
84 changes: 84 additions & 0 deletions bridge/nimbus/filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package nimbusbridge

// https://golang.org/cmd/cgo/

/*
#include <stddef.h>
#include <stdbool.h>
#include <stdlib.h>
#include <libnim.h>
*/
import "C"

import (
"crypto/ecdsa"
"unsafe"

"github.com/ethereum/go-ethereum/crypto"
whispertypes "github.com/status-im/status-protocol-go/transport/whisper/types"
)

type nimbusFilterWrapper struct {
filter *C.filter
id string
own bool
pk *ecdsa.PrivateKey
keySym [whispertypes.AesKeyLength]byte
}

// NewNimbusFilterWrapper returns an object that wraps Nimbus's Filter in a whispertypes interface
func NewNimbusFilterWrapper(f *C.filter, id string, own bool) whispertypes.Filter {
key, err := crypto.HexToECDSA(C.GoString(f.keyAsymHex))
if err != nil {
panic(err)
}

wrapper := &nimbusFilterWrapper{
filter: f,
id: id,
own: own,
pk: key,
}
copy(wrapper.keySym[:], C.GoBytes(unsafe.Pointer(f.keySym), whispertypes.AesKeyLength))
return wrapper
}

// GetNimbusFilterFrom retrieves the underlying whisper Filter struct from a wrapped Filter interface
func GetNimbusFilterFrom(f whispertypes.Filter) *C.filter {
return f.(*nimbusFilterWrapper).filter
}

// ID returns the filter ID
func (w *nimbusFilterWrapper) ID() string {
return w.id
}

// KeyAsym returns the private Key of recipient
func (w *nimbusFilterWrapper) KeyAsym() *ecdsa.PrivateKey {
return w.pk
}

// KeySym returns the key associated with the Topic
func (w *nimbusFilterWrapper) KeySym() []byte {
return w.keySym[:]
}

// Free frees the C memory associated with the filter
func (w *nimbusFilterWrapper) Free() {
if !w.own {
panic("native filter is not owned by Go")
}

if w.filter.topic != nil {
C.free(unsafe.Pointer(w.filter.topic))
w.filter.topic = nil
}
if w.filter.keyAsymHex != nil {
C.free(unsafe.Pointer(w.filter.keyAsymHex))
w.filter.keyAsymHex = nil
}
if w.filter.keySym != nil {
C.free(unsafe.Pointer(w.filter.keySym))
w.filter.keySym = nil
}
}
66 changes: 66 additions & 0 deletions bridge/nimbus/libnim.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#include <stdint.h>
#include <stddef.h>

#pragma once

#ifndef __LIBNIM_H__
#define __LIBNIM_H__

typedef struct {
uint8_t* decoded;
size_t decodedLen;
uint32_t timestamp;
uint32_t ttl;
uint8_t topic[4];
double pow;
uint8_t hash[32];
} received_message;

typedef struct {
const char* keyAsymHex;
const uint8_t* keySym;
double pow;
bool allowP2P;
const uint8_t* topic;
} filter;

typedef struct {
uint8_t* sig; // Public key who signed this message.
uint8_t* recipientPublicKey; // The recipients public key.
uint32_t ttl; // Time-to-live in seconds.
uint32_t timestamp; // Unix timestamp of the message generation.
uint8_t topic[4]; // 4 Bytes: Message topic.
uint8_t* payload; // Decrypted payload.
uint32_t payloadLength; // Decrypted payload length.
uint8_t* padding; // (Optional) Padding (byte array of arbitrary length).
double pow; // Proof of work value.
uint8_t* hash; // Hash of the enveloped message.
} whisperfiltermessage;


typedef void (*received_msg_handler)(const received_message* msg);
// void nimbus_start(uint16_t port);
// void nimbus_poll();
// void NimMain();
const char* nimbus_post(const char* payload);
// void nimbus_subscribe(const char* channel, received_msg_handler msg);
// void nimbus_add_peer(const char* nodeId);

double nimbus_get_min_pow();
void nimbus_get_bloom_filter(uint8_t* pBloomFilter);

const char* nimbus_add_keypair(const char* pszHexPK);
bool nimbus_delete_keypair(const char* pszID);
const char* nimbus_get_private_key(const char* pszID);

const char* nimbus_add_symkey_direct(const uint8_t* pSymKey);
const char* nimbus_add_symkey_from_password(const char* pszPassword);
bool nimbus_get_symkey(const char* pszID, uint8_t* pSymKey);
bool nimbus_delete_symkey(const char* pszID);

const char* nimbus_subscribe_filter(const filter* pcFilter, received_msg_handler msg);
const filter* nimbus_get_filter(const char* pszID);
bool nimbus_unsubscribe_filter(const char* pszID);
whisperfiltermessage* nimbus_getfiltermessages(const char* pszID, uint32_t* count);

#endif // __LIBNIM_H__
143 changes: 143 additions & 0 deletions bridge/nimbus/public_whisper_api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package nimbusbridge

// https://golang.org/cmd/cgo/

/*
#include <stddef.h>
#include <stdbool.h>
#include <stdlib.h>
#include <libnim.h>
*/
import "C"

import (
"context"
"encoding/hex"
"errors"
"unsafe"

whispertypes "github.com/status-im/status-protocol-go/transport/whisper/types"
statusproto "github.com/status-im/status-protocol-go/types"
)

type nimbusPublicWhisperAPIWrapper struct {
}

// NewNimbusPublicWhisperAPIWrapper returns an object that wraps Nimbus's PublicWhisperAPI in a whispertypes interface
func NewNimbusPublicWhisperAPIWrapper() whispertypes.PublicWhisperAPI {
return &nimbusPublicWhisperAPIWrapper{}
}

// AddPrivateKey imports the given private key.
func (w *nimbusPublicWhisperAPIWrapper) AddPrivateKey(ctx context.Context, privateKey statusproto.HexBytes) (string, error) {
privKeyHexC := C.CString(statusproto.EncodeHex(privateKey))
defer C.free(unsafe.Pointer(privKeyHexC))

idC := C.nimbus_add_keypair(privKeyHexC)
if idC == nil {
return "", errors.New("failed to add private key to Nimbus")
}

return C.GoString(idC), nil
}

// GenerateSymKeyFromPassword derives a key from the given password, stores it, and returns its ID.
func (w *nimbusPublicWhisperAPIWrapper) GenerateSymKeyFromPassword(ctx context.Context, passwd string) (string, error) {
passwordC := C.CString(passwd)
defer C.free(unsafe.Pointer(passwordC))

idC := C.nimbus_add_symkey_from_password(passwordC)
if idC == nil {
return "", errors.New("failed to add symkey to Nimbus")
}

return C.GoString(idC), nil
}

// DeleteKeyPair removes the key with the given key if it exists.
func (w *nimbusPublicWhisperAPIWrapper) DeleteKeyPair(ctx context.Context, key string) (bool, error) {
keyC := C.CString(key)
defer C.free(unsafe.Pointer(keyC))

return bool(C.nimbus_delete_keypair(keyC)), nil
}

// NewMessageFilter creates a new filter that can be used to poll for
// (new) messages that satisfy the given criteria.
func (w *nimbusPublicWhisperAPIWrapper) NewMessageFilter(req whispertypes.Criteria) (string, error) {
// topics := make([]whisper.TopicType, len(req.Topics))
// for index, tt := range req.Topics {
// topics[index] = whisper.TopicType(tt)
// }

// criteria := whisper.Criteria{
// SymKeyID: req.SymKeyID,
// PrivateKeyID: req.PrivateKeyID,
// Sig: req.Sig,
// MinPow: req.MinPow,
// Topics: topics,
// AllowP2P: req.AllowP2P,
// }
// return w.publicWhisperAPI.NewMessageFilter(criteria)
// TODO
return "", errors.New("not implemented")
}

// GetFilterMessages returns the messages that match the filter criteria and
// are received between the last poll and now.
func (w *nimbusPublicWhisperAPIWrapper) GetFilterMessages(id string) ([]*whispertypes.Message, error) {
var count uint
var msgs *C.whisperfiltermessage
idC := C.CString(id)
defer C.free(unsafe.Pointer(idC))
msgs = C.nimbus_getfiltermessages(idC, (*C.uint)(unsafe.Pointer(&count)))
if msgs == nil {
return nil, errors.New("failed to retrieve filter messages from Nimbus")
}

wrappedMsgs := make([]*whispertypes.Message, count)
index := uint(0)
for index < count {
msg := (*C.whisperfiltermessage)(unsafe.Pointer(uintptr(unsafe.Pointer(msgs)) + unsafe.Sizeof(*msgs)))
topic := whispertypes.TopicType{}
copy(topic[:], C.GoBytes(unsafe.Pointer(&msg.topic[0]), whispertypes.TopicLength)[:whispertypes.TopicLength])
wrappedMsgs[index] = &whispertypes.Message{
Sig: C.GoBytes(unsafe.Pointer(msg.sig), whispertypes.AesKeyLength),
TTL: uint32(msg.ttl),
Timestamp: uint32(msg.timestamp),
Topic: topic,
Payload: C.GoBytes(unsafe.Pointer(msg.payload), C.int(msg.payloadLength)),
Padding: C.GoBytes(unsafe.Pointer(msg.padding), whispertypes.AesKeyLength),
PoW: float64(msg.pow),
Hash: C.GoBytes(unsafe.Pointer(msg.hash), statusproto.HashLength),
Dst: C.GoBytes(unsafe.Pointer(msg.recipientPublicKey), whispertypes.AesKeyLength),
P2P: true,
}
}
return wrappedMsgs, nil
}

// Post posts a message on the Whisper network.
// returns the hash of the message in case of success.
func (w *nimbusPublicWhisperAPIWrapper) Post(ctx context.Context, req whispertypes.NewMessage) ([]byte, error) {
// msg := whisper.NewMessage{
// SymKeyID: req.SymKeyID,
// PublicKey: req.PublicKey,
// Sig: req.Sig,
// TTL: req.TTL,
// Topic: whisper.TopicType(req.Topic),
// Payload: req.Payload,
// Padding: req.Padding,
// PowTime: req.PowTime,
// PowTarget: req.PowTarget,
// TargetPeer: req.TargetPeer,
// }
payloadC := C.CBytes(req.Payload)
defer C.free(unsafe.Pointer(payloadC))

hashC := C.nimbus_post((*C.char)(payloadC))
if hashC == nil {
return nil, errors.New("Nimbus failed to post message")
}
return hex.DecodeString(C.GoString(hashC))
}
Loading

0 comments on commit 30d6fa5

Please sign in to comment.