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