status-go/geth/jail/execution_policy.go
Ivan Tomilov fc8f59e121 Fixed eth_sendTransaction routing to the local node (#351)
* Fixed eth_sendTransaction routing to the local node
* Add local RPC handlers for eth_accounts and eth_sendTransaction
2017-09-25 18:04:40 +02:00

114 lines
3.4 KiB
Go

package jail
import (
"context"
gethrpc "github.com/ethereum/go-ethereum/rpc"
"github.com/status-im/status-go/geth/common"
"github.com/status-im/status-go/geth/jail/internal/vm"
"github.com/status-im/status-go/geth/params"
"github.com/status-im/status-go/geth/rpc"
)
// ExecutionPolicy provides a central container for the executions of RPCCall requests for both
// remote/upstream processing and internal node processing.
type ExecutionPolicy struct {
nodeManager common.NodeManager
accountManager common.AccountManager
txQueueManager common.TxQueueManager
}
// NewExecutionPolicy returns a new instance of ExecutionPolicy.
func NewExecutionPolicy(
nodeManager common.NodeManager, accountManager common.AccountManager, txQueueManager common.TxQueueManager,
) *ExecutionPolicy {
return &ExecutionPolicy{
nodeManager: nodeManager,
accountManager: accountManager,
txQueueManager: txQueueManager,
}
}
// Execute handles the execution of a RPC request and routes appropriately to either a local or remote ethereum node.
func (ep *ExecutionPolicy) Execute(req common.RPCCall, vm *vm.VM) (map[string]interface{}, error) {
client := ep.nodeManager.RPCClient()
return ep.executeWithClient(client, vm, req)
}
func (ep *ExecutionPolicy) executeWithClient(client *rpc.Client, vm *vm.VM, req common.RPCCall) (map[string]interface{}, error) {
// Arbitrary JSON-RPC response.
var result interface{}
resp := map[string]interface{}{
"jsonrpc": "2.0",
"id": req.ID,
}
// do extra request pre processing (persist message id)
// within function semaphore will be acquired and released,
// so that no more than one client (per cell) can enter
messageID, err := preProcessRequest(vm)
if err != nil {
return nil, common.StopRPCCallError{Err: err}
}
if client == nil {
resp = newErrorResponse("RPC client is not available. Node is stopped?", &req.ID)
} else {
// TODO(adam): check if context is used
ctx := context.WithValue(context.Background(), common.MessageIDKey, messageID)
err = client.CallContext(ctx, &result, req.Method, req.Params...)
if err != nil {
if err2, ok := err.(gethrpc.Error); ok {
resp["error"] = map[string]interface{}{
"code": err2.ErrorCode(),
"message": err2.Error(),
}
} else {
resp = newErrorResponse(err.Error(), &req.ID)
}
}
}
if result == nil {
// Special case null because it is decoded as an empty
// raw message for some reason.
resp["result"] = ""
} else {
resp["result"] = result
}
// do extra request post processing (setting back tx context)
postProcessRequest(vm, req, messageID)
return resp, nil
}
// preProcessRequest pre-processes a given RPC call to a given Otto VM
func preProcessRequest(vm *vm.VM) (string, error) {
messageID := currentMessageID(vm)
return messageID, nil
}
// postProcessRequest post-processes a given RPC call to a given Otto VM
func postProcessRequest(vm *vm.VM, req common.RPCCall, messageID string) {
if len(messageID) > 0 {
vm.Call("addContext", nil, messageID, common.MessageIDKey, messageID) // nolint: errcheck
}
// set extra markers for queued transaction requests
if req.Method == params.SendTransactionMethodName {
vm.Call("addContext", nil, messageID, params.SendTransactionMethodName, true) // nolint: errcheck
}
}
// currentMessageID looks for `status.message_id` variable in current JS context
func currentMessageID(vm *vm.VM) string {
msgID, err := vm.Run("status.message_id")
if err != nil {
return ""
}
return msgID.String()
}