web3 in jail
This commit is contained in:
parent
92737d0395
commit
47b01ec3c2
156
src/jail.go
156
src/jail.go
|
@ -4,6 +4,9 @@ import (
|
|||
"github.com/robertkrimen/otto"
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
var statusJs string
|
||||
|
@ -37,11 +40,25 @@ func printResult(res string, err error) string {
|
|||
|
||||
func Parse(chatId string, js string) string {
|
||||
vm := otto.New()
|
||||
jjs := statusJs + js + `
|
||||
var catalog = JSON.stringify(_status_catalog);
|
||||
`
|
||||
initJjs := statusJs + ";"
|
||||
vms[chatId] = vm
|
||||
_, err := vm.Run(jjs)
|
||||
_, err := vm.Run(initJjs)
|
||||
vm.Set("jeth", struct{}{})
|
||||
|
||||
jethObj, _ := vm.Get("jeth")
|
||||
jethObj.Object().Set("send", Send)
|
||||
jethObj.Object().Set("sendAsync", Send)
|
||||
|
||||
jjs := Web3_JS + `
|
||||
var Web3 = require('web3');
|
||||
var web3 = new Web3(jeth);
|
||||
var Bignumber = require("bignumber.js");
|
||||
function bn(val){
|
||||
return new Bignumber(val);
|
||||
}
|
||||
` + js + "; var catalog = JSON.stringify(_status_catalog);"
|
||||
vm.Run(jjs)
|
||||
|
||||
res, _ := vm.Get("catalog")
|
||||
|
||||
return printResult(res.String(), err)
|
||||
|
@ -57,3 +74,134 @@ func Call(chatId string, path string, args string) string {
|
|||
|
||||
return printResult(res.String(), err);
|
||||
}
|
||||
|
||||
// Send will serialize the first argument, send it to the node and returns the response.
|
||||
func Send(call otto.FunctionCall) (response otto.Value) {
|
||||
// Ensure that we've got a batch request (array) or a single request (object)
|
||||
arg := call.Argument(0).Object()
|
||||
if arg == nil || (arg.Class() != "Array" && arg.Class() != "Object") {
|
||||
throwJSException("request must be an object or array")
|
||||
}
|
||||
// Convert the otto VM arguments to Go values
|
||||
data, err := call.Otto.Call("JSON.stringify", nil, arg)
|
||||
if err != nil {
|
||||
throwJSException(err.Error())
|
||||
}
|
||||
reqjson, err := data.ToString()
|
||||
|
||||
if err != nil {
|
||||
throwJSException(err.Error())
|
||||
}
|
||||
|
||||
var (
|
||||
reqs []rpc.JSONRequest
|
||||
batch = true
|
||||
)
|
||||
if err = json.Unmarshal([]byte(reqjson), &reqs); err != nil {
|
||||
// single request?
|
||||
reqs = make([]rpc.JSONRequest, 1)
|
||||
if err = json.Unmarshal([]byte(reqjson), &reqs[0]); err != nil {
|
||||
throwJSException("invalid request")
|
||||
}
|
||||
batch = false
|
||||
}
|
||||
// Iteratively execute the requests
|
||||
call.Otto.Set("response_len", len(reqs))
|
||||
call.Otto.Run("var ret_response = new Array(response_len);")
|
||||
|
||||
for i, req := range reqs {
|
||||
// Execute the RPC request and parse the reply
|
||||
if err = client.Send(&req); err != nil {
|
||||
return newErrorResponse(call, -32603, err.Error(), req.Id)
|
||||
}
|
||||
result := make(map[string]interface{})
|
||||
if err = client.Recv(&result); err != nil {
|
||||
return newErrorResponse(call, -32603, err.Error(), req.Id)
|
||||
}
|
||||
// Feed the reply back into the JavaScript runtime environment
|
||||
id, _ := result["id"]
|
||||
jsonver, _ := result["jsonrpc"]
|
||||
|
||||
call.Otto.Set("ret_id", id)
|
||||
call.Otto.Set("ret_jsonrpc", jsonver)
|
||||
call.Otto.Set("response_idx", i)
|
||||
|
||||
if res, ok := result["result"]; ok {
|
||||
payload, _ := json.Marshal(res)
|
||||
call.Otto.Set("ret_result", string(payload))
|
||||
response, err = call.Otto.Run(`
|
||||
ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, result: JSON.parse(ret_result) };
|
||||
`)
|
||||
continue
|
||||
}
|
||||
if res, ok := result["error"]; ok {
|
||||
payload, _ := json.Marshal(res)
|
||||
call.Otto.Set("ret_result", string(payload))
|
||||
response, err = call.Otto.Run(`
|
||||
ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, error: JSON.parse(ret_result) };
|
||||
`)
|
||||
continue
|
||||
}
|
||||
return newErrorResponse(call, -32603, fmt.Sprintf("Invalid response"), new(int64))
|
||||
}
|
||||
// Convert single requests back from batch ones
|
||||
if !batch {
|
||||
call.Otto.Run("ret_response = ret_response[0];")
|
||||
}
|
||||
// Execute any registered callbacks
|
||||
if call.Argument(1).IsObject() {
|
||||
call.Otto.Set("callback", call.Argument(1))
|
||||
call.Otto.Run(`
|
||||
if (Object.prototype.toString.call(callback) == '[object Function]') {
|
||||
callback(null, ret_response);
|
||||
}
|
||||
`)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// newErrorResponse creates a JSON RPC error response for a specific request id,
|
||||
// containing the specified error code and error message. Beside returning the
|
||||
// error to the caller, it also sets the ret_error and ret_response JavaScript
|
||||
// variables.
|
||||
func newErrorResponse(call otto.FunctionCall, code int, msg string, id interface{}) (response otto.Value) {
|
||||
// Bundle the error into a JSON RPC call response
|
||||
res := rpc.JSONErrResponse{
|
||||
Version: "2.0",
|
||||
Id: id,
|
||||
Error: rpc.JSONError{
|
||||
Code: code,
|
||||
Message: msg,
|
||||
},
|
||||
}
|
||||
// Serialize the error response into JavaScript variables
|
||||
errObj, err := json.Marshal(res.Error)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Failed to serialize JSON RPC error: %v", err)
|
||||
}
|
||||
resObj, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Failed to serialize JSON RPC error response: %v", err)
|
||||
}
|
||||
|
||||
if _, err = call.Otto.Run("ret_error = " + string(errObj)); err != nil {
|
||||
glog.V(logger.Error).Infof("Failed to set `ret_error` to the occurred error: %v", err)
|
||||
}
|
||||
resVal, err := call.Otto.Run("ret_response = " + string(resObj))
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Failed to set `ret_response` to the JSON RPC response: %v", err)
|
||||
}
|
||||
return resVal
|
||||
}
|
||||
|
||||
|
||||
// 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 {
|
||||
val, err := otto.ToValue(msg)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Failed to serialize JavaScript exception %v: %v", msg, err)
|
||||
}
|
||||
panic(val)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/whisper"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -39,6 +40,7 @@ var (
|
|||
accountManager *accounts.Manager // the account manager attached to the currentNode
|
||||
whisperService *whisper.Whisper // whisper service
|
||||
datadir string // data directory for geth
|
||||
client rpc.Client
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -100,6 +102,7 @@ func RunNode(nodeIn *node.Node) {
|
|||
}
|
||||
lightEthereum.StatusBackend.SetTransactionQueueHandler(onSendTransactionRequest)
|
||||
|
||||
client, _ = nodeIn.Attach()
|
||||
nodeIn.Wait()
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue