mirror of
https://github.com/status-im/status-go.git
synced 2025-01-28 07:27:00 +00:00
8153d935d2
The goal of this PR is to make geth/api tests to finally pass from the beginning to the end. I tried to achieve it here by: Removing calls to common.PanicAfter so that we know which tests fail the most, Better sync of some tests using channels, Small test improvements.
199 lines
5.6 KiB
Go
199 lines
5.6 KiB
Go
package jail
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
|
|
gethcommon "github.com/ethereum/go-ethereum/common"
|
|
gethrpc "github.com/ethereum/go-ethereum/rpc"
|
|
"github.com/robertkrimen/otto"
|
|
"github.com/status-im/status-go/geth/common"
|
|
"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, call otto.FunctionCall) (*otto.Object, error) {
|
|
if params.SendTransactionMethodName == req.Method {
|
|
return ep.executeSendTransaction(req, call)
|
|
}
|
|
|
|
client := ep.nodeManager.RPCClient()
|
|
|
|
return ep.executeWithClient(client, req, call)
|
|
}
|
|
|
|
// executeRemoteSendTransaction defines a function to execute RPC method eth_sendTransaction over the upstream server.
|
|
func (ep *ExecutionPolicy) executeSendTransaction(req common.RPCCall, call otto.FunctionCall) (*otto.Object, error) {
|
|
res, err := call.Otto.Object(`({"jsonrpc":"2.0"})`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res.Set("id", req.ID)
|
|
|
|
messageID, err := preProcessRequest(call.Otto, req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// TODO(adam): check if context is used
|
|
ctx := context.WithValue(context.Background(), common.MessageIDKey, messageID)
|
|
args := sendTxArgsFromRPCCall(req)
|
|
|
|
tx := ep.txQueueManager.CreateTransaction(ctx, args)
|
|
|
|
if err := ep.txQueueManager.QueueTransaction(tx); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := ep.txQueueManager.WaitForTransaction(tx); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// invoke post processing
|
|
postProcessRequest(call.Otto, req, messageID)
|
|
|
|
// @TODO(adam): which one is actually used?
|
|
res.Set("result", tx.Hash.Hex())
|
|
res.Set("hash", tx.Hash.Hex())
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func (ep *ExecutionPolicy) executeWithClient(client *rpc.Client, req common.RPCCall, call otto.FunctionCall) (*otto.Object, error) {
|
|
JSON, err := call.Otto.Object("JSON")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result json.RawMessage
|
|
|
|
resp, _ := call.Otto.Object(`({"jsonrpc":"2.0"})`)
|
|
resp.Set("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(call.Otto, req)
|
|
if err != nil {
|
|
return nil, common.StopRPCCallError{Err: err}
|
|
}
|
|
|
|
if client == nil {
|
|
resp = newErrorResponse(call.Otto, -32603, "RPC client is not available. Node is stopped?", &req.ID).Object()
|
|
} else {
|
|
err = client.Call(&result, req.Method, req.Params...)
|
|
if err != nil {
|
|
if err2, ok := err.(gethrpc.Error); ok {
|
|
resp.Set("error", map[string]interface{}{
|
|
"code": err2.ErrorCode(),
|
|
"message": err2.Error(),
|
|
})
|
|
} else {
|
|
resp = newErrorResponse(call.Otto, -32603, err.Error(), &req.ID).Object()
|
|
}
|
|
}
|
|
}
|
|
|
|
if result == nil {
|
|
// Special case null because it is decoded as an empty
|
|
// raw message for some reason.
|
|
resp.Set("result", otto.NullValue())
|
|
} else {
|
|
resultVal, callErr := JSON.Call("parse", string(result))
|
|
if callErr != nil {
|
|
resp = newErrorResponse(call.Otto, -32603, callErr.Error(), &req.ID).Object()
|
|
} else {
|
|
resp.Set("result", resultVal)
|
|
}
|
|
}
|
|
|
|
// do extra request post processing (setting back tx context)
|
|
postProcessRequest(call.Otto, req, messageID)
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
// preProcessRequest pre-processes a given RPC call to a given Otto VM
|
|
func preProcessRequest(vm *otto.Otto, req common.RPCCall) (string, error) {
|
|
messageID := currentMessageID(vm.Context())
|
|
|
|
return messageID, nil
|
|
}
|
|
|
|
// postProcessRequest post-processes a given RPC call to a given Otto VM
|
|
func postProcessRequest(vm *otto.Otto, 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(ctx otto.Context) string {
|
|
if statusObj, ok := ctx.Symbols["status"]; ok {
|
|
messageID, err := statusObj.Object().Get("message_id")
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
if messageID, err := messageID.ToString(); err == nil {
|
|
return messageID
|
|
}
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func sendTxArgsFromRPCCall(req common.RPCCall) common.SendTxArgs {
|
|
// no need to persist extra state for other requests
|
|
if req.Method != params.SendTransactionMethodName {
|
|
return common.SendTxArgs{}
|
|
}
|
|
|
|
var err error
|
|
var fromAddr, toAddr gethcommon.Address
|
|
|
|
fromAddr, err = req.ParseFromAddress()
|
|
if err != nil {
|
|
fromAddr = gethcommon.HexToAddress("0x0")
|
|
}
|
|
|
|
toAddr, err = req.ParseToAddress()
|
|
if err != nil {
|
|
toAddr = gethcommon.HexToAddress("0x0")
|
|
}
|
|
|
|
return common.SendTxArgs{
|
|
To: &toAddr,
|
|
From: fromAddr,
|
|
Value: req.ParseValue(),
|
|
Data: req.ParseData(),
|
|
Gas: req.ParseGas(),
|
|
GasPrice: req.ParseGasPrice(),
|
|
}
|
|
}
|