New error API

This commit is contained in:
Jae Kwon 2018-01-18 00:25:23 -08:00
parent 85cc4321d4
commit 11cccfaf02
4 changed files with 192 additions and 295 deletions

View File

@ -1,55 +0,0 @@
package errors
import (
abci "github.com/tendermint/abci/types"
)
func getABCIError(err error) (ABCIError, bool) {
if err, ok := err.(ABCIError); ok {
return err, true
}
if causer, ok := err.(causer); ok {
err := causer.Cause()
if err, ok := err.(ABCIError); ok {
return err, true
}
}
return nil, false
}
func ResponseDeliverTxFromErr(err error) *abci.ResponseDeliverTx {
var code = CodeInternalError
var log = CodeToDefaultLog(code)
abciErr, ok := getABCIError(err)
if ok {
code = abciErr.ABCICode()
log = abciErr.ABCILog()
}
return &abci.ResponseDeliverTx{
Code: code,
Data: nil,
Log: log,
Tags: nil,
}
}
func ResponseCheckTxFromErr(err error) *abci.ResponseCheckTx {
var code = CodeInternalError
var log = CodeToDefaultLog(code)
abciErr, ok := getABCIError(err)
if ok {
code = abciErr.ABCICode()
log = abciErr.ABCILog()
}
return &abci.ResponseCheckTx{
Code: code,
Data: nil,
Log: log,
// Gas: 0, // TODO
// Fee: 0, // TODO
}
}

View File

@ -1,210 +0,0 @@
package errors
import (
"fmt"
)
const (
// ABCI Response Codes
// Base SDK reserves 0 ~ 99.
CodeInternalError uint32 = 1
CodeTxParseError = 2
CodeBadNonce = 3
CodeUnauthorized = 4
CodeInsufficientFunds = 5
CodeUnknownRequest = 6
CodeUnrecognizedAddress = 7
)
// NOTE: Don't stringer this, we'll put better messages in later.
func CodeToDefaultLog(code uint32) string {
switch code {
case CodeInternalError:
return "Internal error"
case CodeTxParseError:
return "Tx parse error"
case CodeBadNonce:
return "Bad nonce"
case CodeUnauthorized:
return "Unauthorized"
case CodeInsufficientFunds:
return "Insufficent funds"
case CodeUnknownRequest:
return "Unknown request"
case CodeUnrecognizedAddress:
return "Unrecognized address"
default:
return fmt.Sprintf("Unknown code %d", code)
}
}
//--------------------------------------------------------------------------------
// All errors are created via constructors so as to enable us to hijack them
// and inject stack traces if we really want to.
func InternalError(log string) *sdkError {
return newSDKError(CodeInternalError, log)
}
func TxParseError(log string) *sdkError {
return newSDKError(CodeTxParseError, log)
}
func BadNonce(log string) *sdkError {
return newSDKError(CodeBadNonce, log)
}
func Unauthorized(log string) *sdkError {
return newSDKError(CodeUnauthorized, log)
}
func InsufficientFunds(log string) *sdkError {
return newSDKError(CodeInsufficientFunds, log)
}
func UnknownRequest(log string) *sdkError {
return newSDKError(CodeUnknownRequest, log)
}
func UnrecognizedAddress(log string) *sdkError {
return newSDKError(CodeUnrecognizedAddress, log)
}
//----------------------------------------
// ABCIError & sdkError
type ABCIError interface {
ABCICode() uint32
ABCILog() string
Error() string
}
func NewABCIError(code uint32, log string) ABCIError {
return newSDKError(code, log)
}
/*
This struct is intended to be used with pkg/errors.
Usage:
```
import sdk "github.com/cosmos/cosmos-sdk"
import "github.com/pkg/errors"
var err = <some causal error>
if err != nil {
err = sdk.InternalError("").WithCause(err)
err = errors.Wrap(err, "Captured the stack!")
return err
}
```
Then, to get the ABCI code/log, use ABCIErrorCause()
*/
type sdkError struct {
code uint32
log string
cause error
// TODO stacktrace, optional.
}
func newSDKError(code uint32, log string) *sdkError {
// TODO capture stacktrace if ENV is set.
if log == "" {
log = CodeToDefaultLog(code)
}
return &sdkError{
code: code,
log: log,
cause: nil,
}
}
// Implements ABCIError
func (err *sdkError) Error() string {
return fmt.Sprintf("SDKError{%d: %s}", err.code, err.log)
}
// Implements ABCIError
func (err *sdkError) ABCICode() uint32 {
return err.code
}
// Implements ABCIError
func (err *sdkError) ABCILog() string {
return err.log
}
// Implements pkg/errors.causer
func (err *sdkError) Cause() error {
if err.cause != nil {
return err.cause
}
return err
}
// Creates a cloned *sdkError with specific cause
func (err *sdkError) WithCause(cause error) *sdkError {
copy := *err
copy.cause = cause
return &copy
}
//----------------------------------------
// HasSameCause returns true if both errors
// have the same cause.
func HasSameCause(err1 error, err2 error) bool {
if err1 != nil || err2 != nil {
panic("HasSomeCause() requires non-nil arguments")
}
return Cause(err1) == Cause(err2)
}
// Like Cause but stops upon finding an ABCIError.
// If no error in the cause chain is an ABCIError,
// returns nil.
func ABCIErrorCause(err error) ABCIError {
for err != nil {
abciErr, ok := err.(ABCIError)
if ok {
return abciErr
}
cause, ok := err.(causer)
if !ok {
return nil
}
errCause := cause.Cause()
if errCause == nil || errCause == err {
return nil
}
err = errCause
}
return nil
}
// Identitical to pkg/errors.Cause, except handles .Cause()
// returning itself.
// TODO: Merge https://github.com/pkg/errors/issues/89 and
// delete this.
func Cause(err error) error {
for err != nil {
cause, ok := err.(causer)
if !ok {
return err
}
errCause := cause.Cause()
if errCause == nil || errCause == err {
return err
}
err = errCause
}
return err
}
type causer interface {
Cause() error
}

