213 lines
6.0 KiB
Go
213 lines
6.0 KiB
Go
// +build nimbus
|
|
|
|
package nimbusbridge
|
|
|
|
// https://golang.org/cmd/cgo/
|
|
|
|
/*
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <libnimbus.h>
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"container/list"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
"unsafe"
|
|
|
|
"github.com/status-im/status-go/eth-node/types"
|
|
)
|
|
|
|
type nimbusPublicWhisperAPIWrapper struct {
|
|
filterMessagesMu *sync.Mutex
|
|
filterMessages *map[string]*list.List
|
|
routineQueue *RoutineQueue
|
|
}
|
|
|
|
// NewNimbusPublicWhisperAPIWrapper returns an object that wraps Nimbus's PublicWhisperAPI in a types interface
|
|
func NewNimbusPublicWhisperAPIWrapper(filterMessagesMu *sync.Mutex, filterMessages *map[string]*list.List, routineQueue *RoutineQueue) types.PublicWhisperAPI {
|
|
return &nimbusPublicWhisperAPIWrapper{
|
|
filterMessagesMu: filterMessagesMu,
|
|
filterMessages: filterMessages,
|
|
routineQueue: routineQueue,
|
|
}
|
|
}
|
|
|
|
// AddPrivateKey imports the given private key.
|
|
func (w *nimbusPublicWhisperAPIWrapper) AddPrivateKey(ctx context.Context, privateKey types.HexBytes) (string, error) {
|
|
retVal := w.routineQueue.Send(func(c chan<- callReturn) {
|
|
privKeyC := C.CBytes(privateKey)
|
|
defer C.free(unsafe.Pointer(privKeyC))
|
|
|
|
idC := C.malloc(C.size_t(C.ID_LEN))
|
|
defer C.free(idC)
|
|
if C.nimbus_add_keypair((*C.uchar)(privKeyC), (*C.uchar)(idC)) {
|
|
c <- callReturn{value: types.EncodeHex(C.GoBytes(idC, C.ID_LEN))}
|
|
} else {
|
|
c <- callReturn{err: errors.New("failed to add private key to Nimbus")}
|
|
}
|
|
})
|
|
if retVal.err != nil {
|
|
return "", retVal.err
|
|
}
|
|
|
|
return retVal.value.(string), 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) {
|
|
retVal := w.routineQueue.Send(func(c chan<- callReturn) {
|
|
passwordC := C.CString(passwd)
|
|
defer C.free(unsafe.Pointer(passwordC))
|
|
|
|
idC := C.malloc(C.size_t(C.ID_LEN))
|
|
defer C.free(idC)
|
|
if C.nimbus_add_symkey_from_password(passwordC, (*C.uchar)(idC)) {
|
|
c <- callReturn{value: types.EncodeHex(C.GoBytes(idC, C.ID_LEN))}
|
|
} else {
|
|
c <- callReturn{err: errors.New("failed to add symkey to Nimbus")}
|
|
}
|
|
})
|
|
if retVal.err != nil {
|
|
return "", retVal.err
|
|
}
|
|
|
|
return retVal.value.(string), nil
|
|
}
|
|
|
|
// DeleteKeyPair removes the key with the given key if it exists.
|
|
func (w *nimbusPublicWhisperAPIWrapper) DeleteKeyPair(ctx context.Context, key string) (bool, error) {
|
|
retVal := w.routineQueue.Send(func(c chan<- callReturn) {
|
|
keyC, err := decodeHexID(key)
|
|
if err != nil {
|
|
c <- callReturn{err: err}
|
|
return
|
|
}
|
|
defer C.free(unsafe.Pointer(keyC))
|
|
|
|
c <- callReturn{value: C.nimbus_delete_keypair(keyC)}
|
|
})
|
|
if retVal.err != nil {
|
|
return false, retVal.err
|
|
}
|
|
|
|
return retVal.value.(bool), 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 types.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) ([]*types.Message, error) {
|
|
idC := C.CString(id)
|
|
defer C.free(unsafe.Pointer(idC))
|
|
|
|
var (
|
|
messageList *list.List
|
|
ok bool
|
|
)
|
|
w.filterMessagesMu.Lock()
|
|
defer w.filterMessagesMu.Unlock()
|
|
if messageList, ok = (*w.filterMessages)[id]; !ok {
|
|
return nil, fmt.Errorf("no filter with ID %s", id)
|
|
}
|
|
|
|
retVal := make([]*types.Message, messageList.Len())
|
|
if messageList.Len() == 0 {
|
|
return retVal, nil
|
|
}
|
|
|
|
elem := messageList.Front()
|
|
index := 0
|
|
for elem != nil {
|
|
retVal[index] = (elem.Value).(*types.Message)
|
|
index++
|
|
next := elem.Next()
|
|
messageList.Remove(elem)
|
|
elem = next
|
|
}
|
|
return retVal, 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 types.NewMessage) ([]byte, error) {
|
|
retVal := w.routineQueue.Send(func(c chan<- callReturn) {
|
|
msg := C.post_message{
|
|
ttl: C.uint32_t(req.TTL),
|
|
powTime: C.double(req.PowTime),
|
|
powTarget: C.double(req.PowTarget),
|
|
}
|
|
if req.SigID != "" {
|
|
sourceID, err := decodeHexID(req.SigID)
|
|
if err != nil {
|
|
c <- callReturn{err: err}
|
|
return
|
|
}
|
|
msg.sourceID = sourceID
|
|
defer C.free(unsafe.Pointer(sourceID))
|
|
}
|
|
if req.SymKeyID != "" {
|
|
symKeyID, err := decodeHexID(req.SymKeyID)
|
|
if err != nil {
|
|
c <- callReturn{err: err}
|
|
return
|
|
}
|
|
msg.symKeyID = symKeyID
|
|
defer C.free(unsafe.Pointer(symKeyID))
|
|
}
|
|
if req.PublicKey != nil && len(req.PublicKey) > 0 {
|
|
msg.pubKey = (*C.uchar)(C.CBytes(req.PublicKey[1:]))
|
|
defer C.free(unsafe.Pointer(msg.pubKey))
|
|
}
|
|
msg.payloadLen = C.size_t(len(req.Payload))
|
|
msg.payload = (*C.uchar)(C.CBytes(req.Payload))
|
|
defer C.free(unsafe.Pointer(msg.payload))
|
|
msg.paddingLen = C.size_t(len(req.Padding))
|
|
msg.padding = (*C.uchar)(C.CBytes(req.Padding))
|
|
defer C.free(unsafe.Pointer(msg.padding))
|
|
copyTopicToCBuffer(&msg.topic[0], req.Topic[:])
|
|
|
|
// TODO: return envelope hash once nimbus_post is improved to return it
|
|
if C.nimbus_post(&msg) {
|
|
c <- callReturn{value: make([]byte, 0)}
|
|
return
|
|
}
|
|
c <- callReturn{err: fmt.Errorf("failed to post message symkeyid=%s pubkey=%#x topic=%#x", req.SymKeyID, req.PublicKey, req.Topic[:])}
|
|
// hashC := C.nimbus_post(&msg)
|
|
// if hashC == nil {
|
|
// return nil, errors.New("Nimbus failed to post message")
|
|
// }
|
|
// return hex.DecodeString(C.GoString(hashC))
|
|
})
|
|
if retVal.err != nil {
|
|
return nil, retVal.err
|
|
}
|
|
|
|
return retVal.value.([]byte), nil
|
|
}
|