refactors signal sending + localStorage.set() tests. Fixes #75

This commit is contained in:
Victor Farazdagi 2016-12-18 23:36:17 +03:00
parent c1d2cfde08
commit 473d997849
12 changed files with 205 additions and 127 deletions

View File

@ -4,12 +4,11 @@ import (
"testing"
)
// the actual test functions are in non-_test.go files (so that they can use cgo i.e. import "C")
// the only intent of these wrappers is for gotest can find what tests are exposed.
func TestExportedAPI(t *testing.T) {
allTestsDone := make(chan struct{}, 1)
go testExportedAPI(t, allTestsDone)
<- allTestsDone
<-allTestsDone
}

View File

@ -537,7 +537,7 @@ func testCompleteTransaction(t *testing.T) bool {
// replace transaction notification handler
var txHash = ""
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
var envelope geth.GethEvent
var envelope geth.SignalEnvelope
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
return
@ -616,7 +616,7 @@ func testCompleteMultipleQueuedTransactions(t *testing.T) bool {
// replace transaction notification handler
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
var txId string
var envelope geth.GethEvent
var envelope geth.SignalEnvelope
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
return
@ -745,7 +745,7 @@ func testDiscardTransaction(t *testing.T) bool {
var txId string
txFailedEventCalled := false
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
var envelope geth.GethEvent
var envelope geth.SignalEnvelope
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
return
@ -860,7 +860,7 @@ func testDiscardMultipleQueuedTransactions(t *testing.T) bool {
txFailedEventCallCount := 0
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
var txId string
var envelope geth.GethEvent
var envelope geth.SignalEnvelope
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
return
@ -1072,7 +1072,7 @@ func startTestNode(t *testing.T) <-chan struct{} {
waitForNodeStart := make(chan struct{}, 1)
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
t.Log(jsonEvent)
var envelope geth.GethEvent
var envelope geth.SignalEnvelope
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
return

View File

@ -1,11 +1,5 @@
package geth
/*
#include <stddef.h>
#include <stdbool.h>
extern bool StatusServiceSignalEvent( const char *jsonEvent );
*/
import "C"
import (
"encoding/json"
"errors"
@ -304,13 +298,10 @@ func (m *NodeManager) onNodeStarted() {
close(m.node.started)
// send signal up to native app
event := GethEvent{
SendSignal(SignalEnvelope{
Type: EventNodeStarted,
Event: struct{}{},
}
body, _ := json.Marshal(&event)
C.StatusServiceSignalEvent(C.CString(string(body)))
})
}
// populateStaticPeers connects current node with our publicly available LES cluster

View File

@ -1,16 +0,0 @@
package geth
/*
#include <stddef.h>
#include <stdbool.h>
extern bool StatusServiceSignalEvent( const char *jsonEvent );
*/
import "C"
const (
EventLocalStorageSet = "local_storage.set"
)
func SendSignal(data []byte) {
C.StatusServiceSignalEvent(C.CString(string(data)))
}

View File

@ -1,12 +1,5 @@
package geth
/*
#include <stddef.h>
#include <stdbool.h>
extern bool StatusServiceSignalEvent( const char *jsonEvent );
*/
import "C"
import (
"context"
"encoding/json"
@ -35,17 +28,14 @@ const (
)
func onSendTransactionRequest(queuedTx status.QueuedTx) {
event := GethEvent{
SendSignal(SignalEnvelope{
Type: EventTransactionQueued,
Event: SendTransactionEvent{
Id: string(queuedTx.Id),
Args: queuedTx.Args,
MessageId: messageIdFromContext(queuedTx.Context),
},
}
body, _ := json.Marshal(&event)
C.StatusServiceSignalEvent(C.CString(string(body)))
})
}
func onSendTransactionReturn(queuedTx *status.QueuedTx, err error) {
@ -59,7 +49,7 @@ func onSendTransactionReturn(queuedTx *status.QueuedTx, err error) {
}
// error occurred, signal up to application
event := GethEvent{
SendSignal(SignalEnvelope{
Type: EventTransactionFailed,
Event: ReturnSendTransactionEvent{
Id: string(queuedTx.Id),
@ -68,10 +58,7 @@ func onSendTransactionReturn(queuedTx *status.QueuedTx, err error) {
ErrorMessage: err.Error(),
ErrorCode: sendTransactionErrorCode(err),
},
}
body, _ := json.Marshal(&event)
C.StatusServiceSignalEvent(C.CString(string(body)))
})
}
func sendTransactionErrorCode(err error) string {

View File

@ -35,7 +35,7 @@ func TestQueuedTransactions(t *testing.T) {
// replace transaction notification handler
var txHash = common.Hash{}
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
var envelope geth.GethEvent
var envelope geth.SignalEnvelope
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
return
@ -107,7 +107,7 @@ func TestDoubleCompleteQueuedTransactions(t *testing.T) {
txFailedEventCalled := false
txHash := common.Hash{}
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
var envelope geth.GethEvent
var envelope geth.SignalEnvelope
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
return
@ -230,7 +230,7 @@ func TestDiscardQueuedTransactions(t *testing.T) {
var txId string
txFailedEventCalled := false
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
var envelope geth.GethEvent
var envelope geth.SignalEnvelope
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
return
@ -345,7 +345,7 @@ func TestCompleteMultipleQueuedTransactions(t *testing.T) {
// replace transaction notification handler
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
var txId string
var envelope geth.GethEvent
var envelope geth.SignalEnvelope
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
return
@ -471,7 +471,7 @@ func TestDiscardMultipleQueuedTransactions(t *testing.T) {
txFailedEventCallCount := 0
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
var txId string
var envelope geth.GethEvent
var envelope geth.SignalEnvelope
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
return
@ -613,7 +613,7 @@ func TestNonExistentQueuedTransactions(t *testing.T) {
// replace transaction notification handler
var txHash = common.Hash{}
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
var envelope geth.GethEvent
var envelope geth.SignalEnvelope
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
return
@ -667,7 +667,7 @@ func TestEvictionOfQueuedTransactions(t *testing.T) {
// replace transaction notification handler
var txHash = common.Hash{}
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
var envelope geth.GethEvent
var envelope geth.SignalEnvelope
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
return

View File

@ -5,6 +5,11 @@ import (
"github.com/ethereum/go-ethereum/les/status"
)
type SignalEnvelope struct {
Type string `json:"type"`
Event interface{} `json:"event"`
}
type AccountInfo struct {
Address string `json:"address"`
PubKey string `json:"pubkey"`
@ -41,10 +46,6 @@ type SendTransactionEvent struct {
MessageId string `json:"message_id"`
}
type LocalStorageEvent struct {
Data string `json:"data"`
}
type ReturnSendTransactionEvent struct {
Id string `json:"id"`
Args status.SendTxArgs `json:"args"`
@ -81,9 +82,9 @@ type DiscardTransactionsResult struct {
Results map[string]DiscardTransactionResult `json:"results"`
}
type GethEvent struct {
Type string `json:"type"`
Event interface{} `json:"event"`
type LocalStorageSetEvent struct {
ChatId string `json:"chat_id"`
Data string `json:"data"`
}
type RPCCall struct {

View File

@ -8,6 +8,7 @@ extern bool StatusServiceSignalEvent(const char *jsonEvent);
import "C"
import (
"bytes"
"encoding/json"
"io"
"os"
"path"
@ -39,12 +40,18 @@ func SetDefaultNodeNotificationHandler(fn NodeNotificationHandler) {
notificationHandler = fn
}
// SendSignal sends application signal (JSON, normally) upwards
func SendSignal(signal SignalEnvelope) {
data, _ := json.Marshal(&signal)
C.StatusServiceSignalEvent(C.CString(string(data)))
}
//export NotifyNode
func NotifyNode(jsonEvent *C.char) {
notificationHandler(C.GoString(jsonEvent))
}
// export TriggerTestSignal
//export TriggerTestSignal
func TriggerTestSignal() {
C.StatusServiceSignalEvent(C.CString(`{"answer": 42}`))
}

View File

@ -1,13 +1,6 @@
package geth
/*
#include <stddef.h>
#include <stdbool.h>
extern bool StatusServiceSignalEvent( const char *jsonEvent );
*/
import "C"
import (
"encoding/json"
"time"
"github.com/ethereum/go-ethereum/common"
@ -20,7 +13,7 @@ var (
)
func onWhisperMessage(message *whisper.Message) {
event := GethEvent{
SendSignal(SignalEnvelope{
Type: "whisper",
Event: WhisperMessageEvent{
Payload: string(message.Payload),
@ -30,9 +23,7 @@ func onWhisperMessage(message *whisper.Message) {
TTL: int64(message.TTL / time.Second),
Hash: common.ToHex(message.Hash.Bytes()),
},
}
body, _ := json.Marshal(&event)
C.StatusServiceSignalEvent(C.CString(string(body)))
})
}
func AddWhisperFilter(args whisper.NewFilterArgs) int {

59
jail/handlers.go Normal file
View File

@ -0,0 +1,59 @@
package jail
import (
"github.com/robertkrimen/otto"
"github.com/status-im/status-go/geth"
)
const (
EventLocalStorageSet = "local_storage.set"
LocalStorageMaxDataLen = 256
)
// makeSendHandler returns jeth.send() and jeth.sendAsync() handler
func makeSendHandler(jail *Jail, chatId string) func(call otto.FunctionCall) (response otto.Value) {
return func(call otto.FunctionCall) (response otto.Value) {
return jail.Send(chatId, call)
}
}
// makeJethIsConnectedHandler returns jeth.isConnected() handler
func makeJethIsConnectedHandler(jail *Jail) func(call otto.FunctionCall) (response otto.Value) {
return func(call otto.FunctionCall) otto.Value {
client, err := jail.RPCClient()
if err != nil {
return newErrorResponse(call, -32603, err.Error(), nil)
}
var netListeningResult bool
if err := client.Call(&netListeningResult, "net_listening"); err != nil {
return newErrorResponse(call, -32603, err.Error(), nil)
}
if netListeningResult != true {
return newErrorResponse(call, -32603, geth.ErrInvalidGethNode.Error(), nil)
}
return newResultResponse(call, true)
}
}
// makeLocalStorageSetHandler returns localStorage.set() handler
func makeLocalStorageSetHandler(chatId string) func(call otto.FunctionCall) (response otto.Value) {
return func(call otto.FunctionCall) otto.Value {
data := call.Argument(0).String()
if len(data) > LocalStorageMaxDataLen { // cap input string
data = data[:LocalStorageMaxDataLen]
}
geth.SendSignal(geth.SignalEnvelope{
Type: EventLocalStorageSet,
Event: geth.LocalStorageSetEvent{
ChatId: chatId,
Data: data,
},
})
return newResultResponse(call, true)
}
}

View File

@ -69,22 +69,6 @@ func NewJailedRuntime(id string) *JailedRuntime {
}
}
func localStorageSetData(call otto.FunctionCall) otto.Value {
data := call.Argument(0).String()
event := geth.GethEvent{
Type: geth.EventLocalStorageSet,
Event: geth.LocalStorageEvent{
Data: data,
},
}
body, _ := json.Marshal(&event)
geth.SendSignal(body)
return otto.Value{}
}
func (jail *Jail) Parse(chatId string, js string) string {
if jail == nil {
return printError(ErrInvalidJail.Error())
@ -98,22 +82,18 @@ func (jail *Jail) Parse(chatId string, js string) string {
initJjs := jail.statusJS + ";"
_, err := vm.Run(initJjs)
// jeth and its handlers
vm.Set("jeth", struct{}{})
sendHandler := func(call otto.FunctionCall) (response otto.Value) {
return jail.Send(chatId, call)
}
jethObj, _ := vm.Get("jeth")
jethObj.Object().Set("send", sendHandler)
jethObj.Object().Set("sendAsync", sendHandler)
jethObj.Object().Set("isConnected", func(call otto.FunctionCall) (response otto.Value) {
return jail.IsConnected(call)
})
jethObj.Object().Set("send", makeSendHandler(jail, chatId))
jethObj.Object().Set("sendAsync", makeSendHandler(jail, chatId))
jethObj.Object().Set("isConnected", makeJethIsConnectedHandler(jail))
// localStorage and its handlers
vm.Set("localStorage", struct{}{})
localStorage, _ := vm.Get("localStorage")
localStorage.Object().Set("set", localStorageSetData)
localStorage.Object().Set("set", makeLocalStorageSetHandler(chatId))
jjs := Web3_JS + `
var Web3 = require('web3');
@ -166,26 +146,6 @@ func (jail *Jail) GetVM(chatId string) (*otto.Otto, error) {
return cell.vm, nil
}
func (jail *Jail) IsConnected(call otto.FunctionCall) otto.Value {
resp, _ := call.Otto.Object(`({"jsonrpc":"2.0", "result": "true"})`)
client, err := jail.RPCClient()
if err != nil {
return newErrorResponse(call, -32603, err.Error(), nil)
}
var netListeningResult bool
if err := client.Call(&netListeningResult, "net_listening"); err != nil {
return newErrorResponse(call, -32603, err.Error(), nil)
}
if netListeningResult != true {
return newErrorResponse(call, -32603, geth.ErrInvalidGethNode.Error(), nil)
}
return resp.Value()
}
// Send will serialize the first argument, send it to the node and returns the response.
func (jail *Jail) Send(chatId string, call otto.FunctionCall) (response otto.Value) {
client, err := jail.RPCClient()
@ -207,7 +167,7 @@ func (jail *Jail) Send(chatId string, call otto.FunctionCall) (response otto.Val
var (
rawReq = []byte(reqVal.String())
reqs []geth.RPCCall
batch bool
batch bool
)
if rawReq[0] == '[' {
batch = true
@ -349,6 +309,13 @@ func newErrorResponse(call otto.FunctionCall, code int, msg string, id interface
return val
}
func newResultResponse(call otto.FunctionCall, result interface{}) otto.Value {
resp, _ := call.Otto.Object(`({"jsonrpc":"2.0"})`)
resp.Set("result", result)
return resp.Value()
}
// throwJSException panics on an otto.Value. The Otto VM will recover from the
// Go panic and throw msg as a JavaScript error.
func throwJSException(msg interface{}) otto.Value {

View File

@ -221,7 +221,7 @@ func TestJailSendQueuedTransaction(t *testing.T) {
// replace transaction notification handler
requireMessageId := false
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
var envelope geth.GethEvent
var envelope geth.SignalEnvelope
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
return
@ -458,7 +458,99 @@ func TestIsConnected(t *testing.T) {
return
}
expectedResponse := `{"jsonrpc":"2.0","result":"true"}`
expectedResponse := `{"jsonrpc":"2.0","result":true}`
if !reflect.DeepEqual(response, expectedResponse) {
t.Errorf("expected response is not returned: expected %s, got %s", expectedResponse, response)
return
}
}
func TestLocalStorageSet(t *testing.T) {
err := geth.PrepareTestNode()
if err != nil {
t.Error(err)
return
}
jailInstance := jail.Init("")
jailInstance.Parse(CHAT_ID_CALL, "")
// obtain VM for a given chat (to send custom JS to jailed version of Send())
vm, err := jailInstance.GetVM(CHAT_ID_CALL)
if err != nil {
t.Errorf("cannot get VM: %v", err)
return
}
testData := "foobar"
opCompletedSuccessfully := make(chan struct{}, 1)
// replace transaction notification handler
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
var envelope geth.SignalEnvelope
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
return
}
if envelope.Type == jail.EventLocalStorageSet {
event := envelope.Event.(map[string]interface{})
chatId, ok := event["chat_id"].(string)
if !ok {
t.Error("Chat id is required, but not found")
return
}
if chatId != CHAT_ID_CALL {
t.Errorf("incorrect chat id: expected %q, got: %q", CHAT_ID_CALL, chatId)
return
}
actualData, ok := event["data"].(string)
if !ok {
t.Error("Data field is required, but not found")
return
}
if actualData != testData {
t.Errorf("incorrect data: expected %q, got: %q", testData, actualData)
return
}
t.Logf("event processed: %s", jsonEvent)
opCompletedSuccessfully <- struct{}{} // so that timeout is aborted
}
})
_, err = vm.Run(`
var responseValue = localStorage.set("` + testData + `");
responseValue = JSON.stringify(responseValue);
`)
if err != nil {
t.Errorf("cannot run custom code on VM: %v", err)
return
}
// make sure that signal is sent (and its parameters are correct)
select {
case <-opCompletedSuccessfully:
// pass
case <-time.After(3 * time.Second):
t.Error("operation timed out")
}
responseValue, err := vm.Get("responseValue")
if err != nil {
t.Errorf("cannot obtain result of localStorage.set(): %v", err)
return
}
response, err := responseValue.ToString()
if err != nil {
t.Errorf("cannot parse result: %v", err)
return
}
expectedResponse := `{"jsonrpc":"2.0","result":true}`
if !reflect.DeepEqual(response, expectedResponse) {
t.Errorf("expected response is not returned: expected %s, got %s", expectedResponse, response)
return