diff --git a/pkg/api/apierrors/errors.go b/pkg/api/apierrors/errors.go index 82a91747d..ffaaca54f 100644 --- a/pkg/api/apierrors/errors.go +++ b/pkg/api/apierrors/errors.go @@ -19,6 +19,7 @@ import ( const ( ErrInternal = "INTERNAL" ErrConflict = "CONFLICT" + ErrTXID = "TXID_CONFLICT" ErrInsufficientFund = "INSUFFICIENT_FUND" ErrValidation = "VALIDATION" ErrContextCancelled = "CONTEXT_CANCELLED" @@ -64,6 +65,8 @@ func coreErrorToErrorCode(c *gin.Context, err error) (int, string, string) { ledger.IsScriptErrorWithCode(err, ErrScriptMetadataOverride): scriptErr := err.(*ledger.ScriptError) return http.StatusBadRequest, scriptErr.Code, EncodeLink(scriptErr.Message) + case ledger.IsTXIDError(err): + return http.StatusConflict, ErrTXID, "" case errors.Is(err, context.Canceled): return http.StatusInternalServerError, ErrContextCancelled, "" case storage.IsError(err): diff --git a/pkg/api/controllers/script_controller.go b/pkg/api/controllers/script_controller.go index 6598487ba..aec310113 100644 --- a/pkg/api/controllers/script_controller.go +++ b/pkg/api/controllers/script_controller.go @@ -48,6 +48,9 @@ func (ctl *ScriptController) PostScript(c *gin.Context) { case *ledger.ConflictError: code = apierrors.ErrConflict message = e.Error() + case *ledger.TXIDError: + code = apierrors.ErrTXID + message = e.Error() default: logging.FromContext(c.Request.Context()).Errorf( "internal errors executing script: %s", err) diff --git a/pkg/ledger/error.go b/pkg/ledger/error.go index af633e822..9f4e8a849 100644 --- a/pkg/ledger/error.go +++ b/pkg/ledger/error.go @@ -100,6 +100,25 @@ func IsConflictError(err error) bool { return errors.Is(err, &ConflictError{}) } +type TXIDError struct{} + +func (e TXIDError) Error() string { + return "conflict error on transaction id" +} + +func (e TXIDError) Is(err error) bool { + _, ok := err.(*TXIDError) + return ok +} + +func NewTXIDError() *TXIDError { + return &TXIDError{} +} + +func IsTXIDError(err error) bool { + return errors.Is(err, &TXIDError{}) +} + const ( ScriptErrorInsufficientFund = "INSUFFICIENT_FUND" ScriptErrorCompilationFailed = "COMPILATION_FAILED" diff --git a/pkg/ledger/execute_txsdata.go b/pkg/ledger/execute_txsdata.go index 1f0e218a4..ff0fe646f 100644 --- a/pkg/ledger/execute_txsdata.go +++ b/pkg/ledger/execute_txsdata.go @@ -162,6 +162,8 @@ func (l *Ledger) ExecuteTxsData(ctx context.Context, preview, checkBalances bool switch { case storage.IsErrorCode(err, storage.ConstraintFailed): return []core.ExpandedTransaction{}, NewConflictError() + case storage.IsErrorCode(err, storage.ConstraintTXID): + return []core.ExpandedTransaction{}, NewTXIDError() default: return []core.ExpandedTransaction{}, errors.Wrap(err, "committing transactions") diff --git a/pkg/storage/errors.go b/pkg/storage/errors.go index af07dfb74..698b7714b 100644 --- a/pkg/storage/errors.go +++ b/pkg/storage/errors.go @@ -13,6 +13,7 @@ type Code string const ( ConstraintFailed Code = "CONSTRAINT_FAILED" + ConstraintTXID Code = "CONSTRAINT_TXID" TooManyClient Code = "TOO_MANY_CLIENT" ) diff --git a/pkg/storage/sqlstorage/flavor.go b/pkg/storage/sqlstorage/flavor.go index 9e8c2da5d..cca392aef 100644 --- a/pkg/storage/sqlstorage/flavor.go +++ b/pkg/storage/sqlstorage/flavor.go @@ -56,6 +56,9 @@ func init() { if errors.As(err, &pgConnError) { switch pgConnError.Code { case "23505": + if pgConnError.ConstraintName == "transactions_id_key" { + return storage.NewError(storage.ConstraintTXID, err) + } return storage.NewError(storage.ConstraintFailed, err) case "53300": return storage.NewError(storage.TooManyClient, err)