168 lines
4.4 KiB
Go
168 lines
4.4 KiB
Go
package jail
|
|
|
|
import (
|
|
"os"
|
|
|
|
"github.com/robertkrimen/otto"
|
|
"github.com/status-im/status-go/geth/jail/console"
|
|
"github.com/status-im/status-go/signal"
|
|
)
|
|
|
|
// 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)
|
|
},
|
|
},
|
|
"send": createSendHandler(jail, cell),
|
|
"sendAsync": createSendAsyncHandler(jail, cell),
|
|
"isConnected": createIsConnectedHandler(jail),
|
|
}
|
|
|
|
return cell.jsvm.Set("jeth", jeth)
|
|
}
|
|
|
|
// 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),
|
|
}
|
|
|
|
return cell.jsvm.Set("statusSignals", statusSignals)
|
|
}
|
|
|
|
// 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.jsvm.UnsafeVM()
|
|
|
|
request, err := vm.Call("JSON.stringify", nil, call.Argument(0))
|
|
if err != nil {
|
|
throwJSError(err)
|
|
}
|
|
|
|
response, err := jail.sendRPCCall(request.String())
|
|
if err != nil {
|
|
throwJSError(err)
|
|
}
|
|
|
|
value, err := vm.ToValue(response)
|
|
if err != nil {
|
|
throwJSError(err)
|
|
}
|
|
|
|
return value
|
|
}
|
|
}
|
|
|
|
// createSendAsyncHandler returns jeth.sendAsync() handler.
|
|
func createSendAsyncHandler(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.
|
|
unsafeVM := cell.jsvm.UnsafeVM()
|
|
|
|
request, err := unsafeVM.Call("JSON.stringify", nil, call.Argument(0))
|
|
if err != nil {
|
|
throwJSError(err)
|
|
}
|
|
|
|
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.jsvm
|
|
callback := call.Argument(1)
|
|
response, err := jail.sendRPCCall(request.String())
|
|
|
|
// If provided callback argument is not a function, don't call it.
|
|
if callback.Class() != "Function" {
|
|
return
|
|
}
|
|
|
|
// nolint: errcheck
|
|
if err != nil {
|
|
cell.CallAsync(callback, vm.MakeCustomError("Error", err.Error()))
|
|
} else {
|
|
cell.CallAsync(callback, nil, response)
|
|
}
|
|
}()
|
|
|
|
return otto.UndefinedValue()
|
|
}
|
|
}
|
|
|
|
// 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 {
|
|
return func(call otto.FunctionCall) otto.Value {
|
|
client := jail.RPCClient()
|
|
if client == nil {
|
|
throwJSError(ErrNoRPCClient)
|
|
}
|
|
|
|
var netListeningResult bool
|
|
if err := client.Call(&netListeningResult, "net_listening"); err != nil {
|
|
throwJSError(err)
|
|
}
|
|
|
|
if netListeningResult {
|
|
return otto.TrueValue()
|
|
}
|
|
|
|
return otto.FalseValue()
|
|
}
|
|
}
|
|
|
|
func createSendSignalHandler(cell *Cell) func(otto.FunctionCall) otto.Value {
|
|
return func(call otto.FunctionCall) otto.Value {
|
|
message := call.Argument(0).String()
|
|
|
|
signal.SendJailSignal(cell.id, message)
|
|
|
|
// 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.jsvm.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
|
|
}
|
|
|
|
return
|
|
}
|