diff --git a/keycard_context.go b/keycard_context.go index cbc3a31..edef88f 100644 --- a/keycard_context.go +++ b/keycard_context.go @@ -9,6 +9,7 @@ import ( "github.com/status-im/keycard-go/globalplatform" "github.com/status-im/keycard-go/io" "github.com/status-im/keycard-go/types" + "github.com/status-im/nim-keycard-go/go/keycard/signal" ) type keycardContext struct { @@ -91,6 +92,7 @@ func (kc *keycardContext) run() { return } + signal.SendKeycardConnected("") l("card found at index %d", index) reader := kc.readers[index] diff --git a/main.go b/main.go index 1d5c1c6..f24c79e 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,9 @@ import ( "encoding/json" "fmt" "time" + "unsafe" + + "github.com/status-im/nim-keycard-go/go/keycard/signal" ) var kctx *keycardContext @@ -15,6 +18,10 @@ func main() { } func example() { + signal.SetDefaultNodeNotificationHandler(func(jsonEvent string) { + fmt.Printf("SIGNAL %+v\n", jsonEvent) + }) + fmt.Printf("RUNNING EXAMPLE \n") res := Start() fmt.Printf("*** start %+v\n", C.GoString(res)) @@ -399,3 +406,8 @@ func ChangePairingPassword(jsonParams *C.char) *C.char { return retValue("ok", true) } + +//export SetSignalEventCallback +func SetSignalEventCallback(cb unsafe.Pointer) { + signal.SetSignalEventCallback(cb) +} diff --git a/signal/events_keycard.go b/signal/events_keycard.go new file mode 100644 index 0000000..3127046 --- /dev/null +++ b/signal/events_keycard.go @@ -0,0 +1,10 @@ +package signal + +const ( + // EventSignRequestAdded is triggered when send transaction request is queued + EventKeycardConnected = "keycard.connected" +) + +func SendKeycardConnected(event interface{}) { + send(EventKeycardConnected, event) +} diff --git a/signal/signals.c b/signal/signals.c new file mode 100644 index 0000000..8274bea --- /dev/null +++ b/signal/signals.c @@ -0,0 +1,25 @@ +// ====================================================================================== +// cgo compilation (for desktop platforms and local tests) +// ====================================================================================== + +#include +#include +#include +#include "_cgo_export.h" + +typedef void (*callback)(const char *jsonEvent); +callback gCallback = 0; + +bool StatusServiceSignalEvent(const char *jsonEvent) { + if (gCallback) { + gCallback(jsonEvent); + } else { + NotifyNode((char *)jsonEvent); // re-send notification back to status node + } + + return true; +} + +void SetEventCallback(void *cb) { + gCallback = (callback)cb; +} diff --git a/signal/signals.go b/signal/signals.go new file mode 100644 index 0000000..8073a63 --- /dev/null +++ b/signal/signals.go @@ -0,0 +1,116 @@ +package signal + +/* +#include +#include +#include +extern bool StatusServiceSignalEvent(const char *jsonEvent); +extern void SetEventCallback(void *cb); +*/ +import "C" +import ( + "encoding/json" + "unsafe" + + "sync" + + "github.com/ethereum/go-ethereum/log" +) + +// MobileSignalHandler is a simple callback function that gets called when any signal is received +type MobileSignalHandler func([]byte) + +// storing the current signal handler here +var mobileSignalHandler MobileSignalHandler + +// All general log messages in this package should be routed through this logger. +var logger = log.New("package", "status-go/signal") + +// Envelope is a general signal sent upward from node to RN app +type Envelope struct { + Type string `json:"type"` + Event interface{} `json:"event"` +} + +// NewEnvelope creates new envlope of given type and event payload. +func NewEnvelope(typ string, event interface{}) *Envelope { + return &Envelope{ + Type: typ, + Event: event, + } +} + +// send sends application signal (in JSON) upwards to application (via default notification handler) +func send(typ string, event interface{}) { + signal := NewEnvelope(typ, event) + data, err := json.Marshal(&signal) + if err != nil { + logger.Error("Marshalling signal envelope", "error", err) + return + } + // If a Go implementation of signal handler is set, let's use it. + if mobileSignalHandler != nil { + mobileSignalHandler(data) + } else { + // ...and fallback to C implementation otherwise. + str := C.CString(string(data)) + C.StatusServiceSignalEvent(str) + C.free(unsafe.Pointer(str)) + } +} + +// NodeNotificationHandler defines a handler able to process incoming node events. +// Events are encoded as JSON strings. +type NodeNotificationHandler func(jsonEvent string) + +var notificationHandler NodeNotificationHandler = TriggerDefaultNodeNotificationHandler + +// notificationHandlerMutex guards notificationHandler for concurrent calls +var notificationHandlerMutex sync.RWMutex + +// SetDefaultNodeNotificationHandler sets notification handler to invoke on Send +func SetDefaultNodeNotificationHandler(fn NodeNotificationHandler) { + notificationHandlerMutex.Lock() + notificationHandler = fn + notificationHandlerMutex.Unlock() +} + +// ResetDefaultNodeNotificationHandler sets notification handler to default one +func ResetDefaultNodeNotificationHandler() { + notificationHandlerMutex.Lock() + notificationHandler = TriggerDefaultNodeNotificationHandler + notificationHandlerMutex.Unlock() +} + +// TriggerDefaultNodeNotificationHandler triggers default notification handler (helpful in tests) +func TriggerDefaultNodeNotificationHandler(jsonEvent string) { + logger.Trace("Notification received", "event", jsonEvent) +} + +//export NotifyNode +//nolint: golint +func NotifyNode(jsonEvent *C.char) { + notificationHandlerMutex.RLock() + defer notificationHandlerMutex.RUnlock() + notificationHandler(C.GoString(jsonEvent)) +} + +//export TriggerTestSignal +//nolint: golint +func TriggerTestSignal() { + str := C.CString(`{"answer": 42}`) + C.StatusServiceSignalEvent(str) + C.free(unsafe.Pointer(str)) +} + +// SetMobileSignalHandler sets new handler for geth events +// this function uses pure go implementation +func SetMobileSignalHandler(handler MobileSignalHandler) { + mobileSignalHandler = handler +} + +// SetSignalEventCallback set callback +// this function uses C implementation (see `signals.c` file) +func SetSignalEventCallback(cb unsafe.Pointer) { + C.SetEventCallback(cb) +}