Merge pull request #82 from farazdagi/feature/local-storage
Feature/local storage
This commit is contained in:
commit
80510c02d2
|
@ -4,7 +4,6 @@ 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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"`
|
||||
|
@ -77,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 {
|
||||
|
|
|
@ -8,6 +8,7 @@ extern bool StatusServiceSignalEvent(const char *jsonEvent);
|
|||
import "C"
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
|
@ -39,6 +40,12 @@ 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))
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
47
jail/jail.go
47
jail/jail.go
|
@ -82,18 +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", makeLocalStorageSetHandler(chatId))
|
||||
|
||||
jjs := Web3_JS + `
|
||||
var Web3 = require('web3');
|
||||
|
@ -146,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()
|
||||
|
@ -329,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 {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue