From 094f921e5028fc215efbc86118e3d3e5b0663055 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 12 Mar 2015 19:07:03 -0500 Subject: [PATCH] Convert to proper errors Allow returning different JSON RPC error codes depending on error type --- rpc/api.go | 18 +++---- rpc/args.go | 125 +++++++++++++++++++++++++++++------------------- rpc/http.go | 39 +++++++++++++-- rpc/messages.go | 99 +++++++++++++++++++++++--------------- rpc/util.go | 6 +-- 5 files changed, 183 insertions(+), 104 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index b94d2d6dc..b72a0dd60 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -578,7 +578,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error } return p.Call(args, reply) case "eth_flush": - return errNotImplemented + return NewNotImplementedError(req.Method) case "eth_getBlockByHash": args := new(GetBlockByHashArgs) if err := json.Unmarshal(req.Params, &args); err != nil { @@ -618,7 +618,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return err } if args.Index > int64(len(v.Transactions)) || args.Index < 0 { - return NewErrorWithMessage(errDecodeArgs, "Transaction index does not exist") + return NewValidationError("Index", "does not exist") } *reply = v.Transactions[args.Index] case "eth_getTransactionByBlockNumberAndIndex": @@ -632,7 +632,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return err } if args.Index > int64(len(v.Transactions)) || args.Index < 0 { - return NewErrorWithMessage(errDecodeArgs, "Transaction index does not exist") + return NewValidationError("Index", "does not exist") } *reply = v.Transactions[args.Index] case "eth_getUncleByBlockHashAndIndex": @@ -646,7 +646,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return err } if args.Index > int64(len(v.Uncles)) || args.Index < 0 { - return NewErrorWithMessage(errDecodeArgs, "Uncle index does not exist") + return NewValidationError("Index", "does not exist") } uncle, err := p.GetBlockByHash(toHex(v.Uncles[args.Index]), false) @@ -665,7 +665,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return err } if args.Index > int64(len(v.Uncles)) || args.Index < 0 { - return NewErrorWithMessage(errDecodeArgs, "Uncle index does not exist") + return NewValidationError("Index", "does not exist") } uncle, err := p.GetBlockByHash(toHex(v.Uncles[args.Index]), false) @@ -678,7 +678,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error case "eth_compileSolidity": case "eth_compileLLL": case "eth_compileSerpent": - return errNotImplemented + return NewNotImplementedError(req.Method) case "eth_newFilter": args := new(FilterOptions) if err := json.Unmarshal(req.Params, &args); err != nil { @@ -717,7 +717,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return p.AllLogs(args, reply) case "eth_getWork": case "eth_submitWork": - return errNotImplemented + return NewNotImplementedError(req.Method) case "db_put": args := new(DbArgs) if err := json.Unmarshal(req.Params, &args); err != nil { @@ -746,7 +746,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return p.HasWhisperIdentity(args.Identity, reply) case "shh_newGroup": case "shh_addToGroup": - return errNotImplemented + return NewNotImplementedError(req.Method) case "shh_newFilter": args := new(WhisperFilterArgs) if err := json.Unmarshal(req.Params, &args); err != nil { @@ -790,7 +790,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error // } // return p.WatchTx(args, reply) default: - return NewErrorWithMessage(errNotImplemented, req.Method) + return NewNotImplementedError(req.Method) } rpclogger.DebugDetailf("Reply: %T %s", reply, reply) diff --git a/rpc/args.go b/rpc/args.go index faca03b63..8d4ad5967 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -11,7 +11,7 @@ import ( func blockNumber(raw json.RawMessage, number *int64) (err error) { var str string if err = json.Unmarshal(raw, &str); err != nil { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } switch str { @@ -34,16 +34,16 @@ func (args *GetBlockByHashArgs) UnmarshalJSON(b []byte) (err error) { var obj []interface{} r := bytes.NewReader(b) if err := json.NewDecoder(r).Decode(&obj); err != nil { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } if len(obj) < 1 { - return errArguments + return NewInsufficientParamsError(len(obj), 1) } argstr, ok := obj[0].(string) if !ok { - return errDecodeArgs + return NewDecodeParamError("BlockHash not a string") } args.BlockHash = argstr @@ -63,11 +63,11 @@ func (args *GetBlockByNumberArgs) UnmarshalJSON(b []byte) (err error) { var obj []interface{} r := bytes.NewReader(b) if err := json.NewDecoder(r).Decode(&obj); err != nil { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } if len(obj) < 1 { - return errArguments + return NewInsufficientParamsError(len(obj), 1) } if v, ok := obj[0].(float64); ok { @@ -117,7 +117,7 @@ type GetStorageArgs struct { func (args *GetStorageArgs) UnmarshalJSON(b []byte) (err error) { if err = UnmarshalRawMessages(b, &args.Address, &args.BlockNumber); err != nil { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } return nil @@ -125,7 +125,7 @@ func (args *GetStorageArgs) UnmarshalJSON(b []byte) (err error) { func (args *GetStorageArgs) requirements() error { if len(args.Address) == 0 { - return NewErrorWithMessage(errArguments, "Address cannot be blank") + return NewValidationError("Address", "cannot be blank") } return nil } @@ -139,10 +139,10 @@ type GetStorageAtArgs struct { func (args *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) { var obj []string if err = UnmarshalRawMessages(b, &obj, &args.BlockNumber); err != nil { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } if len(obj) < 2 { - return errDecodeArgs + return NewInsufficientParamsError(len(obj), 2) } args.Address = obj[0] @@ -153,11 +153,11 @@ func (args *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) { func (args *GetStorageAtArgs) requirements() error { if len(args.Address) == 0 { - return NewErrorWithMessage(errArguments, "Address cannot be blank") + return NewValidationError("Address", "cannot be blank") } if len(args.Key) == 0 { - return NewErrorWithMessage(errArguments, "Key cannot be blank") + return NewValidationError("Key", "cannot be blank") } return nil } @@ -169,7 +169,7 @@ type GetTxCountArgs struct { func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) { if err = UnmarshalRawMessages(b, &args.Address, &args.BlockNumber); err != nil { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } return nil @@ -177,7 +177,7 @@ func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) { func (args *GetTxCountArgs) requirements() error { if len(args.Address) == 0 { - return NewErrorWithMessage(errArguments, "Address cannot be blank") + return NewValidationError("Address", "cannot be blank") } return nil } @@ -191,16 +191,16 @@ func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) { var obj []interface{} r := bytes.NewReader(b) if err := json.NewDecoder(r).Decode(&obj); err != nil { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } if len(obj) < 1 { - return errArguments + return NewInsufficientParamsError(len(obj), 1) } addstr, ok := obj[0].(string) if !ok { - return errDecodeArgs + return NewDecodeParamError("Address is not a string") } args.Address = addstr @@ -213,7 +213,7 @@ func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) { } // if err = UnmarshalRawMessages(b, &args.Address, &args.BlockNumber); err != nil { - // return errDecodeArgs + // return NewDecodeParamError(err.Error()) // } return nil @@ -221,7 +221,7 @@ func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) { func (args *GetBalanceArgs) requirements() error { if len(args.Address) == 0 { - return NewErrorWithMessage(errArguments, "Address cannot be blank") + return NewValidationError("Address", "cannot be blank") } return nil } @@ -233,7 +233,7 @@ type GetDataArgs struct { func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) { if err = UnmarshalRawMessages(b, &args.Address, &args.BlockNumber); err != nil { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } return nil @@ -241,7 +241,7 @@ func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) { func (args *GetDataArgs) requirements() error { if len(args.Address) == 0 { - return NewErrorWithMessage(errArguments, "Address cannot be blank") + return NewValidationError("Address", "cannot be blank") } return nil } @@ -255,23 +255,23 @@ func (args *BlockNumIndexArgs) UnmarshalJSON(b []byte) (err error) { var obj []interface{} r := bytes.NewReader(b) if err := json.NewDecoder(r).Decode(&obj); err != nil { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } if len(obj) < 1 { - return errArguments + return NewInsufficientParamsError(len(obj), 1) } arg0, ok := obj[0].(string) if !ok { - return errDecodeArgs + return NewDecodeParamError("BlockNumber is not string") } args.BlockNumber = ethutil.Big(arg0).Int64() if len(obj) > 1 { arg1, ok := obj[1].(string) if !ok { - return errDecodeArgs + return NewDecodeParamError("Index not a string") } args.Index = ethutil.Big(arg1).Int64() } @@ -288,23 +288,23 @@ func (args *HashIndexArgs) UnmarshalJSON(b []byte) (err error) { var obj []interface{} r := bytes.NewReader(b) if err := json.NewDecoder(r).Decode(&obj); err != nil { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } if len(obj) < 1 { - return errArguments + return NewInsufficientParamsError(len(obj), 1) } arg0, ok := obj[0].(string) if !ok { - return errDecodeArgs + return NewDecodeParamError("Hash not a string") } args.Hash = arg0 if len(obj) > 1 { arg1, ok := obj[1].(string) if !ok { - return errDecodeArgs + return NewDecodeParamError("Index not a string") } args.Index = ethutil.Big(arg1).Int64() } @@ -320,11 +320,11 @@ func (args *Sha3Args) UnmarshalJSON(b []byte) (err error) { var obj []interface{} r := bytes.NewReader(b) if err := json.NewDecoder(r).Decode(&obj); err != nil { - return NewErrorWithMessage(errDecodeArgs, err.Error()) + return NewDecodeParamError(err.Error()) } if len(obj) < 1 { - return errArguments + return NewInsufficientParamsError(len(obj), 1) } args.Data = obj[0].(string) @@ -387,12 +387,13 @@ func (args *FilterOptions) UnmarshalJSON(b []byte) (err error) { } if err = json.Unmarshal(b, &obj); err != nil { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } if len(obj) < 1 { - return errArguments + return NewInsufficientParamsError(len(obj), 1) } + args.Earliest = int64(ethutil.Big(obj[0].FromBlock).Int64()) args.Latest = int64(ethutil.Big(obj[0].ToBlock).Int64()) args.Max = int(ethutil.Big(obj[0].Limit).Int64()) @@ -417,11 +418,11 @@ func (args *DbArgs) UnmarshalJSON(b []byte) (err error) { var obj []interface{} r := bytes.NewReader(b) if err := json.NewDecoder(r).Decode(&obj); err != nil { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } if len(obj) < 2 { - return errArguments + return NewInsufficientParamsError(len(obj), 2) } args.Database = obj[0].(string) args.Key = obj[1].(string) @@ -435,10 +436,10 @@ func (args *DbArgs) UnmarshalJSON(b []byte) (err error) { func (a *DbArgs) requirements() error { if len(a.Database) == 0 { - return NewErrorWithMessage(errArguments, "Database cannot be blank") + return NewValidationError("Database", "cannot be blank") } if len(a.Key) == 0 { - return NewErrorWithMessage(errArguments, "Key cannot be blank") + return NewValidationError("Key", "cannot be blank") } return nil } @@ -463,11 +464,11 @@ func (args *WhisperMessageArgs) UnmarshalJSON(b []byte) (err error) { } if err = json.Unmarshal(b, &obj); err != nil { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } if len(obj) < 1 { - return errArguments + return NewInsufficientParamsError(len(obj), 1) } args.Payload = obj[0].Payload args.To = obj[0].To @@ -487,7 +488,7 @@ func (args *CompileArgs) UnmarshalJSON(b []byte) (err error) { var obj []interface{} r := bytes.NewReader(b) if err := json.NewDecoder(r).Decode(&obj); err != nil { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } if len(obj) > 0 { @@ -505,11 +506,11 @@ func (args *FilterStringArgs) UnmarshalJSON(b []byte) (err error) { var obj []string r := bytes.NewReader(b) if err := json.NewDecoder(r).Decode(&obj); err != nil { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } if len(obj) < 1 { - return errDecodeArgs + return NewInsufficientParamsError(len(obj), 1) } args.Word = obj[0] @@ -525,11 +526,11 @@ func (args *FilterIdArgs) UnmarshalJSON(b []byte) (err error) { var obj []string r := bytes.NewReader(b) if err := json.NewDecoder(r).Decode(&obj); err != nil { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } if len(obj) < 1 { - return errDecodeArgs + return NewInsufficientParamsError(len(obj), 1) } args.Id = int(ethutil.Big(obj[0]).Int64()) @@ -545,11 +546,11 @@ func (args *WhisperIdentityArgs) UnmarshalJSON(b []byte) (err error) { var obj []string r := bytes.NewReader(b) if err := json.NewDecoder(r).Decode(&obj); err != nil { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } if len(obj) < 1 { - return errDecodeArgs + return NewInsufficientParamsError(len(obj), 1) } args.Identity = obj[0] @@ -571,11 +572,11 @@ func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { } if err = json.Unmarshal(b, &obj); err != nil { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } if len(obj) < 1 { - return errArguments + return NewInsufficientParamsError(len(obj), 1) } args.To = obj[0].To @@ -584,3 +585,31 @@ func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { return nil } + +// func (req *RpcRequest) ToRegisterArgs() (string, error) { +// if len(req.Params) < 1 { +// return "", errArguments +// } + +// var args string +// err := json.Unmarshal(req.Params, &args) +// if err != nil { +// return "", err +// } + +// return args, nil +// } + +// func (req *RpcRequest) ToWatchTxArgs() (string, error) { +// if len(req.Params) < 1 { +// return "", errArguments +// } + +// var args string +// err := json.Unmarshal(req.Params, &args) +// if err != nil { +// return "", err +// } + +// return args, nil +// } diff --git a/rpc/http.go b/rpc/http.go index 857cf3221..8b45319ff 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -25,22 +25,51 @@ func JSONRPC(pipe *xeth.XEth, dataDir string) http.Handler { rpchttplogger.DebugDetailln("Handling request") if req.ContentLength > maxSizeReqLength { - jsonerr := &RpcErrorObject{-32700, "Error: Request too large"} + jsonerr := &RpcErrorObject{-32700, "Request too large"} json.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr}) return } reqParsed, reqerr := json.ParseRequestBody(req) - if reqerr != nil { - jsonerr := &RpcErrorObject{-32700, "Error: Could not parse request"} + switch reqerr.(type) { + case nil: + break + case *DecodeParamError: + jsonerr := &RpcErrorObject{-32602, reqerr.Error()} + json.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr}) + return + case *InsufficientParamsError: + jsonerr := &RpcErrorObject{-32602, reqerr.Error()} + json.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr}) + return + case *ValidationError: + jsonerr := &RpcErrorObject{-32602, reqerr.Error()} + json.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr}) + return + default: + jsonerr := &RpcErrorObject{-32700, "Could not parse request"} json.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr}) return } var response interface{} reserr := api.GetRequestReply(&reqParsed, &response) - if reserr != nil { - rpchttplogger.Warnln(reserr) + switch reserr.(type) { + case nil: + break + case *NotImplementedError: + jsonerr := &RpcErrorObject{-32601, reserr.Error()} + json.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: reqParsed.ID, Error: jsonerr}) + return + case *InsufficientParamsError: + jsonerr := &RpcErrorObject{-32602, reserr.Error()} + json.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: reqParsed.ID, Error: jsonerr}) + return + case *ValidationError: + jsonerr := &RpcErrorObject{-32602, reserr.Error()} + json.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: reqParsed.ID, Error: jsonerr}) + return + default: jsonerr := &RpcErrorObject{-32603, reserr.Error()} json.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: reqParsed.ID, Error: jsonerr}) return diff --git a/rpc/messages.go b/rpc/messages.go index a3ebbf330..781394196 100644 --- a/rpc/messages.go +++ b/rpc/messages.go @@ -18,16 +18,69 @@ package rpc import ( "encoding/json" - "errors" "fmt" ) -var ( - errArguments = errors.New("Error: Insufficient arguments") - errNotImplemented = errors.New("Error: Method not implemented") - errUnknown = errors.New("Error: Unknown error") - errDecodeArgs = errors.New("Error: Could not decode arguments") -) +type InsufficientParamsError struct { + have int + want int +} + +func (e *InsufficientParamsError) Error() string { + return fmt.Sprintf("insufficient params, want %d have %d", e.want, e.have) +} + +func NewInsufficientParamsError(have int, want int) *InsufficientParamsError { + return &InsufficientParamsError{ + have: have, + want: want, + } +} + +type NotImplementedError struct { + Method string +} + +func (e *NotImplementedError) Error() string { + return fmt.Sprintf("%s method not implemented", e.Method) +} + +func NewNotImplementedError(method string) *NotImplementedError { + return &NotImplementedError{ + Method: method, + } +} + +type DecodeParamError struct { + err string +} + +func (e *DecodeParamError) Error() string { + return fmt.Sprintf("could not decode, %s", e.err) + +} + +func NewDecodeParamError(errstr string) error { + return &DecodeParamError{ + err: errstr, + } +} + +type ValidationError struct { + ParamName string + msg string +} + +func (e *ValidationError) Error() string { + return fmt.Sprintf("%s not valid, %s", e.ParamName, e.msg) +} + +func NewValidationError(param string, msg string) error { + return &ValidationError{ + ParamName: param, + msg: msg, + } +} type RpcRequest struct { ID interface{} `json:"id"` @@ -53,35 +106,3 @@ type RpcErrorObject struct { Message string `json:"message"` // Data interface{} `json:"data"` } - -func NewErrorWithMessage(err error, msg string) error { - return fmt.Errorf("%s: %s", err.Error(), msg) -} - -// func (req *RpcRequest) ToRegisterArgs() (string, error) { -// if len(req.Params) < 1 { -// return "", errArguments -// } - -// var args string -// err := json.Unmarshal(req.Params, &args) -// if err != nil { -// return "", err -// } - -// return args, nil -// } - -// func (req *RpcRequest) ToWatchTxArgs() (string, error) { -// if len(req.Params) < 1 { -// return "", errArguments -// } - -// var args string -// err := json.Unmarshal(req.Params, &args) -// if err != nil { -// return "", err -// } - -// return args, nil -// } diff --git a/rpc/util.go b/rpc/util.go index 8ff3c6d31..573190e59 100644 --- a/rpc/util.go +++ b/rpc/util.go @@ -42,7 +42,7 @@ type JsonWrapper struct{} func UnmarshalRawMessages(b []byte, iface interface{}, number *int64) (err error) { var data []json.RawMessage if err = json.Unmarshal(b, &data); err != nil && len(data) == 0 { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } // Number index determines the index in the array for a possible block number @@ -74,7 +74,7 @@ func UnmarshalRawMessages(b []byte, iface interface{}, number *int64) (err error fallthrough default: if err = json.Unmarshal(data[0], iface); err != nil { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } numberIndex = 1 } @@ -82,7 +82,7 @@ func UnmarshalRawMessages(b []byte, iface interface{}, number *int64) (err error // <0 index means out of bound for block number if numberIndex >= 0 && len(data) > numberIndex { if err = blockNumber(data[numberIndex], number); err != nil { - return errDecodeArgs + return NewDecodeParamError(err.Error()) } }