165
types/errors.go Normal file
View File

@ -0,0 +1,165 @@
package types
import (
"fmt"
)
const (
// ABCI Response Codes
// Base SDK reserves 0 ~ 99.
CodeInternalError uint32 = 1
CodeTxParseError = 2
CodeBadNonce = 3
CodeUnauthorized = 4
CodeInsufficientFunds = 5
CodeUnknownRequest = 6
CodeUnrecognizedAddress = 7
)
// NOTE: Don't stringer this, we'll put better messages in later.
func CodeToDefaultLog(code uint32) string {
switch code {
case CodeInternalError:
return "Internal error"
case CodeTxParseError:
return "Tx parse error"
case CodeBadNonce:
return "Bad nonce"
case CodeUnauthorized:
return "Unauthorized"
case CodeInsufficientFunds:
return "Insufficent funds"
case CodeUnknownRequest:
return "Unknown request"
case CodeUnrecognizedAddress:
return "Unrecognized address"
default:
return fmt.Sprintf("Unknown code %d", code)
}
}
//--------------------------------------------------------------------------------
// All errors are created via constructors so as to enable us to hijack them
// and inject stack traces if we really want to.
func InternalError(log string) Error {
return newError(CodeInternalError, log)
}
func TxParseError(log string) Error {
return newError(CodeTxParseError, log)
}
func BadNonce(log string) Error {
return newError(CodeBadNonce, log)
}
func Unauthorized(log string) Error {
return newError(CodeUnauthorized, log)
}
func InsufficientFunds(log string) Error {
return newError(CodeInsufficientFunds, log)
}
func UnknownRequest(log string) Error {
return newError(CodeUnknownRequest, log)
}
func UnrecognizedAddress(log string) Error {
return newError(CodeUnrecognizedAddress, log)
}
//----------------------------------------
// Error & sdkError
type Error interface {
Error() string
ABCICode() uint32
ABCILog() string
Trace(msg string) Error
TraceCause(cause error, msg string) Error
Cause() error
Result() Result
}
func NewError(code uint32, log string) Error {
return newError(code, log)
}
type traceItem struct {
msg string
filename string
lineno int
}
type sdkError struct {
code uint32
log string
cause error
trace []traceItem
}
func newError(code uint32, log string) *sdkError {
// TODO capture stacktrace if ENV is set.
if log == "" {
log = CodeToDefaultLog(code)
}
return &sdkError{
code: code,
log: log,
cause: nil,
trace: nil,
}
}
// Implements ABCIError.
func (err *sdkError) Error() string {
return fmt.Sprintf("Error{%d:%s,%v,%v}", err.code, err.log, err.cause, len(err.trace))
}
// Implements ABCIError.
func (err *sdkError) ABCICode() uint32 {
return err.code
}
// Implements ABCIError.
func (err *sdkError) ABCILog() string {
return err.log
}
// Add tracing information to log with msg.
func (err *sdkError) Trace(msg string) Error {
// Include file & line number & msg to log.
// Do not include the whole stack trace.
err.trace = append(err.trace, traceItem{
filename: "todo", // TODO
lineno: -1, // TODO
msg: msg,
})
return err
}
// Add tracing information to log with cause and msg.
func (err *sdkError) TraceCause(cause error, msg string) Error {
err.cause = cause
// Include file & line number & cause & msg to log.
// Do not include the whole stack trace.
err.trace = append(err.trace, traceItem{
filename: "todo", // TODO
lineno: -1, // TODO
msg: msg,
})
return err
}
func (err *sdkError) Cause() error {
return err.cause
}
func (err *sdkError) Result() Result {
return Result{
Code: err.ABCICode(),
Log: err.ABCILog(),
}
}

