github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/rpc/jsonrpc/types/types.go (about) 1 package types 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "net/http" 10 "regexp" 11 "strconv" 12 "strings" 13 14 tmjson "github.com/ari-anchor/sei-tendermint/libs/json" 15 "github.com/ari-anchor/sei-tendermint/rpc/coretypes" 16 ) 17 18 // ErrorCode is the type of JSON-RPC error codes. 19 type ErrorCode int 20 21 func (e ErrorCode) String() string { 22 if s, ok := errorCodeString[e]; ok { 23 return s 24 } 25 return fmt.Sprintf("server error: code %d", e) 26 } 27 28 // Constants defining the standard JSON-RPC error codes. 29 const ( 30 CodeParseError ErrorCode = -32700 // Invalid JSON received by the server 31 CodeInvalidRequest ErrorCode = -32600 // The JSON sent is not a valid request object 32 CodeMethodNotFound ErrorCode = -32601 // The method does not exist or is unavailable 33 CodeInvalidParams ErrorCode = -32602 // Invalid method parameters 34 CodeInternalError ErrorCode = -32603 // Internal JSON-RPC error 35 ) 36 37 var errorCodeString = map[ErrorCode]string{ 38 CodeParseError: "Parse error", 39 CodeInvalidRequest: "Invalid request", 40 CodeMethodNotFound: "Method not found", 41 CodeInvalidParams: "Invalid params", 42 CodeInternalError: "Internal error", 43 } 44 45 //---------------------------------------- 46 // REQUEST 47 48 type RPCRequest struct { 49 id json.RawMessage 50 51 Method string 52 Params json.RawMessage 53 } 54 55 // NewRequest returns an empty request with the specified ID. 56 func NewRequest(id int) RPCRequest { 57 return RPCRequest{id: []byte(strconv.Itoa(id))} 58 } 59 60 // ID returns a string representation of the request ID. 61 func (req RPCRequest) ID() string { return string(req.id) } 62 63 // IsNotification reports whether req is a notification (has an empty ID). 64 func (req RPCRequest) IsNotification() bool { return len(req.id) == 0 } 65 66 type rpcRequestJSON struct { 67 V string `json:"jsonrpc"` // must be "2.0" 68 ID json.RawMessage `json:"id,omitempty"` 69 M string `json:"method"` 70 P json.RawMessage `json:"params"` 71 } 72 73 // isNullOrEmpty reports whether data is empty or the JSON "null" value. 74 func isNullOrEmpty(data json.RawMessage) bool { 75 return len(data) == 0 || bytes.Equal(data, []byte("null")) 76 } 77 78 // validID matches the text of a JSON value that is allowed to serve as a 79 // JSON-RPC request ID. Precondition: Target value is legal JSON. 80 var validID = regexp.MustCompile(`^(?:".*"|-?\d+)$`) 81 82 // UnmarshalJSON decodes a request from a JSON-RPC 2.0 request object. 83 func (req *RPCRequest) UnmarshalJSON(data []byte) error { 84 var wrapper rpcRequestJSON 85 if err := json.Unmarshal(data, &wrapper); err != nil { 86 return err 87 } else if wrapper.V != "" && wrapper.V != "2.0" { 88 return fmt.Errorf("invalid version: %q", wrapper.V) 89 } 90 91 if !isNullOrEmpty(wrapper.ID) { 92 if !validID.Match(wrapper.ID) { 93 return fmt.Errorf("invalid request ID: %q", string(wrapper.ID)) 94 } 95 req.id = wrapper.ID 96 } 97 req.Method = wrapper.M 98 req.Params = wrapper.P 99 return nil 100 } 101 102 // MarshalJSON marshals a request with the appropriate version tag. 103 func (req RPCRequest) MarshalJSON() ([]byte, error) { 104 return json.Marshal(rpcRequestJSON{ 105 V: "2.0", 106 ID: req.id, 107 M: req.Method, 108 P: req.Params, 109 }) 110 } 111 112 func (req RPCRequest) String() string { 113 return fmt.Sprintf("RPCRequest{%s %s/%X}", req.ID(), req.Method, req.Params) 114 } 115 116 // MakeResponse constructs a success response to req with the given result. If 117 // there is an error marshaling result to JSON, it returns an error response. 118 func (req RPCRequest) MakeResponse(result interface{}) RPCResponse { 119 data, err := tmjson.Marshal(result) 120 if err != nil { 121 return req.MakeErrorf(CodeInternalError, "marshaling result: %v", err) 122 } 123 return RPCResponse{id: req.id, Result: data} 124 } 125 126 // MakeErrorf constructs an error response to req with the given code and a 127 // message constructed by formatting msg with args. 128 func (req RPCRequest) MakeErrorf(code ErrorCode, msg string, args ...interface{}) RPCResponse { 129 return RPCResponse{ 130 id: req.id, 131 Error: &RPCError{ 132 Code: int(code), 133 Message: code.String(), 134 Data: fmt.Sprintf(msg, args...), 135 }, 136 } 137 } 138 139 // MakeError constructs an error response to req from the given error value. 140 // This function will panic if err == nil. 141 func (req RPCRequest) MakeError(err error) RPCResponse { 142 if err == nil { 143 panic("cannot construct an error response for nil") 144 } 145 if e, ok := err.(*RPCError); ok { 146 return RPCResponse{id: req.id, Error: e} 147 } 148 if errors.Is(err, coretypes.ErrZeroOrNegativeHeight) || 149 errors.Is(err, coretypes.ErrZeroOrNegativePerPage) || 150 errors.Is(err, coretypes.ErrPageOutOfRange) || 151 errors.Is(err, coretypes.ErrInvalidRequest) { 152 return RPCResponse{id: req.id, Error: &RPCError{ 153 Code: int(CodeInvalidRequest), 154 Message: CodeInvalidRequest.String(), 155 Data: err.Error(), 156 }} 157 } 158 return RPCResponse{id: req.id, Error: &RPCError{ 159 Code: int(CodeInternalError), 160 Message: CodeInternalError.String(), 161 Data: err.Error(), 162 }} 163 } 164 165 // SetMethodAndParams updates the method and parameters of req with the given 166 // values, leaving the ID unchanged. 167 func (req *RPCRequest) SetMethodAndParams(method string, params interface{}) error { 168 payload, err := json.Marshal(params) 169 if err != nil { 170 return err 171 } 172 req.Method = method 173 req.Params = payload 174 return nil 175 } 176 177 //---------------------------------------- 178 // RESPONSE 179 180 type RPCError struct { 181 Code int `json:"code"` 182 Message string `json:"message"` 183 Data string `json:"data,omitempty"` 184 } 185 186 func (err RPCError) Error() string { 187 const baseFormat = "RPC error %v - %s" 188 if err.Data != "" { 189 return fmt.Sprintf(baseFormat+": %s", err.Code, err.Message, err.Data) 190 } 191 return fmt.Sprintf(baseFormat, err.Code, err.Message) 192 } 193 194 type RPCResponse struct { 195 id json.RawMessage 196 197 Result json.RawMessage 198 Error *RPCError 199 } 200 201 // ID returns a representation of the response ID. 202 func (resp RPCResponse) ID() string { return string(resp.id) } 203 204 type rpcResponseJSON struct { 205 V string `json:"jsonrpc"` // must be "2.0" 206 ID json.RawMessage `json:"id,omitempty"` 207 R json.RawMessage `json:"result,omitempty"` 208 E *RPCError `json:"error,omitempty"` 209 } 210 211 // UnmarshalJSON decodes a response from a JSON-RPC 2.0 response object. 212 func (resp *RPCResponse) UnmarshalJSON(data []byte) error { 213 var wrapper rpcResponseJSON 214 if err := json.Unmarshal(data, &wrapper); err != nil { 215 return err 216 } else if wrapper.V != "" && wrapper.V != "2.0" { 217 return fmt.Errorf("invalid version: %q", wrapper.V) 218 } 219 220 if !isNullOrEmpty(wrapper.ID) { 221 if !validID.Match(wrapper.ID) { 222 return fmt.Errorf("invalid response ID: %q", string(wrapper.ID)) 223 } 224 resp.id = wrapper.ID 225 } 226 resp.Error = wrapper.E 227 resp.Result = wrapper.R 228 return nil 229 } 230 231 // MarshalJSON marshals a response with the appropriate version tag. 232 func (resp RPCResponse) MarshalJSON() ([]byte, error) { 233 return json.Marshal(rpcResponseJSON{ 234 V: "2.0", 235 ID: resp.id, 236 R: resp.Result, 237 E: resp.Error, 238 }) 239 } 240 241 func (resp RPCResponse) String() string { 242 if resp.Error == nil { 243 return fmt.Sprintf("RPCResponse{%s %X}", resp.ID(), resp.Result) 244 } 245 return fmt.Sprintf("RPCResponse{%s %v}", resp.ID(), resp.Error) 246 } 247 248 //---------------------------------------- 249 250 // WSRPCConnection represents a websocket connection. 251 type WSRPCConnection interface { 252 // GetRemoteAddr returns a remote address of the connection. 253 GetRemoteAddr() string 254 // WriteRPCResponse writes the response onto connection (BLOCKING). 255 WriteRPCResponse(context.Context, RPCResponse) error 256 // TryWriteRPCResponse tries to write the response onto connection (NON-BLOCKING). 257 TryWriteRPCResponse(context.Context, RPCResponse) bool 258 // Context returns the connection's context. 259 Context() context.Context 260 } 261 262 // CallInfo carries JSON-RPC request metadata for RPC functions invoked via 263 // JSON-RPC. It can be recovered from the context with GetCallInfo. 264 type CallInfo struct { 265 RPCRequest *RPCRequest // non-nil for requests via HTTP or websocket 266 HTTPRequest *http.Request // non-nil for requests via HTTP 267 WSConn WSRPCConnection // non-nil for requests via websocket 268 } 269 270 type callInfoKey struct{} 271 272 // WithCallInfo returns a child context of ctx with the ci attached. 273 func WithCallInfo(ctx context.Context, ci *CallInfo) context.Context { 274 return context.WithValue(ctx, callInfoKey{}, ci) 275 } 276 277 // GetCallInfo returns the CallInfo record attached to ctx, or nil if ctx does 278 // not contain a call record. 279 func GetCallInfo(ctx context.Context) *CallInfo { 280 if v := ctx.Value(callInfoKey{}); v != nil { 281 return v.(*CallInfo) 282 } 283 return nil 284 } 285 286 // RemoteAddr returns the remote address (usually a string "IP:port"). If 287 // neither HTTPRequest nor WSConn is set, an empty string is returned. 288 // 289 // For HTTP requests, this reports the request's RemoteAddr. 290 // For websocket requests, this reports the connection's GetRemoteAddr. 291 func (ci *CallInfo) RemoteAddr() string { 292 if ci == nil { 293 return "" 294 } else if ci.HTTPRequest != nil { 295 return ci.HTTPRequest.RemoteAddr 296 } else if ci.WSConn != nil { 297 return ci.WSConn.GetRemoteAddr() 298 } 299 return "" 300 } 301 302 //---------------------------------------- 303 // SOCKETS 304 305 // Determine if its a unix or tcp socket. 306 // If tcp, must specify the port; `0.0.0.0` will return incorrectly as "unix" since there's no port 307 // TODO: deprecate 308 func SocketType(listenAddr string) string { 309 socketType := "unix" 310 if len(strings.Split(listenAddr, ":")) >= 2 { 311 socketType = "tcp" 312 } 313 return socketType 314 }