rpc: add new client, use it everywhere
The new client implementation supports concurrent requests, subscriptions and replaces the various ad hoc RPC clients throughout go-ethereum.
This commit is contained in:
61
rpc/json.go
61
rpc/json.go
@ -30,49 +30,43 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
JSONRPCVersion = "2.0"
|
||||
jsonrpcVersion = "2.0"
|
||||
serviceMethodSeparator = "_"
|
||||
subscribeMethod = "eth_subscribe"
|
||||
unsubscribeMethod = "eth_unsubscribe"
|
||||
notificationMethod = "eth_subscription"
|
||||
)
|
||||
|
||||
// JSON-RPC request
|
||||
type JSONRequest struct {
|
||||
type jsonRequest struct {
|
||||
Method string `json:"method"`
|
||||
Version string `json:"jsonrpc"`
|
||||
Id json.RawMessage `json:"id,omitempty"`
|
||||
Payload json.RawMessage `json:"params,omitempty"`
|
||||
}
|
||||
|
||||
// JSON-RPC response
|
||||
type JSONSuccessResponse struct {
|
||||
type jsonSuccessResponse struct {
|
||||
Version string `json:"jsonrpc"`
|
||||
Id interface{} `json:"id,omitempty"`
|
||||
Result interface{} `json:"result"`
|
||||
}
|
||||
|
||||
// JSON-RPC error object
|
||||
type JSONError struct {
|
||||
type jsonError struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// JSON-RPC error response
|
||||
type JSONErrResponse struct {
|
||||
type jsonErrResponse struct {
|
||||
Version string `json:"jsonrpc"`
|
||||
Id interface{} `json:"id,omitempty"`
|
||||
Error JSONError `json:"error"`
|
||||
Error jsonError `json:"error"`
|
||||
}
|
||||
|
||||
// JSON-RPC notification payload
|
||||
type jsonSubscription struct {
|
||||
Subscription string `json:"subscription"`
|
||||
Result interface{} `json:"result,omitempty"`
|
||||
}
|
||||
|
||||
// JSON-RPC notification
|
||||
type jsonNotification struct {
|
||||
Version string `json:"jsonrpc"`
|
||||
Method string `json:"method"`
|
||||
@ -91,6 +85,17 @@ type jsonCodec struct {
|
||||
rw io.ReadWriteCloser // connection
|
||||
}
|
||||
|
||||
func (err *jsonError) Error() string {
|
||||
if err.Message == "" {
|
||||
return fmt.Sprintf("json-rpc error %d", err.Code)
|
||||
}
|
||||
return err.Message
|
||||
}
|
||||
|
||||
func (err *jsonError) ErrorCode() int {
|
||||
return err.Code
|
||||
}
|
||||
|
||||
// NewJSONCodec creates a new RPC server codec with support for JSON-RPC 2.0
|
||||
func NewJSONCodec(rwc io.ReadWriteCloser) ServerCodec {
|
||||
d := json.NewDecoder(rwc)
|
||||
@ -113,7 +118,7 @@ func isBatch(msg json.RawMessage) bool {
|
||||
// ReadRequestHeaders will read new requests without parsing the arguments. It will
|
||||
// return a collection of requests, an indication if these requests are in batch
|
||||
// form or an error when the incoming message could not be read/parsed.
|
||||
func (c *jsonCodec) ReadRequestHeaders() ([]rpcRequest, bool, RPCError) {
|
||||
func (c *jsonCodec) ReadRequestHeaders() ([]rpcRequest, bool, Error) {
|
||||
c.decMu.Lock()
|
||||
defer c.decMu.Unlock()
|
||||
|
||||
@ -148,8 +153,8 @@ func checkReqId(reqId json.RawMessage) error {
|
||||
// parseRequest will parse a single request from the given RawMessage. It will return
|
||||
// the parsed request, an indication if the request was a batch or an error when
|
||||
// the request could not be parsed.
|
||||
func parseRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, RPCError) {
|
||||
var in JSONRequest
|
||||
func parseRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, Error) {
|
||||
var in jsonRequest
|
||||
if err := json.Unmarshal(incomingMsg, &in); err != nil {
|
||||
return nil, false, &invalidMessageError{err.Error()}
|
||||
}
|
||||
@ -197,8 +202,8 @@ func parseRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, RPCError) {
|
||||
|
||||
// parseBatchRequest will parse a batch request into a collection of requests from the given RawMessage, an indication
|
||||
// if the request was a batch or an error when the request could not be read.
|
||||
func parseBatchRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, RPCError) {
|
||||
var in []JSONRequest
|
||||
func parseBatchRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, Error) {
|
||||
var in []jsonRequest
|
||||
if err := json.Unmarshal(incomingMsg, &in); err != nil {
|
||||
return nil, false, &invalidMessageError{err.Error()}
|
||||
}
|
||||
@ -253,7 +258,7 @@ func parseBatchRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, RPCErro
|
||||
|
||||
// ParseRequestArguments tries to parse the given params (json.RawMessage) with the given types. It returns the parsed
|
||||
// values or an error when the parsing failed.
|
||||
func (c *jsonCodec) ParseRequestArguments(argTypes []reflect.Type, params interface{}) ([]reflect.Value, RPCError) {
|
||||
func (c *jsonCodec) ParseRequestArguments(argTypes []reflect.Type, params interface{}) ([]reflect.Value, Error) {
|
||||
if args, ok := params.(json.RawMessage); !ok {
|
||||
return nil, &invalidParamsError{"Invalid params supplied"}
|
||||
} else {
|
||||
@ -264,7 +269,7 @@ func (c *jsonCodec) ParseRequestArguments(argTypes []reflect.Type, params interf
|
||||
// parsePositionalArguments tries to parse the given args to an array of values with the given types.
|
||||
// It returns the parsed values or an error when the args could not be parsed. Missing optional arguments
|
||||
// are returned as reflect.Zero values.
|
||||
func parsePositionalArguments(args json.RawMessage, callbackArgs []reflect.Type) ([]reflect.Value, RPCError) {
|
||||
func parsePositionalArguments(args json.RawMessage, callbackArgs []reflect.Type) ([]reflect.Value, Error) {
|
||||
params := make([]interface{}, 0, len(callbackArgs))
|
||||
for _, t := range callbackArgs {
|
||||
params = append(params, reflect.New(t).Interface())
|
||||
@ -302,31 +307,31 @@ func parsePositionalArguments(args json.RawMessage, callbackArgs []reflect.Type)
|
||||
// CreateResponse will create a JSON-RPC success response with the given id and reply as result.
|
||||
func (c *jsonCodec) CreateResponse(id interface{}, reply interface{}) interface{} {
|
||||
if isHexNum(reflect.TypeOf(reply)) {
|
||||
return &JSONSuccessResponse{Version: JSONRPCVersion, Id: id, Result: fmt.Sprintf(`%#x`, reply)}
|
||||
return &jsonSuccessResponse{Version: jsonrpcVersion, Id: id, Result: fmt.Sprintf(`%#x`, reply)}
|
||||
}
|
||||
return &JSONSuccessResponse{Version: JSONRPCVersion, Id: id, Result: reply}
|
||||
return &jsonSuccessResponse{Version: jsonrpcVersion, Id: id, Result: reply}
|
||||
}
|
||||
|
||||
// CreateErrorResponse will create a JSON-RPC error response with the given id and error.
|
||||
func (c *jsonCodec) CreateErrorResponse(id interface{}, err RPCError) interface{} {
|
||||
return &JSONErrResponse{Version: JSONRPCVersion, Id: id, Error: JSONError{Code: err.Code(), Message: err.Error()}}
|
||||
func (c *jsonCodec) CreateErrorResponse(id interface{}, err Error) interface{} {
|
||||
return &jsonErrResponse{Version: jsonrpcVersion, Id: id, Error: jsonError{Code: err.ErrorCode(), Message: err.Error()}}
|
||||
}
|
||||
|
||||
// CreateErrorResponseWithInfo will create a JSON-RPC error response with the given id and error.
|
||||
// info is optional and contains additional information about the error. When an empty string is passed it is ignored.
|
||||
func (c *jsonCodec) CreateErrorResponseWithInfo(id interface{}, err RPCError, info interface{}) interface{} {
|
||||
return &JSONErrResponse{Version: JSONRPCVersion, Id: id,
|
||||
Error: JSONError{Code: err.Code(), Message: err.Error(), Data: info}}
|
||||
func (c *jsonCodec) CreateErrorResponseWithInfo(id interface{}, err Error, info interface{}) interface{} {
|
||||
return &jsonErrResponse{Version: jsonrpcVersion, Id: id,
|
||||
Error: jsonError{Code: err.ErrorCode(), Message: err.Error(), Data: info}}
|
||||
}
|
||||
|
||||
// CreateNotification will create a JSON-RPC notification with the given subscription id and event as params.
|
||||
func (c *jsonCodec) CreateNotification(subid string, event interface{}) interface{} {
|
||||
if isHexNum(reflect.TypeOf(event)) {
|
||||
return &jsonNotification{Version: JSONRPCVersion, Method: notificationMethod,
|
||||
return &jsonNotification{Version: jsonrpcVersion, Method: notificationMethod,
|
||||
Params: jsonSubscription{Subscription: subid, Result: fmt.Sprintf(`%#x`, event)}}
|
||||
}
|
||||
|
||||
return &jsonNotification{Version: JSONRPCVersion, Method: notificationMethod,
|
||||
return &jsonNotification{Version: jsonrpcVersion, Method: notificationMethod,
|
||||
Params: jsonSubscription{Subscription: subid, Result: event}}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user