diff --git a/internal/rpc/core/events.go b/internal/rpc/core/events.go index 82235ae50..054183817 100644 --- a/internal/rpc/core/events.go +++ b/internal/rpc/core/events.go @@ -67,7 +67,7 @@ func (env *Environment) Subscribe(ctx context.Context, query string) (*coretypes return } else if errors.Is(err, tmpubsub.ErrTerminated) { // The subscription was terminated by the publisher. - resp := rpctypes.RPCServerError(subscriptionID, err) + resp := callInfo.RPCRequest.MakeError(err) ok := callInfo.WSConn.TryWriteRPCResponse(opctx, resp) if !ok { env.Logger.Info("Unable to write response (slow client)", @@ -77,7 +77,7 @@ func (env *Environment) Subscribe(ctx context.Context, query string) (*coretypes } // We have a message to deliver to the client. - resp := rpctypes.NewRPCSuccessResponse(subscriptionID, &coretypes.ResultEvent{ + resp := callInfo.RPCRequest.MakeResponse(&coretypes.ResultEvent{ Query: query, Data: msg.Data(), Events: msg.Events(), diff --git a/light/provider/http/http_test.go b/light/provider/http/http_test.go index 8749d4a11..4c7761d50 100644 --- a/light/provider/http/http_test.go +++ b/light/provider/http/http_test.go @@ -80,12 +80,12 @@ func TestProvider(t *testing.T) { lb, err = p.LightBlock(ctx, 9001) require.Error(t, err) require.Nil(t, lb) - assert.Equal(t, provider.ErrHeightTooHigh, err) + assert.ErrorIs(t, err, provider.ErrHeightTooHigh) lb, err = p.LightBlock(ctx, 1) require.Error(t, err) require.Nil(t, lb) - assert.Equal(t, provider.ErrLightBlockNotFound, err) + assert.ErrorIs(t, err, provider.ErrLightBlockNotFound) // if the provider is unable to provide four more blocks then we should return // an unreliable peer error diff --git a/light/rpc/client.go b/light/rpc/client.go index 79158f452..ba7a2c8fa 100644 --- a/light/rpc/client.go +++ b/light/rpc/client.go @@ -655,11 +655,7 @@ func (c *Client) SubscribeWS(ctx context.Context, query string) (*coretypes.Resu case resultEvent := <-out: // We should have a switch here that performs a validation // depending on the event's type. - callInfo.WSConn.TryWriteRPCResponse(bctx, - rpctypes.NewRPCSuccessResponse( - rpctypes.JSONRPCStringID(fmt.Sprintf("%v#event", callInfo.RPCRequest.ID)), - resultEvent, - )) + callInfo.WSConn.TryWriteRPCResponse(bctx, callInfo.RPCRequest.MakeResponse(resultEvent)) case <-bctx.Done(): return } diff --git a/rpc/jsonrpc/server/http_json_handler.go b/rpc/jsonrpc/server/http_json_handler.go index e41274a45..90defb5b5 100644 --- a/rpc/jsonrpc/server/http_json_handler.go +++ b/rpc/jsonrpc/server/http_json_handler.go @@ -13,7 +13,6 @@ import ( "strconv" "github.com/tendermint/tendermint/libs/log" - "github.com/tendermint/tendermint/rpc/coretypes" rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types" ) @@ -25,15 +24,15 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han // For POST requests, reject a non-root URL path. This should not happen // in the standard configuration, since the wrapper checks the path. if hreq.URL.Path != "/" { - writeRPCResponse(w, logger, rpctypes.RPCInvalidRequestError( - nil, fmt.Errorf("invalid path: %q", hreq.URL.Path))) + writeRPCResponse(w, logger, rpctypes.RPCRequest{}.MakeErrorf( + rpctypes.CodeInvalidRequest, "invalid path: %q", hreq.URL.Path)) return } b, err := io.ReadAll(hreq.Body) if err != nil { - writeRPCResponse(w, logger, rpctypes.RPCInvalidRequestError( - nil, fmt.Errorf("reading request body: %w", err))) + writeRPCResponse(w, logger, rpctypes.RPCRequest{}.MakeErrorf( + rpctypes.CodeInvalidRequest, "reading request body: %v", err)) return } @@ -46,7 +45,8 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han requests, err := parseRequests(b) if err != nil { - writeRPCResponse(w, logger, rpctypes.RPCParseError(fmt.Errorf("decoding request: %w", err))) + writeRPCResponse(w, logger, rpctypes.RPCRequest{}.MakeErrorf( + rpctypes.CodeParseError, "decoding request: %v", err)) return } @@ -60,7 +60,7 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han rpcFunc, ok := funcMap[req.Method] if !ok || rpcFunc.ws { - responses = append(responses, rpctypes.RPCMethodNotFoundError(req.ID)) + responses = append(responses, req.MakeErrorf(rpctypes.CodeMethodNotFound, req.Method)) continue } @@ -71,32 +71,17 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han }) args, err := parseParams(ctx, rpcFunc, req.Params) if err != nil { - responses = append(responses, rpctypes.RPCInvalidParamsError( - req.ID, fmt.Errorf("converting JSON parameters: %w", err))) + responses = append(responses, + req.MakeErrorf(rpctypes.CodeInvalidParams, "converting JSON parameters: %v", err)) continue } returns := rpcFunc.f.Call(args) - logger.Debug("HTTPJSONRPC", "method", req.Method, "args", args, "returns", returns) result, err := unreflectResult(returns) - switch e := err.(type) { - // if no error then return a success response - case nil: - responses = append(responses, rpctypes.NewRPCSuccessResponse(req.ID, result)) - - // if this already of type RPC error then forward that error - case *rpctypes.RPCError: - responses = append(responses, rpctypes.NewRPCErrorResponse(req.ID, e.Code, e.Message, e.Data)) - default: // we need to unwrap the error and parse it accordingly - switch errors.Unwrap(err) { - // check if the error was due to an invald request - case coretypes.ErrZeroOrNegativeHeight, coretypes.ErrZeroOrNegativePerPage, - coretypes.ErrPageOutOfRange, coretypes.ErrInvalidRequest: - responses = append(responses, rpctypes.RPCInvalidRequestError(req.ID, err)) - // lastly default all remaining errors as internal errors - default: // includes ctypes.ErrHeightNotAvailable and ctypes.ErrHeightExceedsChainHead - responses = append(responses, rpctypes.RPCInternalError(req.ID, err)) - } + if err == nil { + responses = append(responses, req.MakeResponse(result)) + } else { + responses = append(responses, req.MakeError(err)) } } diff --git a/rpc/jsonrpc/server/http_json_handler_test.go b/rpc/jsonrpc/server/http_json_handler_test.go index 2d56ef072..5e4df724a 100644 --- a/rpc/jsonrpc/server/http_json_handler_test.go +++ b/rpc/jsonrpc/server/http_json_handler_test.go @@ -63,14 +63,12 @@ func TestRPCParams(t *testing.T) { rec := httptest.NewRecorder() mux.ServeHTTP(rec, req) res := rec.Result() - defer res.Body.Close() + // Always expecting back a JSONRPCResponse assert.NotZero(t, res.StatusCode, "#%d: should always return code", i) blob, err := io.ReadAll(res.Body) - if err != nil { - t.Errorf("#%d: err reading body: %v", i, err) - continue - } + require.NoError(t, err, "#%d: reading body", i) + require.NoError(t, res.Body.Close()) recv := new(rpctypes.RPCResponse) assert.Nil(t, json.Unmarshal(blob, recv), "#%d: expecting successful parsing of an RPCResponse:\nblob: %s", i, blob) diff --git a/rpc/jsonrpc/server/http_server_test.go b/rpc/jsonrpc/server/http_server_test.go index ca67de708..12be80d1b 100644 --- a/rpc/jsonrpc/server/http_server_test.go +++ b/rpc/jsonrpc/server/http_server_test.go @@ -3,7 +3,6 @@ package server import ( "context" "crypto/tls" - "errors" "fmt" "io" "net" @@ -126,16 +125,15 @@ func TestServeTLS(t *testing.T) { } func TestWriteRPCResponse(t *testing.T) { - id := rpctypes.JSONRPCIntID(-1) + req := rpctypes.RPCRequest{ID: rpctypes.JSONRPCIntID(-1)} // one argument w := httptest.NewRecorder() logger := log.NewNopLogger() - writeRPCResponse(w, logger, - rpctypes.NewRPCSuccessResponse(id, &sampleResult{"hello"})) + writeRPCResponse(w, logger, req.MakeResponse(&sampleResult{"hello"})) resp := w.Result() body, err := io.ReadAll(resp.Body) - _ = resp.Body.Close() + require.NoError(t, resp.Body.Close()) require.NoError(t, err) assert.Equal(t, 200, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) @@ -145,12 +143,12 @@ func TestWriteRPCResponse(t *testing.T) { // multiple arguments w = httptest.NewRecorder() writeRPCResponse(w, logger, - rpctypes.NewRPCSuccessResponse(id, &sampleResult{"hello"}), - rpctypes.NewRPCSuccessResponse(id, &sampleResult{"world"}), + req.MakeResponse(&sampleResult{"hello"}), + req.MakeResponse(&sampleResult{"world"}), ) resp = w.Result() body, err = io.ReadAll(resp.Body) - _ = resp.Body.Close() + require.NoError(t, resp.Body.Close()) require.NoError(t, err) assert.Equal(t, 200, resp.StatusCode) @@ -162,11 +160,11 @@ func TestWriteRPCResponse(t *testing.T) { func TestWriteHTTPResponse(t *testing.T) { w := httptest.NewRecorder() logger := log.NewNopLogger() - writeHTTPResponse(w, logger, - rpctypes.RPCInternalError(rpctypes.JSONRPCIntID(-1), errors.New("foo"))) + req := rpctypes.RPCRequest{ID: rpctypes.JSONRPCIntID(-1)} + writeHTTPResponse(w, logger, req.MakeErrorf(rpctypes.CodeInternalError, "foo")) resp := w.Result() body, err := io.ReadAll(resp.Body) - _ = resp.Body.Close() + require.NoError(t, resp.Body.Close()) require.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) diff --git a/rpc/jsonrpc/server/http_uri_handler.go b/rpc/jsonrpc/server/http_uri_handler.go index 573631a38..8d626f225 100644 --- a/rpc/jsonrpc/server/http_uri_handler.go +++ b/rpc/jsonrpc/server/http_uri_handler.go @@ -4,7 +4,6 @@ import ( "context" "encoding/hex" "encoding/json" - "errors" "fmt" "net/http" "reflect" @@ -12,7 +11,6 @@ import ( "strings" "github.com/tendermint/tendermint/libs/log" - "github.com/tendermint/tendermint/rpc/coretypes" rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types" ) @@ -33,29 +31,15 @@ func makeHTTPHandler(rpcFunc *RPCFunc, logger log.Logger) func(http.ResponseWrit fmt.Fprintln(w, err.Error()) return } + jreq := rpctypes.RPCRequest{ID: uriReqID} outs := rpcFunc.f.Call(args) logger.Debug("HTTPRestRPC", "method", req.URL.Path, "args", args, "returns", outs) result, err := unreflectResult(outs) - switch e := err.(type) { - // if no error then return a success response - case nil: - writeHTTPResponse(w, logger, rpctypes.NewRPCSuccessResponse(uriReqID, result)) - - // if this already of type RPC error then forward that error. - case *rpctypes.RPCError: - writeHTTPResponse(w, logger, rpctypes.NewRPCErrorResponse(uriReqID, e.Code, e.Message, e.Data)) - - default: // we need to unwrap the error and parse it accordingly - switch errors.Unwrap(err) { - case coretypes.ErrZeroOrNegativeHeight, - coretypes.ErrZeroOrNegativePerPage, - coretypes.ErrPageOutOfRange, - coretypes.ErrInvalidRequest: - writeHTTPResponse(w, logger, rpctypes.RPCInvalidRequestError(uriReqID, err)) - default: // ctypes.ErrHeightNotAvailable, ctypes.ErrHeightExceedsChainHead: - writeHTTPResponse(w, logger, rpctypes.RPCInternalError(uriReqID, err)) - } + if err == nil { + writeHTTPResponse(w, logger, jreq.MakeResponse(result)) + } else { + writeHTTPResponse(w, logger, jreq.MakeError(err)) } } } diff --git a/rpc/jsonrpc/server/ws_handler.go b/rpc/jsonrpc/server/ws_handler.go index e511ef16c..2311d9b0b 100644 --- a/rpc/jsonrpc/server/ws_handler.go +++ b/rpc/jsonrpc/server/ws_handler.go @@ -3,7 +3,6 @@ package server import ( "context" "encoding/json" - "errors" "fmt" "net/http" "runtime/debug" @@ -13,7 +12,6 @@ import ( "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/rpc/client" - "github.com/tendermint/tendermint/rpc/coretypes" rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types" ) @@ -276,8 +274,10 @@ func (wsc *wsConnection) readRoutine(ctx context.Context) { if !ok { err = fmt.Errorf("WSJSONRPC: %v", r) } + req := rpctypes.RPCRequest{ID: uriReqID} wsc.Logger.Error("Panic in WSJSONRPC handler", "err", err, "stack", string(debug.Stack())) - if err := wsc.WriteRPCResponse(writeCtx, rpctypes.RPCInternalError(rpctypes.JSONRPCIntID(-1), err)); err != nil { + if err := wsc.WriteRPCResponse(writeCtx, + req.MakeErrorf(rpctypes.CodeInternalError, "Panic in handler: %v", err)); err != nil { wsc.Logger.Error("error writing RPC response", "err", err) } go wsc.readRoutine(ctx) @@ -317,7 +317,7 @@ func (wsc *wsConnection) readRoutine(ctx context.Context) { err = dec.Decode(&request) if err != nil { if err := wsc.WriteRPCResponse(writeCtx, - rpctypes.RPCParseError(fmt.Errorf("error unmarshaling request: %w", err))); err != nil { + request.MakeErrorf(rpctypes.CodeParseError, "unmarshaling request: %v", err)); err != nil { wsc.Logger.Error("error writing RPC response", "err", err) } continue @@ -336,7 +336,8 @@ func (wsc *wsConnection) readRoutine(ctx context.Context) { // Now, fetch the RPCFunc and execute it. rpcFunc := wsc.funcMap[request.Method] if rpcFunc == nil { - if err := wsc.WriteRPCResponse(writeCtx, rpctypes.RPCMethodNotFoundError(request.ID)); err != nil { + if err := wsc.WriteRPCResponse(writeCtx, + request.MakeErrorf(rpctypes.CodeMethodNotFound, request.Method)); err != nil { wsc.Logger.Error("error writing RPC response", "err", err) } continue @@ -348,9 +349,8 @@ func (wsc *wsConnection) readRoutine(ctx context.Context) { }) args, err := parseParams(fctx, rpcFunc, request.Params) if err != nil { - if err := wsc.WriteRPCResponse(writeCtx, rpctypes.RPCInvalidParamsError( - request.ID, fmt.Errorf("error converting json params to arguments: %w", err)), - ); err != nil { + if err := wsc.WriteRPCResponse(writeCtx, request.MakeErrorf(rpctypes.CodeInvalidParams, + "converting JSON parameters: %v", err)); err != nil { wsc.Logger.Error("error writing RPC response", "err", err) } continue @@ -363,32 +363,14 @@ func (wsc *wsConnection) readRoutine(ctx context.Context) { var resp rpctypes.RPCResponse result, err := unreflectResult(returns) - switch e := err.(type) { - // if no error then return a success response - case nil: - resp = rpctypes.NewRPCSuccessResponse(request.ID, result) - - // if this already of type RPC error then forward that error - case *rpctypes.RPCError: - resp = rpctypes.NewRPCErrorResponse(request.ID, e.Code, e.Message, e.Data) - - default: // we need to unwrap the error and parse it accordingly - switch errors.Unwrap(err) { - // check if the error was due to an invald request - case coretypes.ErrZeroOrNegativeHeight, coretypes.ErrZeroOrNegativePerPage, - coretypes.ErrPageOutOfRange, coretypes.ErrInvalidRequest: - resp = rpctypes.RPCInvalidRequestError(request.ID, err) - - // lastly default all remaining errors as internal errors - default: // includes ctypes.ErrHeightNotAvailable and ctypes.ErrHeightExceedsChainHead - resp = rpctypes.RPCInternalError(request.ID, err) - } + if err == nil { + resp = request.MakeResponse(result) + } else { + resp = request.MakeError(err) } - if err := wsc.WriteRPCResponse(writeCtx, resp); err != nil { wsc.Logger.Error("error writing RPC response", "err", err) } - } } } diff --git a/rpc/jsonrpc/types/types.go b/rpc/jsonrpc/types/types.go index e53a925ed..111a9b656 100644 --- a/rpc/jsonrpc/types/types.go +++ b/rpc/jsonrpc/types/types.go @@ -3,10 +3,13 @@ package types import ( "context" "encoding/json" + "errors" "fmt" "net/http" "reflect" "strings" + + "github.com/tendermint/tendermint/rpc/coretypes" ) // a wrapper to emulate a sum type: jsonrpcid = string | int @@ -43,6 +46,33 @@ func idFromInterface(idInterface interface{}) (jsonrpcid, error) { } } +// ErrorCode is the type of JSON-RPC error codes. +type ErrorCode int + +func (e ErrorCode) String() string { + if s, ok := errorCodeString[e]; ok { + return s + } + return fmt.Sprintf("server error: code %d", e) +} + +// Constants defining the standard JSON-RPC error codes. +const ( + CodeParseError ErrorCode = -32700 // Invalid JSON received by the server + CodeInvalidRequest ErrorCode = -32600 // The JSON sent is not a valid request object + CodeMethodNotFound ErrorCode = -32601 // The method does not exist or is unavailable + CodeInvalidParams ErrorCode = -32602 // Invalid method parameters + CodeInternalError ErrorCode = -32603 // Internal JSON-RPC error +) + +var errorCodeString = map[ErrorCode]string{ + CodeParseError: "Parse error", + CodeInvalidRequest: "Invalid request", + CodeMethodNotFound: "Method not found", + CodeInvalidParams: "Invalid params", + CodeInternalError: "Internal error", +} + //---------------------------------------- // REQUEST @@ -94,6 +124,55 @@ func (req RPCRequest) String() string { return fmt.Sprintf("RPCRequest{%s %s/%X}", req.ID, req.Method, req.Params) } +// MakeResponse constructs a success response to req with the given result. If +// there is an error marshaling result to JSON, it returns an error response. +func (req RPCRequest) MakeResponse(result interface{}) RPCResponse { + data, err := json.Marshal(result) + if err != nil { + return req.MakeErrorf(CodeInternalError, "marshaling result: %v", err) + } + return RPCResponse{ID: req.ID, Result: data} +} + +// MakeErrorf constructs an error response to req with the given code and a +// message constructed by formatting msg with args. +func (req RPCRequest) MakeErrorf(code ErrorCode, msg string, args ...interface{}) RPCResponse { + return RPCResponse{ + ID: req.ID, + Error: &RPCError{ + Code: int(code), + Message: code.String(), + Data: fmt.Sprintf(msg, args...), + }, + } +} + +// MakeError constructs an error response to req from the given error value. +// This function will panic if err == nil. +func (req RPCRequest) MakeError(err error) RPCResponse { + if err == nil { + panic("cannot construct an error response for nil") + } + if e, ok := err.(*RPCError); ok { + return RPCResponse{ID: req.ID, Error: e} + } + if errors.Is(err, coretypes.ErrZeroOrNegativeHeight) || + errors.Is(err, coretypes.ErrZeroOrNegativePerPage) || + errors.Is(err, coretypes.ErrPageOutOfRange) || + errors.Is(err, coretypes.ErrInvalidRequest) { + return RPCResponse{ID: req.ID, Error: &RPCError{ + Code: int(CodeInvalidRequest), + Message: CodeInvalidRequest.String(), + Data: err.Error(), + }} + } + return RPCResponse{ID: req.ID, Error: &RPCError{ + Code: int(CodeInternalError), + Message: CodeInternalError.String(), + Data: err.Error(), + }} +} + // ParamsToRequest constructs a new RPCRequest with the given ID, method, and parameters. func ParamsToRequest(id jsonrpcid, method string, params interface{}) (RPCRequest, error) { payload, err := json.Marshal(params) @@ -169,21 +248,6 @@ func (resp RPCResponse) MarshalJSON() ([]byte, error) { }) } -func NewRPCSuccessResponse(id jsonrpcid, res interface{}) RPCResponse { - result, err := json.Marshal(res) - if err != nil { - return RPCInternalError(id, fmt.Errorf("error marshaling response: %w", err)) - } - return RPCResponse{ID: id, Result: result} -} - -func NewRPCErrorResponse(id jsonrpcid, code int, msg string, data string) RPCResponse { - return RPCResponse{ - ID: id, - Error: &RPCError{Code: code, Message: msg, Data: data}, - } -} - func (resp RPCResponse) String() string { if resp.Error == nil { return fmt.Sprintf("RPCResponse{%s %X}", resp.ID, resp.Result) @@ -191,36 +255,6 @@ func (resp RPCResponse) String() string { return fmt.Sprintf("RPCResponse{%s %v}", resp.ID, resp.Error) } -// From the JSON-RPC 2.0 spec: -// If there was an error in detecting the id in the Request object (e.g. Parse -// error/Invalid Request), it MUST be Null. -func RPCParseError(err error) RPCResponse { - return NewRPCErrorResponse(nil, -32700, "Parse error", err.Error()) -} - -// From the JSON-RPC 2.0 spec: -// If there was an error in detecting the id in the Request object (e.g. Parse -// error/Invalid Request), it MUST be Null. -func RPCInvalidRequestError(id jsonrpcid, err error) RPCResponse { - return NewRPCErrorResponse(id, -32600, "Invalid Request", err.Error()) -} - -func RPCMethodNotFoundError(id jsonrpcid) RPCResponse { - return NewRPCErrorResponse(id, -32601, "Method not found", "") -} - -func RPCInvalidParamsError(id jsonrpcid, err error) RPCResponse { - return NewRPCErrorResponse(id, -32602, "Invalid params", err.Error()) -} - -func RPCInternalError(id jsonrpcid, err error) RPCResponse { - return NewRPCErrorResponse(id, -32603, "Internal error", err.Error()) -} - -func RPCServerError(id jsonrpcid, err error) RPCResponse { - return NewRPCErrorResponse(id, -32000, "Server error", err.Error()) -} - //---------------------------------------- // WSRPCConnection represents a websocket connection. diff --git a/rpc/jsonrpc/types/types_test.go b/rpc/jsonrpc/types/types_test.go index 477d83788..2dbfed895 100644 --- a/rpc/jsonrpc/types/types_test.go +++ b/rpc/jsonrpc/types/types_test.go @@ -2,7 +2,6 @@ package types import ( "encoding/json" - "errors" "fmt" "testing" @@ -32,20 +31,27 @@ var responseTests = []responseTest{ func TestResponses(t *testing.T) { for _, tt := range responseTests { - jsonid := tt.id - a := NewRPCSuccessResponse(jsonid, &SampleResult{"hello"}) - b, _ := json.Marshal(a) + req := RPCRequest{ + ID: tt.id, + Method: "whatever", + } + + a := req.MakeResponse(&SampleResult{"hello"}) + b, err := json.Marshal(a) + require.NoError(t, err) s := fmt.Sprintf(`{"jsonrpc":"2.0","id":%v,"result":{"Value":"hello"}}`, tt.expected) assert.Equal(t, s, string(b)) - d := RPCParseError(errors.New("hello world")) - e, _ := json.Marshal(d) - f := `{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error","data":"hello world"}}` + d := req.MakeErrorf(CodeParseError, "hello world") + e, err := json.Marshal(d) + require.NoError(t, err) + f := fmt.Sprintf(`{"jsonrpc":"2.0","id":%v,"error":{"code":-32700,"message":"Parse error","data":"hello world"}}`, tt.expected) assert.Equal(t, f, string(e)) - g := RPCMethodNotFoundError(jsonid) - h, _ := json.Marshal(g) - i := fmt.Sprintf(`{"jsonrpc":"2.0","id":%v,"error":{"code":-32601,"message":"Method not found"}}`, tt.expected) + g := req.MakeErrorf(CodeMethodNotFound, "foo") + h, err := json.Marshal(g) + require.NoError(t, err) + i := fmt.Sprintf(`{"jsonrpc":"2.0","id":%v,"error":{"code":-32601,"message":"Method not found","data":"foo"}}`, tt.expected) assert.Equal(t, string(h), i) } } @@ -59,7 +65,8 @@ func TestUnmarshallResponses(t *testing.T) { ) require.NoError(t, err) - a := NewRPCSuccessResponse(tt.id, &SampleResult{"hello"}) + req := RPCRequest{ID: tt.id} + a := req.MakeResponse(&SampleResult{"hello"}) assert.Equal(t, *response, a) } response := &RPCResponse{}