Fix errors/ and x/coin/errors.go
This commit is contained in:
parent
a9b2636439
commit
6b5f08e918
123
errors/errors.go
123
errors/errors.go
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
const (
|
||||
// ABCI Response Codes
|
||||
// Base SDK reserves 0 ~ 99.
|
||||
CodeInternalError uint32 = 1
|
||||
CodeTxParseError = 2
|
||||
CodeBadNonce = 3
|
||||
|
@ -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"
|
||||
|
@ -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.
|
||||
|
@ -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 ©
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
||||
func WithCode(err error, code uint32) sdkError {
|
||||
return sdkError{
|
||||
code: code,
|
||||
cause: err,
|
||||
log: "",
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package coin
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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) }
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
func ErrInvalidAddress() errors.ABCIError {
|
||||
return errors.WithCode(errInvalidAddress, CodeInvalidInput)
|
||||
//----------------------------------------
|
||||
// Error constructors
|
||||
|
||||
func ErrInvalidInput(log string) error {
|
||||
return newError(CodeInvalidInput, log)
|
||||
}
|
||||
|
||||
func ErrInvalidCoins() errors.ABCIError {
|
||||
return errors.WithCode(errInvalidCoins, CodeInvalidInput)
|
||||
func ErrInvalidOutput(log string) error {
|
||||
return newError(CodeInvalidOutput, log)
|
||||
}
|
||||
|
||||
func ErrInsufficientFunds() errors.ABCIError {
|
||||
return errors.WithCode(errInsufficientFunds, CodeInvalidInput)
|
||||
func ErrInvalidAddress(log string) error {
|
||||
return newError(CodeInvalidAddress, log)
|
||||
}
|
||||
|
||||
func ErrInsufficientCredit() errors.ABCIError {
|
||||
return errors.WithCode(errInsufficientCredit, CodeInvalidInput)
|
||||
func ErrUnknownAddress(log string) error {
|
||||
return newError(CodeUnknownAddress, log)
|
||||
}
|
||||
|
||||
func ErrNoInputs() errors.ABCIError {
|
||||
return errors.WithCode(errNoInputs, CodeInvalidInput)
|
||||
func ErrUnknownRequest(log string) error {
|
||||
return newError(CodeUnknownRequest, log)
|
||||
}
|
||||
|
||||
func ErrNoOutputs() errors.ABCIError {
|
||||
return errors.WithCode(errNoOutputs, CodeInvalidOutput)
|
||||
//----------------------------------------
|
||||
// Misc
|
||||
|
||||
func logOrDefault(log string, code uint32) string {
|
||||
if log != "" {
|
||||
return log
|
||||
} else {
|
||||
return codeToDefaultLog
|
||||
}
|
||||
}
|
||||
|
||||
func newError(code uint32, log string) error {
|
||||
log = logOrDefaultLog(log, code)
|
||||
return errors.NewABCIError(code, log)
|
||||
}
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
package coin
|
||||
|
||||
import "github.com/tendermint/go-wire/data"
|
||||
|
||||
// TEMP
|
||||
|
||||
type Actor struct {
|
||||
ChainID string
|
||||
App string
|
||||
Address data.Bytes
|
||||
}
|
||||
|
||||
// Account - coin account structure
|
||||
type Account struct {
|
||||
// Coins is how much is on the account
|
||||
Coins Coins `json:"coins"`
|
||||
// Credit is how much has been "fronted" to the account
|
||||
// (this is usually 0 except for trusted chains)
|
||||
Credit Coins `json:"credit"`
|
||||
}
|
47
x/coin/tx.go
47
x/coin/tx.go
|
@ -1,47 +1,34 @@
|
|||
package coin
|
||||
|
||||
// TODO rename this to msg.go
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
)
|
||||
|
||||
/*func init() {
|
||||
sdk.TxMapper.
|
||||
RegisterImplementation(SendTx{}, TypeSend, ByteSend).
|
||||
RegisterImplementation(CreditTx{}, TypeCredit, ByteCredit)
|
||||
}*/
|
||||
|
||||
// we reserve the 0x20-0x3f range for standard modules
|
||||
const (
|
||||
NameCoin = "coin"
|
||||
|
||||
ByteSend = 0x20
|
||||
TypeSend = NameCoin + "/send"
|
||||
ByteCredit = 0x21
|
||||
TypeCredit = NameCoin + "/credit"
|
||||
)
|
||||
type CoinMsg interface {
|
||||
AssertIsCoinMsg()
|
||||
Type() string // "send", "credit"
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// TxInput - expected coin movement outputs, used with SendTx
|
||||
type TxInput struct {
|
||||
Address Actor `json:"address"`
|
||||
Coins Coins `json:"coins"`
|
||||
// Input is a source of coins in a transaction.
|
||||
type Input struct {
|
||||
Address cmn.Bytes
|
||||
Coins Coins
|
||||
}
|
||||
|
||||
// ValidateBasic - validate transaction input
|
||||
func (txIn TxInput) ValidateBasic() error {
|
||||
if txIn.Address.App == "" {
|
||||
func (in Input) ValidateBasic() error {
|
||||
if !auth.IsValidAddress(in.Address) {
|
||||
return ErrInvalidAddress()
|
||||
}
|
||||
// TODO: knowledge of app-specific codings?
|
||||
if len(txIn.Address.Address) == 0 {
|
||||
return ErrInvalidAddress()
|
||||
if !in.Coins.IsValid() {
|
||||
return ErrInvalidInput()
|
||||
}
|
||||
if !txIn.Coins.IsValid() {
|
||||
return ErrInvalidCoins()
|
||||
}
|
||||
if !txIn.Coins.IsPositive() {
|
||||
return ErrInvalidCoins()
|
||||
if !in.Coins.IsPositive() {
|
||||
return ErrInvalidInput()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package coin
|
||||
|
||||
type Coinser interface {
|
||||
GetCoins() Coins
|
||||
SetCoins(Coins)
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package coin
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// Denominations can be 3 ~ 16 characters long.
|
||||
rDnm = `[[:alpha:]][[:alnum:]]{2,15}`
|
||||
rAmt = `[[:digit:]]+`
|
||||
rSpc = `[[:space:]]*`
|
||||
reCoin_ = fmt.Sprintf(`^(%s)%s(%s)$`, reDenom_, re_, reAmt_)
|
||||
)
|
||||
|
||||
// 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(coinStr string) (coin Coin, err error) {
|
||||
coinStr = strings.TrimSpace(coinStr)
|
||||
|
||||
matches := reCoin.FindStringSubmatch(coinStr)
|
||||
if matches == nil {
|
||||
err = errors.Errorf("Invalid coin expression: %s", coinStr)
|
||||
return
|
||||
}
|
||||
denomStr, amountStr := matches[2], matches[1]
|
||||
|
||||
amount, err := strconv.Atoi(amountStr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return Coin{denomStr, int64(amount)}, nil
|
||||
}
|
||||
|
||||
// ParseCoins will parse out a list of coins separated by commas.
|
||||
// If nothing is provided, it returns nil Coins.
|
||||
// Returned coins are sorted.
|
||||
func ParseCoins(coinsStr string) (coins Coins, err error) {
|
||||
coinsStr = strings.TrimSpace(coinsStr)
|
||||
if len(coinsStr) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
coinStrs := strings.Split(coinsStr, ",")
|
||||
for _, coinStr := range coinStrs {
|
||||
coin, err := ParseCoin(coinStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
coins = append(coins, coin)
|
||||
}
|
||||
|
||||
// Sort coins for determinism.
|
||||
coins.Sort()
|
||||
|
||||
// Validate coins before returning.
|
||||
if !coins.IsValid() {
|
||||
return nil, errors.Errorf("ParseCoins invalid: %#v", coins)
|
||||
}
|
||||
|
||||
return coins, nil
|
||||
}
|
Loading…
Reference in New Issue