package rpctypes import ( "context" "encoding/json" "fmt" "strings" "github.com/pkg/errors" "github.com/tendermint/go-amino" tmpubsub "github.com/tendermint/tmlibs/pubsub" ) //---------------------------------------- // REQUEST type RPCRequest struct { JSONRPC string `json:"jsonrpc"` ID string `json:"id"` Method string `json:"method"` Params json.RawMessage `json:"params"` // must be map[string]interface{} or []interface{} } func NewRPCRequest(id string, method string, params json.RawMessage) RPCRequest { return RPCRequest{ JSONRPC: "2.0", ID: id, Method: method, Params: params, } } func (req RPCRequest) String() string { return fmt.Sprintf("[%s %s]", req.ID, req.Method) } func MapToRequest(cdc *amino.Codec, id string, method string, params map[string]interface{}) (RPCRequest, error) { var params_ = make(map[string]json.RawMessage, len(params)) for name, value := range params { valueJSON, err := cdc.MarshalJSON(value) if err != nil { return RPCRequest{}, err } params_[name] = valueJSON } payload, err := json.Marshal(params_) // NOTE: Amino doesn't handle maps yet. if err != nil { return RPCRequest{}, err } request := NewRPCRequest(id, method, payload) return request, nil } func ArrayToRequest(cdc *amino.Codec, id string, method string, params []interface{}) (RPCRequest, error) { var params_ = make([]json.RawMessage, len(params)) for i, value := range params { valueJSON, err := cdc.MarshalJSON(value) if err != nil { return RPCRequest{}, err } params_[i] = valueJSON } payload, err := json.Marshal(params_) // NOTE: Amino doesn't handle maps yet. if err != nil { return RPCRequest{}, err } request := NewRPCRequest(id, method, payload) return request, nil } //---------------------------------------- // RESPONSE type RPCError struct { Code int `json:"code"` Message string `json:"message"` Data string `json:"data,omitempty"` } func (err RPCError) Error() string { const baseFormat = "RPC error %v - %s" if err.Data != "" { return fmt.Sprintf(baseFormat+": %s", err.Code, err.Message, err.Data) } return fmt.Sprintf(baseFormat, err.Code, err.Message) } type RPCResponse struct { JSONRPC string `json:"jsonrpc"` ID string `json:"id"` Result json.RawMessage `json:"result,omitempty"` Error *RPCError `json:"error,omitempty"` } func NewRPCSuccessResponse(cdc *amino.Codec, id string, res interface{}) RPCResponse { var rawMsg json.RawMessage if res != nil { var js []byte js, err := cdc.MarshalJSON(res) if err != nil { return RPCInternalError(id, errors.Wrap(err, "Error marshalling response")) } rawMsg = json.RawMessage(js) } return RPCResponse{JSONRPC: "2.0", ID: id, Result: rawMsg} } func NewRPCErrorResponse(id string, code int, msg string, data string) RPCResponse { return RPCResponse{ JSONRPC: "2.0", ID: id, Error: &RPCError{Code: code, Message: msg, Data: data}, } } func (resp RPCResponse) String() string { if resp.Error == nil { return fmt.Sprintf("[%s %v]", resp.ID, resp.Result) } return fmt.Sprintf("[%s %s]", resp.ID, resp.Error) } func RPCParseError(id string, err error) RPCResponse { return NewRPCErrorResponse(id, -32700, "Parse error. Invalid JSON", err.Error()) } func RPCInvalidRequestError(id string, err error) RPCResponse { return NewRPCErrorResponse(id, -32600, "Invalid Request", err.Error()) } func RPCMethodNotFoundError(id string) RPCResponse { return NewRPCErrorResponse(id, -32601, "Method not found", "") } func RPCInvalidParamsError(id string, err error) RPCResponse { return NewRPCErrorResponse(id, -32602, "Invalid params", err.Error()) } func RPCInternalError(id string, err error) RPCResponse { return NewRPCErrorResponse(id, -32603, "Internal error", err.Error()) } func RPCServerError(id string, err error) RPCResponse { return NewRPCErrorResponse(id, -32000, "Server error", err.Error()) } //---------------------------------------- // *wsConnection implements this interface. type WSRPCConnection interface { GetRemoteAddr() string WriteRPCResponse(resp RPCResponse) TryWriteRPCResponse(resp RPCResponse) bool GetEventSubscriber() EventSubscriber Codec() *amino.Codec } // EventSubscriber mirros tendermint/tendermint/types.EventBusSubscriber type EventSubscriber interface { Subscribe(ctx context.Context, subscriber string, query tmpubsub.Query, out chan<- interface{}) error Unsubscribe(ctx context.Context, subscriber string, query tmpubsub.Query) error UnsubscribeAll(ctx context.Context, subscriber string) error } // websocket-only RPCFuncs take this as the first parameter. type WSRPCContext struct { Request RPCRequest WSRPCConnection } //---------------------------------------- // SOCKETS // // Determine if its a unix or tcp socket. // If tcp, must specify the port; `0.0.0.0` will return incorrectly as "unix" since there's no port // TODO: deprecate func SocketType(listenAddr string) string { socketType := "unix" if len(strings.Split(listenAddr, ":")) >= 2 { socketType = "tcp" } return socketType }