View File

@ -2,9 +2,7 @@
package bank
import (
"fmt"
"github.com/cosmos/cosmos-sdk/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
)
const (
@ -15,7 +13,8 @@ const (
CodeUnknownAddress uint32 = 104
CodeInsufficientCoins uint32 = 105
CodeInvalidCoins uint32 = 106
CodeUnknownRequest uint32 = errors.CodeUnknownRequest
CodeInvalidSequence uint32 = 107
CodeUnknownRequest uint32 = sdk.CodeUnknownRequest
)
// NOTE: Don't stringer this, we'll put better messages in later.
@ -33,61 +32,59 @@ func codeToDefaultLog(code uint32) string {
return "Insufficient coins"
case CodeInvalidCoins:
return "Invalid coins"
case CodeInvalidSequence:
return "Invalid sequence"
case CodeUnknownRequest:
return "Unknown request"
default:
return errors.CodeToDefaultLog(code)
return sdk.CodeToDefaultLog(code)
}
}
//----------------------------------------
// Error constructors
func ErrInvalidInput(log string) error {
func ErrInvalidInput(log string) sdk.Error {
return newError(CodeInvalidInput, log)
}
func ErrInvalidOutput(log string) error {
func ErrNoInputs() sdk.Error {
return newError(CodeInvalidInput, "")
}
func ErrInvalidOutput(log string) sdk.Error {
return newError(CodeInvalidOutput, log)
}
func ErrInvalidAddress(log string) error {
func ErrNoOutputs() sdk.Error {
return newError(CodeInvalidOutput, "")
}
func ErrInvalidSequence(seq int64) sdk.Error {
return newError(CodeInvalidSequence, "")
}
func ErrInvalidAddress(log string) sdk.Error {
return newError(CodeInvalidAddress, log)
}
func ErrUnknownAddress(log string) error {
func ErrUnknownAddress(log string) sdk.Error {
return newError(CodeUnknownAddress, log)
}
func ErrInsufficientCoins(log string) error {
func ErrInsufficientCoins(log string) sdk.Error {
return newError(CodeInsufficientCoins, log)
}
func ErrInvalidCoins(log string) error {
func ErrInvalidCoins(log string) sdk.Error {
return newError(CodeInvalidCoins, log)
}
func ErrUnknownRequest(log string) error {
func ErrUnknownRequest(log string) sdk.Error {
return newError(CodeUnknownRequest, log)
}
//----------------------------------------
// TODO: clean up
func ErrNoInputs() error {
return fmt.Errorf("No inputs")
}
func ErrNoOutputs() error {
return fmt.Errorf("No outputs")
}
func ErrInvalidSequence(seq int64) error {
return fmt.Errorf("Bad sequence %d", seq)
}
//----------------------------------------
// Misc
func logOrDefaultLog(log string, code uint32) string {
if log != "" {
@ -97,7 +94,7 @@ func logOrDefaultLog(log string, code uint32) string {
}
}
func newError(code uint32, log string) error {
func newError(code uint32, log string) sdk.Error {
log = logOrDefaultLog(log, code)
return errors.NewABCIError(code, log)
return sdk.NewError(code, log)
}