Handle gracefully RPC calls when node is stopped (#1329)
This commit is contained in:
parent
b1f9030177
commit
08931fb761
|
@ -39,6 +39,9 @@ var (
|
|||
ErrWhisperIdentityInjectionFailure = errors.New("failed to inject identity into Whisper")
|
||||
// ErrUnsupportedRPCMethod is for methods not supported by the RPC interface
|
||||
ErrUnsupportedRPCMethod = errors.New("method is unsupported by RPC interface")
|
||||
// ErrRPCClientUnavailable is returned if an RPC client can't be retrieved.
|
||||
// This is a normal situation when a node is stopped.
|
||||
ErrRPCClientUnavailable = errors.New("JSON-RPC client is unavailable")
|
||||
)
|
||||
|
||||
// StatusBackend implements Status.im service
|
||||
|
@ -215,15 +218,21 @@ func (b *StatusBackend) ResetChainData() error {
|
|||
}
|
||||
|
||||
// CallRPC executes public RPC requests on node's in-proc RPC server.
|
||||
func (b *StatusBackend) CallRPC(inputJSON string) string {
|
||||
func (b *StatusBackend) CallRPC(inputJSON string) (string, error) {
|
||||
client := b.statusNode.RPCClient()
|
||||
return client.CallRaw(inputJSON)
|
||||
if client == nil {
|
||||
return "", ErrRPCClientUnavailable
|
||||
}
|
||||
return client.CallRaw(inputJSON), nil
|
||||
}
|
||||
|
||||
// CallPrivateRPC executes public and private RPC requests on node's in-proc RPC server.
|
||||
func (b *StatusBackend) CallPrivateRPC(inputJSON string) string {
|
||||
func (b *StatusBackend) CallPrivateRPC(inputJSON string) (string, error) {
|
||||
client := b.statusNode.RPCPrivateClient()
|
||||
return client.CallRaw(inputJSON)
|
||||
if client == nil {
|
||||
return "", ErrRPCClientUnavailable
|
||||
}
|
||||
return client.CallRaw(inputJSON), nil
|
||||
}
|
||||
|
||||
// SendTransaction creates a new transaction and waits until it's complete.
|
||||
|
|
|
@ -228,20 +228,22 @@ func TestBackendCallRPCConcurrently(t *testing.T) {
|
|||
for i := 0; i < count; i++ {
|
||||
wg.Add(1)
|
||||
go func(idx int) {
|
||||
result := backend.CallRPC(fmt.Sprintf(
|
||||
result, err := backend.CallRPC(fmt.Sprintf(
|
||||
`{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":%d}`,
|
||||
idx+1,
|
||||
))
|
||||
assert.NoError(t, err)
|
||||
assert.NotContains(t, result, "error")
|
||||
wg.Done()
|
||||
}(i)
|
||||
|
||||
wg.Add(1)
|
||||
go func(idx int) {
|
||||
result := backend.CallPrivateRPC(fmt.Sprintf(
|
||||
result, err := backend.CallPrivateRPC(fmt.Sprintf(
|
||||
`{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":%d}`,
|
||||
idx+1,
|
||||
))
|
||||
assert.NoError(t, err)
|
||||
assert.NotContains(t, result, "error")
|
||||
wg.Done()
|
||||
}(i)
|
||||
|
@ -291,13 +293,30 @@ func TestBlockedRPCMethods(t *testing.T) {
|
|||
defer func() { require.NoError(t, backend.StopNode()) }()
|
||||
|
||||
for idx, m := range rpc.BlockedMethods() {
|
||||
result := backend.CallRPC(fmt.Sprintf(
|
||||
result, err := backend.CallRPC(fmt.Sprintf(
|
||||
`{"jsonrpc":"2.0","method":"%s","params":[],"id":%d}`,
|
||||
m,
|
||||
idx+1,
|
||||
))
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, result, fmt.Sprintf(`{"code":-32700,"message":"%s"}`, rpc.ErrMethodNotFound))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCallRPCWithStoppedNode(t *testing.T) {
|
||||
backend := NewStatusBackend()
|
||||
|
||||
resp, err := backend.CallRPC(
|
||||
`{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}`,
|
||||
)
|
||||
assert.Equal(t, ErrRPCClientUnavailable, err)
|
||||
assert.Equal(t, "", resp)
|
||||
|
||||
resp, err = backend.CallPrivateRPC(
|
||||
`{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}`,
|
||||
)
|
||||
assert.Equal(t, ErrRPCClientUnavailable, err)
|
||||
assert.Equal(t, "", resp)
|
||||
}
|
||||
|
||||
// TODO(adam): add concurrent tests for: SendTransaction
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
"github.com/status-im/status-go/services/typeddata"
|
||||
"github.com/status-im/status-go/signal"
|
||||
"github.com/status-im/status-go/transactions"
|
||||
"gopkg.in/go-playground/validator.v9"
|
||||
validator "gopkg.in/go-playground/validator.v9"
|
||||
)
|
||||
|
||||
// All general log messages in this package should be routed through this logger.
|
||||
|
@ -224,14 +224,20 @@ func ResetChainData() *C.char {
|
|||
//CallRPC calls public APIs via RPC
|
||||
//export CallRPC
|
||||
func CallRPC(inputJSON *C.char) *C.char {
|
||||
outputJSON := statusBackend.CallRPC(C.GoString(inputJSON))
|
||||
outputJSON, err := statusBackend.CallRPC(C.GoString(inputJSON))
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
return C.CString(outputJSON)
|
||||
}
|
||||
|
||||
//CallPrivateRPC calls both public and private APIs via RPC
|
||||
//export CallPrivateRPC
|
||||
func CallPrivateRPC(inputJSON *C.char) *C.char {
|
||||
outputJSON := statusBackend.CallPrivateRPC(C.GoString(inputJSON))
|
||||
outputJSON, err := statusBackend.CallPrivateRPC(C.GoString(inputJSON))
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
return C.CString(outputJSON)
|
||||
}
|
||||
|
||||
|
|
|
@ -45,14 +45,18 @@ func (s *BaseJSONRPCSuite) AssertAPIMethodExportedPrivately(method string) {
|
|||
}
|
||||
|
||||
func (s *BaseJSONRPCSuite) isMethodExported(method string, private bool) bool {
|
||||
var result string
|
||||
var (
|
||||
result string
|
||||
err error
|
||||
)
|
||||
|
||||
cmd := fmt.Sprintf(`{"jsonrpc":"2.0", "method": "%s", "params": []}`, method)
|
||||
if private {
|
||||
result = s.Backend.CallPrivateRPC(cmd)
|
||||
result, err = s.Backend.CallPrivateRPC(cmd)
|
||||
} else {
|
||||
result = s.Backend.CallRPC(cmd)
|
||||
result, err = s.Backend.CallRPC(cmd)
|
||||
}
|
||||
s.NoError(err)
|
||||
|
||||
var response struct {
|
||||
Error *rpcError `json:"error"`
|
||||
|
|
|
@ -120,7 +120,9 @@ func (s *DebugAPISuite) sendPostConfirmMessage(symID string) string {
|
|||
`{"jsonrpc":"2.0","method":"debug_postSync","params":[%s],"id":67}`,
|
||||
body)
|
||||
|
||||
return s.Backend.CallPrivateRPC(basicCall)
|
||||
resp, err := s.Backend.CallPrivateRPC(basicCall)
|
||||
s.NoError(err)
|
||||
return resp
|
||||
}
|
||||
|
||||
// addPeers adds a peer to the running node
|
||||
|
|
|
@ -45,7 +45,8 @@ func (s *FiltersAPISuite) TestFilters() {
|
|||
|
||||
basicCall := `{"jsonrpc":"2.0","method":"eth_newBlockFilter","params":[],"id":67}`
|
||||
|
||||
response := s.Backend.CallRPC(basicCall)
|
||||
response, err := s.Backend.CallRPC(basicCall)
|
||||
s.NoError(err)
|
||||
filterID := s.filterIDFromRPCResponse(response)
|
||||
|
||||
// we don't check new blocks on private network, because no one mines them
|
||||
|
@ -65,7 +66,8 @@ func (s *FiltersAPISuite) TestFilters() {
|
|||
|
||||
basicCall = fmt.Sprintf(`{"jsonrpc":"2.0","method":"eth_uninstallFilter","params":["%s"],"id":67}`, filterID)
|
||||
|
||||
response = s.Backend.CallRPC(basicCall)
|
||||
response, err = s.Backend.CallRPC(basicCall)
|
||||
s.NoError(err)
|
||||
result := s.boolFromRPCResponse(response)
|
||||
|
||||
s.True(result, "filter expected to be removed successfully")
|
||||
|
@ -79,7 +81,8 @@ func (s *FiltersAPISuite) getFirstFilterChange(filterID string) chan string {
|
|||
timeout := time.Now().Add(time.Minute)
|
||||
for time.Now().Before(timeout) {
|
||||
basicCall := fmt.Sprintf(`{"jsonrpc":"2.0","method":"eth_getFilterChanges","params":["%s"],"id":67}`, filterID)
|
||||
response := s.Backend.CallRPC(basicCall)
|
||||
response, err := s.Backend.CallRPC(basicCall)
|
||||
s.NoError(err)
|
||||
filterChanges := s.arrayFromRPCResponse(response)
|
||||
if len(filterChanges) > 0 {
|
||||
result <- filterChanges[0]
|
||||
|
|
|
@ -67,8 +67,8 @@ func (s *PersonalSignSuite) TestPersonalSignUnsupportedMethod() {
|
|||
signDataString,
|
||||
TestConfig.Account1.Address)
|
||||
|
||||
rawResult := s.Backend.CallRPC(basicCall)
|
||||
|
||||
rawResult, err := s.Backend.CallRPC(basicCall)
|
||||
s.NoError(err)
|
||||
s.Contains(rawResult, `"error":{"code":-32700,"message":"method is unsupported by RPC interface"}`)
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ func (s *PersonalSignSuite) TestPersonalRecoverUnsupportedMethod() {
|
|||
signDataString,
|
||||
"")
|
||||
|
||||
rawResult := s.Backend.CallRPC(basicCall)
|
||||
|
||||
rawResult, err := s.Backend.CallRPC(basicCall)
|
||||
s.NoError(err)
|
||||
s.Contains(rawResult, `"error":{"code":-32700,"message":"method is unsupported by RPC interface"}`)
|
||||
}
|
||||
|
|
|
@ -125,7 +125,8 @@ func (s *StatusAPISuite) testStatusLogin(testParams statusTestParams) *status.Lo
|
|||
`{"jsonrpc":"2.0","method":"status_login","params":[%s],"id":67}`,
|
||||
body)
|
||||
|
||||
result := s.Backend.CallPrivateRPC(basicCall)
|
||||
result, err := s.Backend.CallPrivateRPC(basicCall)
|
||||
s.NoError(err)
|
||||
if testParams.ExpectedError == nil {
|
||||
var r struct {
|
||||
Error string `json:"error"`
|
||||
|
@ -164,7 +165,8 @@ func (s *StatusAPISuite) testStatusSignup(testParams statusTestParams) *status.S
|
|||
`{"jsonrpc":"2.0","method":"status_signup","params":[%s],"id":67}`,
|
||||
body)
|
||||
|
||||
result := s.Backend.CallPrivateRPC(basicCall)
|
||||
result, err := s.Backend.CallPrivateRPC(basicCall)
|
||||
s.NoError(err)
|
||||
|
||||
if testParams.ExpectedError == nil {
|
||||
var r struct {
|
||||
|
|
|
@ -67,11 +67,13 @@ func (s *TransactionsTestSuite) TestCallUpstreamPrivateRPCSendTransaction() {
|
|||
s.sendTransactionUsingRPCClient(s.Backend.CallPrivateRPC)
|
||||
}
|
||||
|
||||
func (s *TransactionsTestSuite) sendTransactionUsingRPCClient(callRPCFn func(string) string) {
|
||||
func (s *TransactionsTestSuite) sendTransactionUsingRPCClient(
|
||||
callRPCFn func(string) (string, error),
|
||||
) {
|
||||
err := s.Backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
|
||||
result := callRPCFn(`{
|
||||
result, err := callRPCFn(`{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "eth_sendTransaction",
|
||||
|
@ -81,6 +83,7 @@ func (s *TransactionsTestSuite) sendTransactionUsingRPCClient(callRPCFn func(str
|
|||
"value": "0x9184e72a"
|
||||
}]
|
||||
}`)
|
||||
s.NoError(err)
|
||||
s.Contains(result, `"error":{"code":-32700,"message":"method is unsupported by RPC interface"}`)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue