diff --git a/pkg/api/controllers/transaction_controller.go b/pkg/api/controllers/transaction_controller.go index 50a3c84b6..7b6347062 100644 --- a/pkg/api/controllers/transaction_controller.go +++ b/pkg/api/controllers/transaction_controller.go @@ -80,14 +80,16 @@ func (ctl *TransactionController) PostTransaction(c *gin.Context) { switch eerr.Code { case storage.ConstraintFailed: ctl.responseError(c, http.StatusConflict, err) - return + default: + ctl.responseError(c, http.StatusInternalServerError, err) } + case *ledger.InsufficientFundError: + ctl.responseError(c, http.StatusBadRequest, err) + case *ledger.ValidationError: + ctl.responseError(c, http.StatusBadRequest, err) + default: + ctl.responseError(c, http.StatusInternalServerError, err) } - ctl.responseError( - c, - http.StatusInternalServerError, - err, - ) return } ctl.response( @@ -150,11 +152,21 @@ func (ctl *TransactionController) RevertTransaction(c *gin.Context) { l, _ := c.Get("ledger") err := l.(*ledger.Ledger).RevertTransaction(c.Request.Context(), c.Param("txid")) if err != nil { - ctl.responseError( - c, - http.StatusInternalServerError, - err, - ) + switch eerr := err.(type) { + case *storage.Error: + switch eerr.Code { + case storage.ConstraintFailed: + ctl.responseError(c, http.StatusConflict, err) + default: + ctl.responseError(c, http.StatusInternalServerError, err) + } + case *ledger.InsufficientFundError: + ctl.responseError(c, http.StatusBadRequest, err) + case *ledger.ValidationError: + ctl.responseError(c, http.StatusBadRequest, err) + default: + ctl.responseError(c, http.StatusInternalServerError, err) + } return } ctl.response( diff --git a/pkg/ledger/error.go b/pkg/ledger/error.go new file mode 100644 index 000000000..54f436bb8 --- /dev/null +++ b/pkg/ledger/error.go @@ -0,0 +1,29 @@ +package ledger + +import "fmt" + +type InsufficientFundError struct { + Asset string +} + +func (e InsufficientFundError) Error() string { + return fmt.Sprintf("balance.insufficient.%s", e.Asset) +} + +func NewInsufficientFundError(asset string) *InsufficientFundError { + return &InsufficientFundError{Asset: asset} +} + +type ValidationError struct { + Msg string +} + +func (v ValidationError) Error() string { + return v.Msg +} + +func NewValidationError(msg string) *ValidationError { + return &ValidationError{ + Msg: msg, + } +} \ No newline at end of file diff --git a/pkg/ledger/ledger.go b/pkg/ledger/ledger.go index 92334701b..c207cfc10 100644 --- a/pkg/ledger/ledger.go +++ b/pkg/ledger/ledger.go @@ -57,7 +57,7 @@ func (l *Ledger) Commit(ctx context.Context, ts []core.Transaction) ([]core.Tran for i := range ts { if len(ts[i].Postings) == 0 { - return ts, errors.New("transaction has no postings") + return ts, NewValidationError("transaction has no postings") } ts[i].ID = count + int64(i) @@ -67,6 +67,9 @@ func (l *Ledger) Commit(ctx context.Context, ts []core.Transaction) ([]core.Tran last = &ts[i] for _, p := range ts[i].Postings { + if p.Amount < 0 { + return ts, NewValidationError("negative amount") + } if _, ok := rf[p.Source]; !ok { rf[p.Source] = map[string]int64{} } @@ -101,7 +104,6 @@ func (l *Ledger) Commit(ctx context.Context, ts []core.Transaction) ([]core.Tran } balances, err := l.store.AggregateBalances(ctx, addr) - if err != nil { return ts, err } @@ -110,10 +112,7 @@ func (l *Ledger) Commit(ctx context.Context, ts []core.Transaction) ([]core.Tran balance, ok := balances[asset] if !ok || balance < checks[asset] { - return ts, fmt.Errorf( - "balance.insufficient.%s", - asset, - ) + return ts, NewInsufficientFundError(asset) } } } @@ -228,13 +227,13 @@ func (l *Ledger) SaveMeta(ctx context.Context, targetType string, targetID strin defer unlock() if targetType == "" { - return errors.New("empty target type") + return NewValidationError("empty target type") } if targetType != targetTypeTransaction && targetType != targetTypeAccount { - return fmt.Errorf("unknown target type '%s'", targetType) + return NewValidationError(fmt.Sprintf("unknown target type '%s'", targetType)) } if targetID == "" { - return errors.New("empty target id") + return NewValidationError("empty target id") } lastMetaID, err := l.store.LastMetaID(ctx)