Skip to content

Commit

Permalink
Fix errors/ and x/coin/errors.go
Browse files Browse the repository at this point in the history
  • Loading branch information
jaekwon committed Dec 21, 2017
1 parent 5edf50e commit c0598cd
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 166 deletions.
119 changes: 83 additions & 36 deletions errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

const (
// ABCI Response Codes
// Base SDK reserves 0 ~ 99.
CodeInternalError uint32 = 1
CodeTxParseError = 2
CodeBadNonce = 3
Expand All @@ -17,7 +18,7 @@ const (
)

// NOTE: Don't stringer this, we'll put better messages in later.
func codeToDefaultLog(code uint32) string {
func CodeToDefaultLog(code uint32) string {
switch code {
case CodeInternalError:
return "Internal error"
Expand All @@ -40,39 +41,43 @@ func codeToDefaultLog(code uint32) string {
// 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 {
func InternalError(log string) *sdkError {
return newSDKError(CodeInternalError, log)
}

func TxParseError(log string) sdkError {
func TxParseError(log string) *sdkError {
return newSDKError(CodeTxParseError, log)
}

func BadNonce(log string) sdkError {
func BadNonce(log string) *sdkError {
return newSDKError(CodeBadNonce, log)
}

func Unauthorized(log string) sdkError {
func Unauthorized(log string) *sdkError {
return newSDKError(CodeUnauthorized, log)
}

func InsufficientFunds(log string) sdkError {
func InsufficientFunds(log string) *sdkError {
return newSDKError(CodeInsufficientFunds, log)
}

func UnknownRequest(log string) sdkError {
func UnknownRequest(log string) *sdkError {
return newSDKError(CodeUnknownRequest, 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.
Expand Down Expand Up @@ -101,61 +106,103 @@ type sdkError struct {
code uint32
log string
cause error
// TODO stacktrace
// TODO stacktrace, optional.
}

func newSDKError(code uint32, log string) sdkError {
// TODO capture stacktrace if ENV is set
func newSDKError(code uint32, log string) *sdkError {
// TODO capture stacktrace if ENV is set.
if log == "" {
log = codeToDefaultLog(code)
log = CodeToDefaultLog(code)
}
return sdkError{
code: code,
log: log,
return &sdkError{
code: code,
log: log,
cause: nil,
}
}

func (err sdkError) Error() string {
// Implements ABCIError
func (err *sdkError) Error() string {
return fmt.Sprintf("SDKError{%d: %s}", err.code, err.log)
}

// Implements ABCIError
func (err sdkError) ABCICode() uint32 {
func (err *sdkError) ABCICode() uint32 {
return err.code
}

// Implements ABCIError
func (err sdkError) ABCILog() string {
func (err *sdkError) ABCILog() string {
return err.log
}

func (err sdkError) Cause() error {
return err.cause
// Implements pkg/errors.causer
func (err *sdkError) Cause() error {
if err.cause != nil {
return err.cause
}
return err
}

func (err sdkError) WithCause(cause error) sdkError {
copy := err
// Creates a cloned *sdkError with specific cause
func (err *sdkError) WithCause(cause error) *sdkError {
copy := *err
copy.cause = cause
return copy
return &copy
}

// HasErrorCode checks if this error would return the named error code
func HasErrorCode(err error, code uint32) bool {
// XXX Get the cause if not ABCIError
if abciErr, ok := err.(ABCIError); ok {
return abciErr.ABCICode() == code
//----------------------------------------

// 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 code == CodeInternalError
return Cause(err1) == Cause(err2)
}

func IsSameError(pattern error, err error) bool {
return err != nil && (errors.Cause(err) == errors.Cause(pattern))
// 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 err
}
err = errCause
}
return err
}

func WithCode(err error, code uint32) sdkError {
return sdkError{
code: code,
cause: err,
log: "",
// 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
}
59 changes: 3 additions & 56 deletions x/coin/coin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package coin

import (
"fmt"
"regexp"
"sort"
"strconv"
"strings"
Expand Down Expand Up @@ -33,33 +32,8 @@ func (coin Coin) IsGTE(other Coin) bool {
(coin.Amount >= other.Amount)
}

//regex codes for extracting coins from string
var reDenom = regexp.MustCompile("")
var reAmt = regexp.MustCompile("(\\d+)")

var reCoin = regexp.MustCompile("^([[:digit:]]+)[[:space:]]*([[:alpha:]]+)$")

// ParseCoin parses a cli input for one coin type, returning errors if invalid.
// This returns an error on an empty string as well.
func ParseCoin(str string) (Coin, error) {
var coin Coin

matches := reCoin.FindStringSubmatch(strings.TrimSpace(str))
if matches == nil {
return coin, errors.Errorf("%s is invalid coin definition", str)
}

// parse the amount (should always parse properly)
amt, err := strconv.Atoi(matches[1])
if err != nil {
return coin, err
}

coin = Coin{matches[2], int64(amt)}
return coin, nil
}

//----------------------------------------
// Coins

// Coins is a set of Coin, one per currency
type Coins []Coin
Expand All @@ -76,34 +50,6 @@ func (coins Coins) String() string {
return out[:len(out)-1]
}

// ParseCoins will parse out a list of coins separated by commas.
// If nothing is provided, it returns an empty array
func ParseCoins(str string) (Coins, error) {
// empty string is empty list...
if len(str) == 0 {
return nil, nil
}

split := strings.Split(str, ",")
var coins Coins

for _, el := range split {
coin, err := ParseCoin(el)
if err != nil {
return coins, err
}
coins = append(coins, coin)
}

// ensure they are in proper order, to avoid random failures later
coins.Sort()
if !coins.IsValid() {
return nil, errors.Errorf("ParseCoins invalid: %#v", coins)
}

return coins, nil
}

// IsValid asserts the Coins are sorted, and don't have 0 amounts
func (coins Coins) IsValid() bool {
switch len(coins) {
Expand Down Expand Up @@ -242,7 +188,8 @@ func (coins Coins) IsNonnegative() bool {
return true
}

/*** Implement Sort interface ***/
//----------------------------------------
// Sort interface

//nolint
func (coins Coins) Len() int { return len(coins) }
Expand Down
13 changes: 13 additions & 0 deletions x/coin/decorator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package coin

import (
sdk "github.com/cosmos/cosmos-sdk"
)

func Decorator(ctx sdk.Context, store sdk.MultiStore, tx sdk.Tx, next sdk.Handler) sdk.Result {
if msg, ok := tx.(CoinsMsg); ok {
handleCoinsMsg(ctx, store, msg)
} else {
next(ctx, store, tx)
}
}
69 changes: 45 additions & 24 deletions x/coin/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,68 @@ import (
"github.com/cosmos/cosmos-sdk/errors"
)

var (
errNoAccount = fmt.Errorf("No such account")
errInsufficientFunds = fmt.Errorf("Insufficient funds")
errInsufficientCredit = fmt.Errorf("Insufficient credit")
errNoInputs = fmt.Errorf("No input coins")
errNoOutputs = fmt.Errorf("No output coins")
errInvalidAddress = fmt.Errorf("Invalid address")
errInvalidCoins = fmt.Errorf("Invalid coins")
)

const (
// Coin errors reserve 100 ~ 199.
CodeInvalidInput uint32 = 101
CodeInvalidOutput uint32 = 102
CodeInvalidAddress uint32 = 103
CodeUnknownAddress uint32 = 103
CodeUnknownRequest uint32 = errors.CodeUnknownRequest
)

func ErrNoAccount() errors.ABCIError {
return errors.WithCode(errNoAccount, CodeUnknownAddress)
// NOTE: Don't stringer this, we'll put better messages in later.
func codeToDefaultLog(code uint32) string {
switch code {
case CodeInvalidInput:
return "Invalid input coins"
case CodeInvalidOutput:
return "Invalid output coins"
case CodeInvalidAddress:
return "Invalid address"
case CodeUnknownAddress:
return "Unknown address"
case CodeUnknownRequest:
return "Unknown request"
default:
return errors.CodeToDefaultLog(code)
}
}

//----------------------------------------
// Error constructors

func ErrInvalidInput(log string) error {
return newError(CodeInvalidInput, log)
}

func ErrInvalidAddress() errors.ABCIError {
return errors.WithCode(errInvalidAddress, CodeInvalidInput)
func ErrInvalidOutput(log string) error {
return newError(CodeInvalidOutput, log)
}

func ErrInvalidCoins() errors.ABCIError {
return errors.WithCode(errInvalidCoins, CodeInvalidInput)
func ErrInvalidAddress(log string) error {
return newError(CodeInvalidAddress, log)
}

func ErrInsufficientFunds() errors.ABCIError {
return errors.WithCode(errInsufficientFunds, CodeInvalidInput)
func ErrUnknownAddress(log string) error {
return newError(CodeUnknownAddress, log)
}

func ErrInsufficientCredit() errors.ABCIError {
return errors.WithCode(errInsufficientCredit, CodeInvalidInput)
func ErrUnknownRequest(log string) error {
return newError(CodeUnknownRequest, log)
}

func ErrNoInputs() errors.ABCIError {
return errors.WithCode(errNoInputs, CodeInvalidInput)
//----------------------------------------
// Misc

func logOrDefault(log string, code uint32) string {
if log != "" {
return log
} else {
return codeToDefaultLog
}
}

func ErrNoOutputs() errors.ABCIError {
return errors.WithCode(errNoOutputs, CodeInvalidOutput)
func newError(code uint32, log string) error {
log = logOrDefaultLog(log, code)
return errors.NewABCIError(code, log)
}
Loading

0 comments on commit c0598cd

Please sign in to comment.