@ -4,6 +4,7 @@ import (
"context"
"context"
"encoding/json"
"encoding/json"
"fmt"
"fmt"
"reflect"
"strings"
"strings"
"github.com/pkg/errors"
"github.com/pkg/errors"
@ -13,17 +14,75 @@ import (
tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
)
)
// a wrapper to emulate a sum type: jsonrpcid = string | int
// TODO: refactor when Go 2.0 arrives https://github.com/golang/go/issues/19412
type jsonrpcid interface {
isJSONRPCID ( )
}
// JSONRPCStringID a wrapper for JSON-RPC string IDs
type JSONRPCStringID string
func ( JSONRPCStringID ) isJSONRPCID ( ) { }
// JSONRPCIntID a wrapper for JSON-RPC integer IDs
type JSONRPCIntID int
func ( JSONRPCIntID ) isJSONRPCID ( ) { }
func idFromInterface ( idInterface interface { } ) ( jsonrpcid , error ) {
switch id := idInterface . ( type ) {
case string :
return JSONRPCStringID ( id ) , nil
case float64 :
// json.Unmarshal uses float64 for all numbers
// (https://golang.org/pkg/encoding/json/#Unmarshal),
// but the JSONRPC2.0 spec says the id SHOULD NOT contain
// decimals - so we truncate the decimals here.
return JSONRPCIntID ( int ( id ) ) , nil
default :
typ := reflect . TypeOf ( id )
return nil , fmt . Errorf ( "JSON-RPC ID (%v) is of unknown type (%v)" , id , typ )
}
}
//----------------------------------------
//----------------------------------------
// REQUEST
// REQUEST
type RPCRequest struct {
type RPCRequest struct {
JSONRPC string ` json:"jsonrpc" `
JSONRPC string ` json:"jsonrpc" `
ID string ` json:"id" `
ID jsonrpcid ` json:"id" `
Method string ` json:"method" `
Method string ` json:"method" `
Params json . RawMessage ` json:"params" ` // must be map[string]interface{} or []interface{}
Params json . RawMessage ` json:"params" ` // must be map[string]interface{} or []interface{}
}
}
func NewRPCRequest ( id string , method string , params json . RawMessage ) RPCRequest {
// UnmarshalJSON custom JSON unmarshalling due to jsonrpcid being string or int
func ( request * RPCRequest ) UnmarshalJSON ( data [ ] byte ) error {
unsafeReq := & struct {
JSONRPC string ` json:"jsonrpc" `
ID interface { } ` json:"id" `
Method string ` json:"method" `
Params json . RawMessage ` json:"params" ` // must be map[string]interface{} or []interface{}
} { }
err := json . Unmarshal ( data , & unsafeReq )
if err != nil {
return err
}
request . JSONRPC = unsafeReq . JSONRPC
request . Method = unsafeReq . Method
request . Params = unsafeReq . Params
if unsafeReq . ID == nil {
return nil
}
id , err := idFromInterface ( unsafeReq . ID )
if err != nil {
return err
}
request . ID = id
return nil
}
func NewRPCRequest ( id jsonrpcid , method string , params json . RawMessage ) RPCRequest {
return RPCRequest {
return RPCRequest {
JSONRPC : "2.0" ,
JSONRPC : "2.0" ,
ID : id ,
ID : id ,
@ -36,7 +95,7 @@ func (req RPCRequest) String() string {
return fmt . Sprintf ( "[%s %s]" , req . ID , req . Method )
return fmt . Sprintf ( "[%s %s]" , req . ID , req . Method )
}
}
func MapToRequest ( cdc * amino . Codec , id string , method string , params map [ string ] interface { } ) ( RPCRequest , error ) {
func MapToRequest ( cdc * amino . Codec , id jsonrpcid , method string , params map [ string ] interface { } ) ( RPCRequest , error ) {
var params_ = make ( map [ string ] json . RawMessage , len ( params ) )
var params_ = make ( map [ string ] json . RawMessage , len ( params ) )
for name , value := range params {
for name , value := range params {
valueJSON , err := cdc . MarshalJSON ( value )
valueJSON , err := cdc . MarshalJSON ( value )
@ -53,7 +112,7 @@ func MapToRequest(cdc *amino.Codec, id string, method string, params map[string]
return request , nil
return request , nil
}
}
func ArrayToRequest ( cdc * amino . Codec , id string , method string , params [ ] interface { } ) ( RPCRequest , error ) {
func ArrayToRequest ( cdc * amino . Codec , id jsonrpcid , method string , params [ ] interface { } ) ( RPCRequest , error ) {
var params_ = make ( [ ] json . RawMessage , len ( params ) )
var params_ = make ( [ ] json . RawMessage , len ( params ) )
for i , value := range params {
for i , value := range params {
valueJSON , err := cdc . MarshalJSON ( value )
valueJSON , err := cdc . MarshalJSON ( value )
@ -89,12 +148,38 @@ func (err RPCError) Error() string {
type RPCResponse struct {
type RPCResponse struct {
JSONRPC string ` json:"jsonrpc" `
JSONRPC string ` json:"jsonrpc" `
ID string ` json:"id" `
ID jsonrpcid ` json:"id" `
Result json . RawMessage ` json:"result,omitempty" `
Result json . RawMessage ` json:"result,omitempty" `
Error * RPCError ` json:"error,omitempty" `
Error * RPCError ` json:"error,omitempty" `
}
}
func NewRPCSuccessResponse ( cdc * amino . Codec , id string , res interface { } ) RPCResponse {
// UnmarshalJSON custom JSON unmarshalling due to jsonrpcid being string or int
func ( response * RPCResponse ) UnmarshalJSON ( data [ ] byte ) error {
unsafeResp := & struct {
JSONRPC string ` json:"jsonrpc" `
ID interface { } ` json:"id" `
Result json . RawMessage ` json:"result,omitempty" `
Error * RPCError ` json:"error,omitempty" `
} { }
err := json . Unmarshal ( data , & unsafeResp )
if err != nil {
return err
}
response . JSONRPC = unsafeResp . JSONRPC
response . Error = unsafeResp . Error
response . Result = unsafeResp . Result
if unsafeResp . ID == nil {
return nil
}
id , err := idFromInterface ( unsafeResp . ID )
if err != nil {
return err
}
response . ID = id
return nil
}
func NewRPCSuccessResponse ( cdc * amino . Codec , id jsonrpcid , res interface { } ) RPCResponse {
var rawMsg json . RawMessage
var rawMsg json . RawMessage
if res != nil {
if res != nil {
@ -109,7 +194,7 @@ func NewRPCSuccessResponse(cdc *amino.Codec, id string, res interface{}) RPCResp
return RPCResponse { JSONRPC : "2.0" , ID : id , Result : rawMsg }
return RPCResponse { JSONRPC : "2.0" , ID : id , Result : rawMsg }
}
}
func NewRPCErrorResponse ( id string , code int , msg string , data string ) RPCResponse {
func NewRPCErrorResponse ( id jsonrpcid , code int , msg string , data string ) RPCResponse {
return RPCResponse {
return RPCResponse {
JSONRPC : "2.0" ,
JSONRPC : "2.0" ,
ID : id ,
ID : id ,
@ -124,27 +209,27 @@ func (resp RPCResponse) String() string {
return fmt . Sprintf ( "[%s %s]" , resp . ID , resp . Error )
return fmt . Sprintf ( "[%s %s]" , resp . ID , resp . Error )
}
}
func RPCParseError ( id string , err error ) RPCResponse {
func RPCParseError ( id jsonrpcid , err error ) RPCResponse {
return NewRPCErrorResponse ( id , - 32700 , "Parse error. Invalid JSON" , err . Error ( ) )
return NewRPCErrorResponse ( id , - 32700 , "Parse error. Invalid JSON" , err . Error ( ) )
}
}
func RPCInvalidRequestError ( id string , err error ) RPCResponse {
func RPCInvalidRequestError ( id jsonrpcid , err error ) RPCResponse {
return NewRPCErrorResponse ( id , - 32600 , "Invalid Request" , err . Error ( ) )
return NewRPCErrorResponse ( id , - 32600 , "Invalid Request" , err . Error ( ) )
}
}
func RPCMethodNotFoundError ( id string ) RPCResponse {
func RPCMethodNotFoundError ( id jsonrpcid ) RPCResponse {
return NewRPCErrorResponse ( id , - 32601 , "Method not found" , "" )
return NewRPCErrorResponse ( id , - 32601 , "Method not found" , "" )
}
}
func RPCInvalidParamsError ( id string , err error ) RPCResponse {
func RPCInvalidParamsError ( id jsonrpcid , err error ) RPCResponse {
return NewRPCErrorResponse ( id , - 32602 , "Invalid params" , err . Error ( ) )
return NewRPCErrorResponse ( id , - 32602 , "Invalid params" , err . Error ( ) )
}
}
func RPCInternalError ( id string , err error ) RPCResponse {
func RPCInternalError ( id jsonrpcid , err error ) RPCResponse {
return NewRPCErrorResponse ( id , - 32603 , "Internal error" , err . Error ( ) )
return NewRPCErrorResponse ( id , - 32603 , "Internal error" , err . Error ( ) )
}
}
func RPCServerError ( id string , err error ) RPCResponse {
func RPCServerError ( id jsonrpcid , err error ) RPCResponse {
return NewRPCErrorResponse ( id , - 32000 , "Server error" , err . Error ( ) )
return NewRPCErrorResponse ( id , - 32000 , "Server error" , err . Error ( ) )
}
}