github.com/MagHErmit/tendermint@v0.282.1/rpc/jsonrpc/types/types.go (about)

     1  package types
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"net/http"
     8  	"reflect"
     9  	"strings"
    10  
    11  	tmjson "github.com/MagHErmit/tendermint/libs/json"
    12  )
    13  
    14  // a wrapper to emulate a sum type: jsonrpcid = string | int
    15  // TODO: refactor when Go 2.0 arrives https://github.com/golang/go/issues/19412
    16  type jsonrpcid interface {
    17  	isJSONRPCID()
    18  }
    19  
    20  // JSONRPCStringID a wrapper for JSON-RPC string IDs
    21  type JSONRPCStringID string
    22  
    23  func (JSONRPCStringID) isJSONRPCID()      {}
    24  func (id JSONRPCStringID) String() string { return string(id) }
    25  
    26  // JSONRPCIntID a wrapper for JSON-RPC integer IDs
    27  type JSONRPCIntID int
    28  
    29  func (JSONRPCIntID) isJSONRPCID()      {}
    30  func (id JSONRPCIntID) String() string { return fmt.Sprintf("%d", id) }
    31  
    32  func idFromInterface(idInterface interface{}) (jsonrpcid, error) {
    33  	switch id := idInterface.(type) {
    34  	case string:
    35  		return JSONRPCStringID(id), nil
    36  	case float64:
    37  		// json.Unmarshal uses float64 for all numbers
    38  		// (https://golang.org/pkg/encoding/json/#Unmarshal),
    39  		// but the JSONRPC2.0 spec says the id SHOULD NOT contain
    40  		// decimals - so we truncate the decimals here.
    41  		return JSONRPCIntID(int(id)), nil
    42  	default:
    43  		typ := reflect.TypeOf(id)
    44  		return nil, fmt.Errorf("json-rpc ID (%v) is of unknown type (%v)", id, typ)
    45  	}
    46  }
    47  
    48  //----------------------------------------
    49  // REQUEST
    50  
    51  type RPCRequest struct {
    52  	JSONRPC string          `json:"jsonrpc"`
    53  	ID      jsonrpcid       `json:"id,omitempty"`
    54  	Method  string          `json:"method"`
    55  	Params  json.RawMessage `json:"params"` // must be map[string]interface{} or []interface{}
    56  }
    57  
    58  // UnmarshalJSON custom JSON unmarshalling due to jsonrpcid being string or int
    59  func (req *RPCRequest) UnmarshalJSON(data []byte) error {
    60  	unsafeReq := struct {
    61  		JSONRPC string          `json:"jsonrpc"`
    62  		ID      interface{}     `json:"id,omitempty"`
    63  		Method  string          `json:"method"`
    64  		Params  json.RawMessage `json:"params"` // must be map[string]interface{} or []interface{}
    65  	}{}
    66  
    67  	err := json.Unmarshal(data, &unsafeReq)
    68  	if err != nil {
    69  		return err
    70  	}
    71  
    72  	if unsafeReq.ID == nil { // notification
    73  		return nil
    74  	}
    75  
    76  	req.JSONRPC = unsafeReq.JSONRPC
    77  	req.Method = unsafeReq.Method
    78  	req.Params = unsafeReq.Params
    79  	id, err := idFromInterface(unsafeReq.ID)
    80  	if err != nil {
    81  		return err
    82  	}
    83  	req.ID = id
    84  
    85  	return nil
    86  }
    87  
    88  func NewRPCRequest(id jsonrpcid, method string, params json.RawMessage) RPCRequest {
    89  	return RPCRequest{
    90  		JSONRPC: "2.0",
    91  		ID:      id,
    92  		Method:  method,
    93  		Params:  params,
    94  	}
    95  }
    96  
    97  func (req RPCRequest) String() string {
    98  	return fmt.Sprintf("RPCRequest{%s %s/%X}", req.ID, req.Method, req.Params)
    99  }
   100  
   101  func MapToRequest(id jsonrpcid, method string, params map[string]interface{}) (RPCRequest, error) {
   102  	var paramsMap = make(map[string]json.RawMessage, len(params))
   103  	for name, value := range params {
   104  		valueJSON, err := tmjson.Marshal(value)
   105  		if err != nil {
   106  			return RPCRequest{}, err
   107  		}
   108  		paramsMap[name] = valueJSON
   109  	}
   110  
   111  	payload, err := json.Marshal(paramsMap)
   112  	if err != nil {
   113  		return RPCRequest{}, err
   114  	}
   115  
   116  	return NewRPCRequest(id, method, payload), nil
   117  }
   118  
   119  func ArrayToRequest(id jsonrpcid, method string, params []interface{}) (RPCRequest, error) {
   120  	var paramsMap = make([]json.RawMessage, len(params))
   121  	for i, value := range params {
   122  		valueJSON, err := tmjson.Marshal(value)
   123  		if err != nil {
   124  			return RPCRequest{}, err
   125  		}
   126  		paramsMap[i] = valueJSON
   127  	}
   128  
   129  	payload, err := json.Marshal(paramsMap)
   130  	if err != nil {
   131  		return RPCRequest{}, err
   132  	}
   133  
   134  	return NewRPCRequest(id, method, payload), nil
   135  }
   136  
   137  //----------------------------------------
   138  // RESPONSE
   139  
   140  type RPCError struct {
   141  	Code    int    `json:"code"`
   142  	Message string `json:"message"`
   143  	Data    string `json:"data,omitempty"`
   144  }
   145  
   146  func (err RPCError) Error() string {
   147  	const baseFormat = "RPC error %v - %s"
   148  	if err.Data != "" {
   149  		return fmt.Sprintf(baseFormat+": %s", err.Code, err.Message, err.Data)
   150  	}
   151  	return fmt.Sprintf(baseFormat, err.Code, err.Message)
   152  }
   153  
   154  type RPCResponse struct {
   155  	JSONRPC string          `json:"jsonrpc"`
   156  	ID      jsonrpcid       `json:"id,omitempty"`
   157  	Result  json.RawMessage `json:"result,omitempty"`
   158  	Error   *RPCError       `json:"error,omitempty"`
   159  }
   160  
   161  // UnmarshalJSON custom JSON unmarshalling due to jsonrpcid being string or int
   162  func (resp *RPCResponse) UnmarshalJSON(data []byte) error {
   163  	unsafeResp := &struct {
   164  		JSONRPC string          `json:"jsonrpc"`
   165  		ID      interface{}     `json:"id,omitempty"`
   166  		Result  json.RawMessage `json:"result,omitempty"`
   167  		Error   *RPCError       `json:"error,omitempty"`
   168  	}{}
   169  	err := json.Unmarshal(data, &unsafeResp)
   170  	if err != nil {
   171  		return err
   172  	}
   173  	resp.JSONRPC = unsafeResp.JSONRPC
   174  	resp.Error = unsafeResp.Error
   175  	resp.Result = unsafeResp.Result
   176  	if unsafeResp.ID == nil {
   177  		return nil
   178  	}
   179  	id, err := idFromInterface(unsafeResp.ID)
   180  	if err != nil {
   181  		return err
   182  	}
   183  	resp.ID = id
   184  	return nil
   185  }
   186  
   187  func NewRPCSuccessResponse(id jsonrpcid, res interface{}) RPCResponse {
   188  	var rawMsg json.RawMessage
   189  
   190  	if res != nil {
   191  		var js []byte
   192  		js, err := tmjson.Marshal(res)
   193  		if err != nil {
   194  			return RPCInternalError(id, fmt.Errorf("error marshaling response: %w", err))
   195  		}
   196  		rawMsg = json.RawMessage(js)
   197  	}
   198  
   199  	return RPCResponse{JSONRPC: "2.0", ID: id, Result: rawMsg}
   200  }
   201  
   202  func NewRPCErrorResponse(id jsonrpcid, code int, msg string, data string) RPCResponse {
   203  	return RPCResponse{
   204  		JSONRPC: "2.0",
   205  		ID:      id,
   206  		Error:   &RPCError{Code: code, Message: msg, Data: data},
   207  	}
   208  }
   209  
   210  func (resp RPCResponse) String() string {
   211  	if resp.Error == nil {
   212  		return fmt.Sprintf("RPCResponse{%s %X}", resp.ID, resp.Result)
   213  	}
   214  	return fmt.Sprintf("RPCResponse{%s %v}", resp.ID, resp.Error)
   215  }
   216  
   217  // From the JSON-RPC 2.0 spec:
   218  //
   219  //	If there was an error in detecting the id in the Request object (e.g. Parse
   220  //	error/Invalid Request), it MUST be Null.
   221  func RPCParseError(err error) RPCResponse {
   222  	return NewRPCErrorResponse(nil, -32700, "Parse error. Invalid JSON", err.Error())
   223  }
   224  
   225  // From the JSON-RPC 2.0 spec:
   226  //
   227  //	If there was an error in detecting the id in the Request object (e.g. Parse
   228  //	error/Invalid Request), it MUST be Null.
   229  func RPCInvalidRequestError(id jsonrpcid, err error) RPCResponse {
   230  	return NewRPCErrorResponse(id, -32600, "Invalid Request", err.Error())
   231  }
   232  
   233  func RPCMethodNotFoundError(id jsonrpcid) RPCResponse {
   234  	return NewRPCErrorResponse(id, -32601, "Method not found", "")
   235  }
   236  
   237  func RPCInvalidParamsError(id jsonrpcid, err error) RPCResponse {
   238  	return NewRPCErrorResponse(id, -32602, "Invalid params", err.Error())
   239  }
   240  
   241  func RPCInternalError(id jsonrpcid, err error) RPCResponse {
   242  	return NewRPCErrorResponse(id, -32603, "Internal error", err.Error())
   243  }
   244  
   245  func RPCServerError(id jsonrpcid, err error) RPCResponse {
   246  	return NewRPCErrorResponse(id, -32000, "Server error", err.Error())
   247  }
   248  
   249  //----------------------------------------
   250  
   251  // WSRPCConnection represents a websocket connection.
   252  type WSRPCConnection interface {
   253  	// GetRemoteAddr returns a remote address of the connection.
   254  	GetRemoteAddr() string
   255  	// WriteRPCResponse writes the response onto connection (BLOCKING).
   256  	WriteRPCResponse(context.Context, RPCResponse) error
   257  	// TryWriteRPCResponse tries to write the response onto connection (NON-BLOCKING).
   258  	TryWriteRPCResponse(RPCResponse) bool
   259  	// Context returns the connection's context.
   260  	Context() context.Context
   261  }
   262  
   263  // Context is the first parameter for all functions. It carries a json-rpc
   264  // request, http request and websocket connection.
   265  //
   266  // - JSONReq is non-nil when JSONRPC is called over websocket or HTTP.
   267  // - WSConn is non-nil when we're connected via a websocket.
   268  // - HTTPReq is non-nil when URI or JSONRPC is called over HTTP.
   269  type Context struct {
   270  	// json-rpc request
   271  	JSONReq *RPCRequest
   272  	// websocket connection
   273  	WSConn WSRPCConnection
   274  	// http request
   275  	HTTPReq *http.Request
   276  }
   277  
   278  // RemoteAddr returns the remote address (usually a string "IP:port").
   279  // If neither HTTPReq nor WSConn is set, an empty string is returned.
   280  // HTTP:
   281  //
   282  //	http.Request#RemoteAddr
   283  //
   284  // WS:
   285  //
   286  //	result of GetRemoteAddr
   287  func (ctx *Context) RemoteAddr() string {
   288  	if ctx.HTTPReq != nil {
   289  		return ctx.HTTPReq.RemoteAddr
   290  	} else if ctx.WSConn != nil {
   291  		return ctx.WSConn.GetRemoteAddr()
   292  	}
   293  	return ""
   294  }
   295  
   296  // Context returns the request's context.
   297  // The returned context is always non-nil; it defaults to the background context.
   298  // HTTP:
   299  //
   300  //	The context is canceled when the client's connection closes, the request
   301  //	is canceled (with HTTP/2), or when the ServeHTTP method returns.
   302  //
   303  // WS:
   304  //
   305  //	The context is canceled when the client's connections closes.
   306  func (ctx *Context) Context() context.Context {
   307  	if ctx.HTTPReq != nil {
   308  		return ctx.HTTPReq.Context()
   309  	} else if ctx.WSConn != nil {
   310  		return ctx.WSConn.Context()
   311  	}
   312  	return context.Background()
   313  }
   314  
   315  //----------------------------------------
   316  // SOCKETS
   317  
   318  // Determine if its a unix or tcp socket.
   319  // If tcp, must specify the port; `0.0.0.0` will return incorrectly as "unix" since there's no port
   320  // TODO: deprecate
   321  func SocketType(listenAddr string) string {
   322  	socketType := "unix"
   323  	if len(strings.Split(listenAddr, ":")) >= 2 {
   324  		socketType = "tcp"
   325  	}
   326  	return socketType
   327  }