2016-12-18 20:36:17 +00:00
|
|
|
package jail
|
|
|
|
|
|
|
|
import (
|
2017-08-04 16:14:17 +00:00
|
|
|
"os"
|
|
|
|
|
2016-12-18 20:36:17 +00:00
|
|
|
"github.com/robertkrimen/otto"
|
2017-08-04 16:14:17 +00:00
|
|
|
"github.com/status-im/status-go/geth/jail/console"
|
2017-09-25 18:22:57 +00:00
|
|
|
"github.com/status-im/status-go/geth/signal"
|
2016-12-18 20:36:17 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2017-11-07 17:36:42 +00:00
|
|
|
// EventSignal is a signal from jail.
|
2017-09-18 12:13:32 +00:00
|
|
|
EventSignal = "jail.signal"
|
2017-11-07 17:36:42 +00:00
|
|
|
// eventConsoleLog defines the event type for the console.log call.
|
2017-08-04 16:14:17 +00:00
|
|
|
eventConsoleLog = "vm.console.log"
|
2016-12-18 20:36:17 +00:00
|
|
|
)
|
|
|
|
|
2017-11-07 17:36:42 +00:00
|
|
|
// registerWeb3Provider creates an object called "jeth",
|
|
|
|
// which is a web3.js provider.
|
|
|
|
func registerWeb3Provider(jail *Jail, cell *Cell) error {
|
|
|
|
jeth := map[string]interface{}{
|
|
|
|
"console": map[string]interface{}{
|
|
|
|
"log": func(fn otto.FunctionCall) otto.Value {
|
|
|
|
return console.Write(fn, os.Stdout, eventConsoleLog)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"send": createSendHandler(jail, cell),
|
|
|
|
"sendAsync": createSendAsyncHandler(jail, cell),
|
|
|
|
"isConnected": createIsConnectedHandler(jail),
|
2017-05-03 14:24:48 +00:00
|
|
|
}
|
2017-08-04 16:14:17 +00:00
|
|
|
|
2017-11-07 17:36:42 +00:00
|
|
|
return cell.Set("jeth", jeth)
|
|
|
|
}
|
2017-05-03 14:24:48 +00:00
|
|
|
|
2017-11-07 17:36:42 +00:00
|
|
|
// registerStatusSignals creates an object called "statusSignals".
|
|
|
|
// TODO(adam): describe what it is and when it's used.
|
|
|
|
func registerStatusSignals(cell *Cell) error {
|
|
|
|
statusSignals := map[string]interface{}{
|
|
|
|
"sendSignal": createSendSignalHandler(cell),
|
2017-08-04 16:14:17 +00:00
|
|
|
}
|
|
|
|
|
2017-11-07 17:36:42 +00:00
|
|
|
return cell.Set("statusSignals", statusSignals)
|
|
|
|
}
|
2017-05-03 14:24:48 +00:00
|
|
|
|
2017-11-07 17:36:42 +00:00
|
|
|
// createSendHandler returns jeth.send().
|
|
|
|
func createSendHandler(jail *Jail, cell *Cell) func(call otto.FunctionCall) otto.Value {
|
|
|
|
return func(call otto.FunctionCall) otto.Value {
|
|
|
|
// As it's a sync call, it's called already from a thread-safe context,
|
|
|
|
// thus using otto.Otto directly. Otherwise, it would try to acquire a lock again
|
|
|
|
// and result in a deadlock.
|
|
|
|
vm := cell.VM.UnsafeVM()
|
|
|
|
|
|
|
|
request, err := vm.Call("JSON.stringify", nil, call.Argument(0))
|
|
|
|
if err != nil {
|
|
|
|
throwJSError(err)
|
|
|
|
}
|
2017-05-03 14:24:48 +00:00
|
|
|
|
2017-11-07 17:36:42 +00:00
|
|
|
response, err := jail.sendRPCCall(request.String())
|
|
|
|
if err != nil {
|
|
|
|
throwJSError(err)
|
|
|
|
}
|
2017-05-03 14:24:48 +00:00
|
|
|
|
2017-11-07 17:36:42 +00:00
|
|
|
value, err := vm.ToValue(response)
|
|
|
|
if err != nil {
|
|
|
|
throwJSError(err)
|
|
|
|
}
|
2017-05-03 14:24:48 +00:00
|
|
|
|
2017-11-07 17:36:42 +00:00
|
|
|
return value
|
|
|
|
}
|
2017-05-03 14:24:48 +00:00
|
|
|
}
|
|
|
|
|
2017-11-07 17:36:42 +00:00
|
|
|
// createSendAsyncHandler returns jeth.sendAsync() handler.
|
|
|
|
func createSendAsyncHandler(jail *Jail, cell *Cell) func(call otto.FunctionCall) otto.Value {
|
2017-09-18 12:13:32 +00:00
|
|
|
return func(call otto.FunctionCall) otto.Value {
|
2017-11-07 17:36:42 +00:00
|
|
|
// As it's a sync call, it's called already from a thread-safe context,
|
|
|
|
// thus using otto.Otto directly. Otherwise, it would try to acquire a lock again
|
|
|
|
// and result in a deadlock.
|
|
|
|
unsafeVM := cell.VM.UnsafeVM()
|
|
|
|
|
|
|
|
request, err := unsafeVM.Call("JSON.stringify", nil, call.Argument(0))
|
|
|
|
if err != nil {
|
|
|
|
throwJSError(err)
|
|
|
|
}
|
2017-09-18 12:13:32 +00:00
|
|
|
|
2017-11-07 17:36:42 +00:00
|
|
|
go func() {
|
|
|
|
// As it's an async call, it's not called from a thread-safe context,
|
|
|
|
// thus using a thread-safe vm.VM.
|
|
|
|
vm := cell.VM
|
2017-10-06 16:52:26 +00:00
|
|
|
callback := call.Argument(1)
|
2017-11-07 17:36:42 +00:00
|
|
|
response, err := jail.sendRPCCall(request.String())
|
|
|
|
|
|
|
|
// If provided callback argument is not a function, don't call it.
|
|
|
|
if callback.Class() != "Function" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-12-28 20:50:36 +00:00
|
|
|
// nolint: errcheck
|
2017-11-07 17:36:42 +00:00
|
|
|
if err != nil {
|
|
|
|
cell.CallAsync(callback, vm.MakeCustomError("Error", err.Error()))
|
|
|
|
} else {
|
|
|
|
cell.CallAsync(callback, nil, response)
|
2017-10-12 09:15:07 +00:00
|
|
|
}
|
2017-09-18 12:13:32 +00:00
|
|
|
}()
|
|
|
|
|
2017-11-07 17:36:42 +00:00
|
|
|
return otto.UndefinedValue()
|
2017-09-18 12:13:32 +00:00
|
|
|
}
|
2016-12-18 20:36:17 +00:00
|
|
|
}
|
|
|
|
|
2017-11-07 17:36:42 +00:00
|
|
|
// createIsConnectedHandler returns jeth.isConnected() handler.
|
|
|
|
// This handler returns `true` if client is actively listening for network connections.
|
|
|
|
func createIsConnectedHandler(jail RPCClientProvider) func(call otto.FunctionCall) otto.Value {
|
2016-12-18 20:36:17 +00:00
|
|
|
return func(call otto.FunctionCall) otto.Value {
|
2017-11-07 17:36:42 +00:00
|
|
|
client := jail.RPCClient()
|
|
|
|
if client == nil {
|
|
|
|
throwJSError(ErrNoRPCClient)
|
|
|
|
}
|
2016-12-18 20:36:17 +00:00
|
|
|
|
|
|
|
var netListeningResult bool
|
|
|
|
if err := client.Call(&netListeningResult, "net_listening"); err != nil {
|
2017-11-07 17:36:42 +00:00
|
|
|
throwJSError(err)
|
2016-12-18 20:36:17 +00:00
|
|
|
}
|
|
|
|
|
2017-11-07 17:36:42 +00:00
|
|
|
if netListeningResult {
|
|
|
|
return otto.TrueValue()
|
2016-12-18 20:36:17 +00:00
|
|
|
}
|
|
|
|
|
2017-11-07 17:36:42 +00:00
|
|
|
return otto.FalseValue()
|
2016-12-18 20:36:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-07 17:36:42 +00:00
|
|
|
func createSendSignalHandler(cell *Cell) func(otto.FunctionCall) otto.Value {
|
2017-07-18 14:26:24 +00:00
|
|
|
return func(call otto.FunctionCall) otto.Value {
|
|
|
|
message := call.Argument(0).String()
|
|
|
|
|
2017-09-25 18:22:57 +00:00
|
|
|
signal.Send(signal.Envelope{
|
2017-09-14 10:43:01 +00:00
|
|
|
Type: EventSignal,
|
2017-11-07 17:36:42 +00:00
|
|
|
Event: struct {
|
|
|
|
ChatID string `json:"chat_id"`
|
|
|
|
Data string `json:"data"`
|
|
|
|
}{
|
|
|
|
ChatID: cell.id,
|
2017-09-14 10:43:01 +00:00
|
|
|
Data: message,
|
2017-07-18 14:26:24 +00:00
|
|
|
},
|
|
|
|
})
|
|
|
|
|
2017-11-07 17:36:42 +00:00
|
|
|
// As it's a sync call, it's called already from a thread-safe context,
|
|
|
|
// thus using otto.Otto directly. Otherwise, it would try to acquire a lock again
|
|
|
|
// and result in a deadlock.
|
|
|
|
vm := cell.VM.UnsafeVM()
|
|
|
|
|
|
|
|
value, err := wrapResultInValue(vm, otto.TrueValue())
|
|
|
|
if err != nil {
|
|
|
|
throwJSError(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// throwJSError calls panic with an error string. It should be called
|
|
|
|
// only in a context that handles panics like otto.Otto.
|
|
|
|
func throwJSError(err error) {
|
|
|
|
value, err := otto.ToValue(err.Error())
|
|
|
|
if err != nil {
|
|
|
|
panic(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
panic(value)
|
|
|
|
}
|
|
|
|
|
|
|
|
func wrapResultInValue(vm *otto.Otto, result interface{}) (value otto.Value, err error) {
|
|
|
|
value, err = vm.Run(`({})`)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
err = value.Object().Set("result", result)
|
|
|
|
if err != nil {
|
|
|
|
return
|
2017-07-18 14:26:24 +00:00
|
|
|
}
|
2017-11-07 17:36:42 +00:00
|
|
|
|
|
|
|
return
|
2017-07-18 14:26:24 +00:00
|
|
|
}
|