@ -3,10 +3,13 @@ package types
import (
import (
"context"
"context"
"encoding/json"
"encoding/json"
"errors"
"fmt"
"fmt"
"net/http"
"net/http"
"reflect"
"reflect"
"strings"
"strings"
"github.com/tendermint/tendermint/rpc/coretypes"
)
)
// a wrapper to emulate a sum type: jsonrpcid = string | int
// 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
// REQUEST
@ -94,6 +124,55 @@ func (req RPCRequest) String() string {
return fmt . Sprintf ( "RPCRequest{%s %s/%X}" , req . ID , req . Method , req . Params )
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.
// ParamsToRequest constructs a new RPCRequest with the given ID, method, and parameters.
func ParamsToRequest ( id jsonrpcid , method string , params interface { } ) ( RPCRequest , error ) {
func ParamsToRequest ( id jsonrpcid , method string , params interface { } ) ( RPCRequest , error ) {
payload , err := json . Marshal ( params )
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 {
func ( resp RPCResponse ) String ( ) string {
if resp . Error == nil {
if resp . Error == nil {
return fmt . Sprintf ( "RPCResponse{%s %X}" , resp . ID , resp . Result )
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 )
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.
// WSRPCConnection represents a websocket connection.