diff --git a/actions/access_keys/count.go b/actions/access_keys/count.go index 885195ba..6c17ce2c 100644 --- a/actions/access_keys/count.go +++ b/actions/access_keys/count.go @@ -3,6 +3,7 @@ package accesskeys import ( "net/http" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/models/filter" "github.com/bitcoin-sv/spv-wallet/server/auth" @@ -26,7 +27,7 @@ func (a *Action) count(c *gin.Context) { var reqParams filter.CountAccessKeys if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -37,7 +38,7 @@ func (a *Action) count(c *gin.Context) { reqParams.Conditions.ToDbConditions(), ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/access_keys/create.go b/actions/access_keys/create.go index c572eab7..b7058641 100644 --- a/actions/access_keys/create.go +++ b/actions/access_keys/create.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/server/auth" "github.com/gin-gonic/gin" @@ -26,7 +27,7 @@ func (a *Action) create(c *gin.Context) { var requestBody CreateAccessKey if err := c.Bind(&requestBody); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -37,7 +38,7 @@ func (a *Action) create(c *gin.Context) { engine.WithMetadatas(requestBody.Metadata), ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/access_keys/get.go b/actions/access_keys/get.go index 656484a0..eac13765 100644 --- a/actions/access_keys/get.go +++ b/actions/access_keys/get.go @@ -3,7 +3,7 @@ package accesskeys import ( "net/http" - "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/server/auth" "github.com/gin-gonic/gin" @@ -27,7 +27,7 @@ func (a *Action) get(c *gin.Context) { id := c.Query("id") if id == "" { - c.JSON(http.StatusBadRequest, engine.ErrMissingFieldID) + spverrors.ErrorResponse(c, spverrors.ErrMissingFieldID, a.Services.Logger) return } @@ -36,12 +36,12 @@ func (a *Action) get(c *gin.Context) { c.Request.Context(), reqXPubID, id, ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } if accessKey.XpubID != reqXPubID { - c.JSON(http.StatusForbidden, "unauthorized") + spverrors.ErrorResponse(c, spverrors.ErrAuthorization, a.Services.Logger) return } diff --git a/actions/access_keys/revoke.go b/actions/access_keys/revoke.go index 7a5cbf30..5868bbbf 100644 --- a/actions/access_keys/revoke.go +++ b/actions/access_keys/revoke.go @@ -3,7 +3,7 @@ package accesskeys import ( "net/http" - "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/server/auth" "github.com/gin-gonic/gin" @@ -26,7 +26,7 @@ func (a *Action) revoke(c *gin.Context) { id := c.Query("id") if id == "" { - c.JSON(http.StatusBadRequest, engine.ErrMissingFieldID) + spverrors.ErrorResponse(c, spverrors.ErrMissingFieldID, a.Services.Logger) return } @@ -36,7 +36,7 @@ func (a *Action) revoke(c *gin.Context) { id, ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/access_keys/search.go b/actions/access_keys/search.go index 7fffdf68..c63345ad 100644 --- a/actions/access_keys/search.go +++ b/actions/access_keys/search.go @@ -3,6 +3,7 @@ package accesskeys import ( "net/http" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoin-sv/spv-wallet/models/filter" @@ -27,7 +28,7 @@ func (a *Action) search(c *gin.Context) { var reqParams filter.SearchAccessKeys if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -39,7 +40,7 @@ func (a *Action) search(c *gin.Context) { mappings.MapToQueryParams(reqParams.QueryParams), ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/admin/access_keys.go b/actions/admin/access_keys.go index 64f0d357..bff980ce 100644 --- a/actions/admin/access_keys.go +++ b/actions/admin/access_keys.go @@ -3,6 +3,7 @@ package admin import ( "net/http" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoin-sv/spv-wallet/models/filter" @@ -24,7 +25,7 @@ import ( func (a *Action) accessKeysSearch(c *gin.Context) { var reqParams filter.AdminSearchAccessKeys if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -35,7 +36,7 @@ func (a *Action) accessKeysSearch(c *gin.Context) { mappings.MapToQueryParams(reqParams.QueryParams), ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } @@ -62,7 +63,7 @@ func (a *Action) accessKeysSearch(c *gin.Context) { func (a *Action) accessKeysCount(c *gin.Context) { var reqParams filter.AdminCountAccessKeys if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -72,7 +73,7 @@ func (a *Action) accessKeysCount(c *gin.Context) { reqParams.Conditions.ToDbConditions(), ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/admin/contact.go b/actions/admin/contact.go index 775ea9f3..46daa005 100644 --- a/actions/admin/contact.go +++ b/actions/admin/contact.go @@ -1,11 +1,11 @@ package admin import ( - "errors" "net/http" "github.com/bitcoin-sv/spv-wallet/actions/common" "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoin-sv/spv-wallet/models/filter" @@ -27,13 +27,13 @@ import ( func (a *Action) contactsSearch(c *gin.Context) { var reqParams filter.SearchContacts if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } conditions, err := reqParams.Conditions.ToDbConditions() if err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -46,7 +46,7 @@ func (a *Action) contactsSearch(c *gin.Context) { mappings.MapToQueryParams(reqParams.QueryParams), ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } @@ -58,7 +58,7 @@ func (a *Action) contactsSearch(c *gin.Context) { conditions, ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } @@ -88,7 +88,7 @@ func (a *Action) contactsSearch(c *gin.Context) { func (a *Action) contactsUpdate(c *gin.Context) { var reqParams UpdateContact if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -101,7 +101,7 @@ func (a *Action) contactsUpdate(c *gin.Context) { &reqParams.Metadata, ) if err != nil { - handleErrors(err, c) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } @@ -133,7 +133,7 @@ func (a *Action) contactsDelete(c *gin.Context) { id, ) if err != nil { - handleErrors(err, c) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } @@ -164,7 +164,7 @@ func (a *Action) contactsReject(c *gin.Context) { engine.ContactRejected, ) if err != nil { - handleErrors(err, c) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } @@ -197,7 +197,7 @@ func (a *Action) contactsAccept(c *gin.Context) { engine.ContactNotConfirmed, ) if err != nil { - handleErrors(err, c) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } @@ -205,14 +205,3 @@ func (a *Action) contactsAccept(c *gin.Context) { c.JSON(http.StatusOK, contract) } - -func handleErrors(err error, c *gin.Context) { - switch { - case errors.Is(err, engine.ErrContactNotFound): - c.JSON(http.StatusNotFound, err.Error()) - case errors.Is(err, engine.ErrContactIncorrectStatus): - c.JSON(http.StatusUnprocessableEntity, err.Error()) - default: - c.JSON(http.StatusInternalServerError, err.Error()) - } -} diff --git a/actions/admin/destinations.go b/actions/admin/destinations.go index b4af0553..9b4499b3 100644 --- a/actions/admin/destinations.go +++ b/actions/admin/destinations.go @@ -3,6 +3,7 @@ package admin import ( "net/http" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoin-sv/spv-wallet/models/filter" @@ -24,7 +25,7 @@ import ( func (a *Action) destinationsSearch(c *gin.Context) { var reqParams filter.SearchDestinations if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -35,7 +36,7 @@ func (a *Action) destinationsSearch(c *gin.Context) { mappings.MapToQueryParams(reqParams.QueryParams), ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } @@ -61,7 +62,7 @@ func (a *Action) destinationsSearch(c *gin.Context) { func (a *Action) destinationsCount(c *gin.Context) { var reqParams filter.CountDestinations if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -71,7 +72,7 @@ func (a *Action) destinationsCount(c *gin.Context) { reqParams.Conditions.ToDbConditions(), ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/admin/paymail_addresses.go b/actions/admin/paymail_addresses.go index 33a08c26..2e0f2609 100644 --- a/actions/admin/paymail_addresses.go +++ b/actions/admin/paymail_addresses.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoin-sv/spv-wallet/models/filter" @@ -26,7 +27,7 @@ func (a *Action) paymailGetAddress(c *gin.Context) { var requestBody PaymailAddress if err := c.ShouldBindJSON(&requestBody); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -39,7 +40,7 @@ func (a *Action) paymailGetAddress(c *gin.Context) { paymailAddress, err := a.Services.SpvWalletEngine.GetPaymailAddress(c.Request.Context(), requestBody.Address, opts...) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } @@ -63,7 +64,7 @@ func (a *Action) paymailGetAddress(c *gin.Context) { func (a *Action) paymailAddressesSearch(c *gin.Context) { var reqParams filter.AdminSearchPaymails if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -74,7 +75,7 @@ func (a *Action) paymailAddressesSearch(c *gin.Context) { mappings.MapToQueryParams(reqParams.QueryParams), ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } @@ -101,7 +102,7 @@ func (a *Action) paymailAddressesSearch(c *gin.Context) { func (a *Action) paymailAddressesCount(c *gin.Context) { var reqParams filter.AdminCountPaymails if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -111,7 +112,7 @@ func (a *Action) paymailAddressesCount(c *gin.Context) { reqParams.Conditions.ToDbConditions(), ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } @@ -133,16 +134,16 @@ func (a *Action) paymailAddressesCount(c *gin.Context) { func (a *Action) paymailCreateAddress(c *gin.Context) { var requestBody CreatePaymail if err := c.ShouldBindJSON(&requestBody); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } if requestBody.Key == "" { - c.JSON(http.StatusExpectationFailed, "xpub is required") + spverrors.ErrorResponse(c, spverrors.ErrMissingFieldXpub, a.Services.Logger) return } if requestBody.Address == "" { - c.JSON(http.StatusBadRequest, "address is required") + spverrors.ErrorResponse(c, spverrors.ErrMissingAddress, a.Services.Logger) return } @@ -180,12 +181,12 @@ func (a *Action) paymailCreateAddress(c *gin.Context) { func (a *Action) paymailDeleteAddress(c *gin.Context) { var requestBody PaymailAddress if err := c.ShouldBindJSON(&requestBody); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } if requestBody.Address == "" { - c.JSON(http.StatusBadRequest, "address is required") + spverrors.ErrorResponse(c, spverrors.ErrMissingAddress, a.Services.Logger) return } @@ -194,7 +195,7 @@ func (a *Action) paymailDeleteAddress(c *gin.Context) { // Delete a new paymail address err := a.Services.SpvWalletEngine.DeletePaymailAddress(c.Request.Context(), requestBody.Address, opts...) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/admin/record.go b/actions/admin/record.go index 4f0df8e6..0bb36d73 100644 --- a/actions/admin/record.go +++ b/actions/admin/record.go @@ -6,6 +6,7 @@ import ( "github.com/bitcoin-sv/spv-wallet/engine" "github.com/bitcoin-sv/spv-wallet/engine/datastore" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/gin-gonic/gin" ) @@ -25,7 +26,7 @@ import ( func (a *Action) transactionRecord(c *gin.Context) { var requestBody RecordTransaction if err := c.ShouldBindJSON(&requestBody); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -42,11 +43,11 @@ func (a *Action) transactionRecord(c *gin.Context) { if errors.Is(err, datastore.ErrDuplicateKey) { // already registered, just return the registered transaction if transaction, err = a.Services.SpvWalletEngine.GetTransactionByHex(c.Request.Context(), requestBody.Hex); err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } } else { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } } diff --git a/actions/admin/stats.go b/actions/admin/stats.go index 9bd911bb..708700f1 100644 --- a/actions/admin/stats.go +++ b/actions/admin/stats.go @@ -3,6 +3,7 @@ package admin import ( "net/http" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/gin-gonic/gin" ) @@ -20,7 +21,7 @@ import ( func (a *Action) stats(c *gin.Context) { stats, err := a.Services.SpvWalletEngine.GetStats(c.Request.Context()) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/admin/transactions.go b/actions/admin/transactions.go index 2c4991d8..74e83f70 100644 --- a/actions/admin/transactions.go +++ b/actions/admin/transactions.go @@ -3,6 +3,7 @@ package admin import ( "net/http" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoin-sv/spv-wallet/models/filter" @@ -24,7 +25,7 @@ import ( func (a *Action) transactionsSearch(c *gin.Context) { var reqParams filter.SearchTransactions if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -35,7 +36,7 @@ func (a *Action) transactionsSearch(c *gin.Context) { mappings.MapToQueryParams(reqParams.QueryParams), ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } @@ -62,7 +63,7 @@ func (a *Action) transactionsSearch(c *gin.Context) { func (a *Action) transactionsCount(c *gin.Context) { var reqParams filter.CountTransactions if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -72,7 +73,7 @@ func (a *Action) transactionsCount(c *gin.Context) { reqParams.Conditions.ToDbConditions(), ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/admin/utxos.go b/actions/admin/utxos.go index ff9f51b4..98b0b7b6 100644 --- a/actions/admin/utxos.go +++ b/actions/admin/utxos.go @@ -3,6 +3,7 @@ package admin import ( "net/http" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/models/filter" "github.com/gin-gonic/gin" @@ -23,13 +24,13 @@ import ( func (a *Action) utxosSearch(c *gin.Context) { var reqParams filter.AdminSearchUtxos if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } conditions, err := reqParams.Conditions.ToDbConditions() if err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrInvalidConditions, a.Services.Logger) return } @@ -40,7 +41,7 @@ func (a *Action) utxosSearch(c *gin.Context) { mappings.MapToQueryParams(reqParams.QueryParams), ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } @@ -62,13 +63,13 @@ func (a *Action) utxosSearch(c *gin.Context) { func (a *Action) utxosCount(c *gin.Context) { var reqParams filter.AdminCountUtxos if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } conditions, err := reqParams.Conditions.ToDbConditions() if err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrInvalidConditions, a.Services.Logger) return } @@ -78,7 +79,7 @@ func (a *Action) utxosCount(c *gin.Context) { conditions, ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/admin/xpubs.go b/actions/admin/xpubs.go index 9da023bb..efc04ccc 100644 --- a/actions/admin/xpubs.go +++ b/actions/admin/xpubs.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoin-sv/spv-wallet/models/filter" @@ -25,7 +26,7 @@ import ( func (a *Action) xpubsCreate(c *gin.Context) { var requestBody CreateXpub if err := c.Bind(&requestBody); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -34,7 +35,7 @@ func (a *Action) xpubsCreate(c *gin.Context) { engine.WithMetadatas(requestBody.Metadata), ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } @@ -57,7 +58,7 @@ func (a *Action) xpubsCreate(c *gin.Context) { func (a *Action) xpubsSearch(c *gin.Context) { var reqParams filter.SearchXpubs if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -68,7 +69,7 @@ func (a *Action) xpubsSearch(c *gin.Context) { mappings.MapToQueryParams(reqParams.QueryParams), ) if err != nil { - c.JSON(http.StatusExpectationFailed, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } @@ -95,7 +96,7 @@ func (a *Action) xpubsSearch(c *gin.Context) { func (a *Action) xpubsCount(c *gin.Context) { var reqParams filter.CountXpubs if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -105,7 +106,7 @@ func (a *Action) xpubsCount(c *gin.Context) { reqParams.Conditions.ToDbConditions(), ) if err != nil { - c.JSON(http.StatusExpectationFailed, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/contacts/accept.go b/actions/contacts/accept.go index c7e86040..289575e5 100644 --- a/actions/contacts/accept.go +++ b/actions/contacts/accept.go @@ -1,10 +1,9 @@ package contacts import ( - "errors" "net/http" - "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/server/auth" "github.com/gin-gonic/gin" ) @@ -29,14 +28,7 @@ func (a *Action) accept(c *gin.Context) { err := a.Services.SpvWalletEngine.AcceptContact(c, reqXPubID, paymail) if err != nil { - switch { - case errors.Is(err, engine.ErrContactNotFound): - c.JSON(http.StatusNotFound, err.Error()) - case errors.Is(err, engine.ErrContactIncorrectStatus): - c.JSON(http.StatusUnprocessableEntity, err.Error()) - default: - c.JSON(http.StatusInternalServerError, err.Error()) - } + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/contacts/confirm.go b/actions/contacts/confirm.go index 203e1e6b..96a833ba 100644 --- a/actions/contacts/confirm.go +++ b/actions/contacts/confirm.go @@ -1,10 +1,9 @@ package contacts import ( - "errors" "net/http" - "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/server/auth" "github.com/gin-gonic/gin" ) @@ -29,14 +28,7 @@ func (a *Action) confirm(c *gin.Context) { err := a.Services.SpvWalletEngine.ConfirmContact(c, reqXPubID, paymail) if err != nil { - switch { - case errors.Is(err, engine.ErrContactNotFound): - c.JSON(http.StatusNotFound, err.Error()) - case errors.Is(err, engine.ErrContactIncorrectStatus): - c.JSON(http.StatusUnprocessableEntity, err.Error()) - default: - c.JSON(http.StatusInternalServerError, err.Error()) - } + spverrors.ErrorResponse(c, err, a.Services.Logger) return } c.Status(http.StatusOK) diff --git a/actions/contacts/models.go b/actions/contacts/models.go index 60c5b414..c772f2c6 100644 --- a/actions/contacts/models.go +++ b/actions/contacts/models.go @@ -1,9 +1,8 @@ package contacts import ( - "errors" - "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" ) // UpsertContact is the model for creating a contact @@ -18,7 +17,7 @@ type UpsertContact struct { func (p *UpsertContact) validate() error { if p.FullName == "" { - return errors.New("fullName is required") + return spverrors.ErrMissingContactFullName } return nil diff --git a/actions/contacts/reject.go b/actions/contacts/reject.go index 25399e40..2dfd9749 100644 --- a/actions/contacts/reject.go +++ b/actions/contacts/reject.go @@ -1,10 +1,9 @@ package contacts import ( - "errors" "net/http" - "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/server/auth" "github.com/gin-gonic/gin" ) @@ -29,14 +28,7 @@ func (a *Action) reject(c *gin.Context) { err := a.Services.SpvWalletEngine.RejectContact(c, reqXPubID, paymail) if err != nil { - switch { - case errors.Is(err, engine.ErrContactNotFound): - c.JSON(http.StatusNotFound, err.Error()) - case errors.Is(err, engine.ErrContactIncorrectStatus): - c.JSON(http.StatusUnprocessableEntity, err.Error()) - default: - c.JSON(http.StatusInternalServerError, err.Error()) - } + spverrors.ErrorResponse(c, err, a.Services.Logger) return } c.Status(http.StatusOK) diff --git a/actions/contacts/search.go b/actions/contacts/search.go index 6f2a1769..fe9a23f0 100644 --- a/actions/contacts/search.go +++ b/actions/contacts/search.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/bitcoin-sv/spv-wallet/actions/common" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoin-sv/spv-wallet/models/filter" @@ -28,13 +29,13 @@ func (a *Action) search(c *gin.Context) { var reqParams filter.SearchContacts if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } conditions, err := reqParams.Conditions.ToDbConditions() if err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrInvalidConditions, a.Services.Logger) return } @@ -48,7 +49,7 @@ func (a *Action) search(c *gin.Context) { mappings.MapToQueryParams(reqParams.QueryParams), ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } @@ -61,7 +62,7 @@ func (a *Action) search(c *gin.Context) { conditions, ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/contacts/unconfirm.go b/actions/contacts/unconfirm.go index 031bcb20..6c722d35 100644 --- a/actions/contacts/unconfirm.go +++ b/actions/contacts/unconfirm.go @@ -1,10 +1,9 @@ package contacts import ( - "errors" "net/http" - "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/server/auth" "github.com/gin-gonic/gin" ) @@ -29,14 +28,7 @@ func (a *Action) unconfirm(c *gin.Context) { err := a.Services.SpvWalletEngine.UnconfirmContact(c, reqXPubID, paymail) if err != nil { - switch { - case errors.Is(err, engine.ErrContactNotFound): - c.JSON(http.StatusNotFound, err.Error()) - case errors.Is(err, engine.ErrContactIncorrectStatus): - c.JSON(http.StatusUnprocessableEntity, err.Error()) - default: - c.JSON(http.StatusInternalServerError, err.Error()) - } + spverrors.ErrorResponse(c, err, a.Services.Logger) return } c.Status(http.StatusOK) diff --git a/actions/contacts/upsert.go b/actions/contacts/upsert.go index 101e3c2b..a5bcba20 100644 --- a/actions/contacts/upsert.go +++ b/actions/contacts/upsert.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoin-sv/spv-wallet/server/auth" @@ -29,12 +30,12 @@ func (a *Action) upsert(c *gin.Context) { var req UpsertContact if err := json.NewDecoder(c.Request.Body).Decode(&req); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } if err := req.validate(); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } @@ -44,8 +45,8 @@ func (a *Action) upsert(c *gin.Context) { reqXPubID, req.RequesterPaymail, engine.WithMetadatas(req.Metadata)) - if err != nil && !errors.Is(err, engine.ErrAddingContactRequest) { - c.JSON(http.StatusInternalServerError, err.Error()) + if err != nil && !errors.Is(err, spverrors.ErrAddingContactRequest) { + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/destinations/count.go b/actions/destinations/count.go index 9455bb71..c6115a5f 100644 --- a/actions/destinations/count.go +++ b/actions/destinations/count.go @@ -3,6 +3,7 @@ package destinations import ( "net/http" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/models/filter" "github.com/bitcoin-sv/spv-wallet/server/auth" @@ -26,7 +27,7 @@ func (a *Action) count(c *gin.Context) { var reqParams filter.CountDestinations if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -37,7 +38,7 @@ func (a *Action) count(c *gin.Context) { reqParams.Conditions.ToDbConditions(), ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/destinations/create.go b/actions/destinations/create.go index bb1f2450..c61d160b 100644 --- a/actions/destinations/create.go +++ b/actions/destinations/create.go @@ -3,8 +3,8 @@ package destinations import ( "net/http" - "github.com/bitcoin-sv/spv-wallet/actions" "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/server/auth" @@ -27,16 +27,16 @@ func (a *Action) create(c *gin.Context) { reqXPub := c.GetString(auth.ParamXPubKey) xPub, err := a.Services.SpvWalletEngine.GetXpub(c.Request.Context(), reqXPub) if err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } else if xPub == nil { - c.JSON(http.StatusBadRequest, actions.ErrXpubNotFound) + spverrors.ErrorResponse(c, spverrors.ErrCouldNotFindXpub, a.Services.Logger) return } var requestBody CreateDestination if err = c.Bind(&requestBody); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -54,7 +54,7 @@ func (a *Action) create(c *gin.Context) { utils.ScriptTypePubKeyHash, opts..., ); err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/destinations/get.go b/actions/destinations/get.go index 0df8eaf4..1e889214 100644 --- a/actions/destinations/get.go +++ b/actions/destinations/get.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/server/auth" "github.com/gin-gonic/gin" @@ -30,7 +31,7 @@ func (a *Action) get(c *gin.Context) { address := c.Query("address") lockingScript := c.Query("locking_script") if id == "" && address == "" && lockingScript == "" { - c.JSON(http.StatusBadRequest, engine.ErrMissingFieldID) + spverrors.ErrorResponse(c, spverrors.ErrOneOfTheFieldsIsRequired, a.Services.Logger) return } @@ -50,7 +51,7 @@ func (a *Action) get(c *gin.Context) { ) } if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/destinations/search.go b/actions/destinations/search.go index 12f6dabd..8296038d 100644 --- a/actions/destinations/search.go +++ b/actions/destinations/search.go @@ -3,6 +3,7 @@ package destinations import ( "net/http" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoin-sv/spv-wallet/models/filter" @@ -27,7 +28,7 @@ func (a *Action) search(c *gin.Context) { var reqParams filter.SearchDestinations if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -39,7 +40,7 @@ func (a *Action) search(c *gin.Context) { mappings.MapToQueryParams(reqParams.QueryParams), ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/destinations/update.go b/actions/destinations/update.go index cd458c8e..e6ec1dd1 100644 --- a/actions/destinations/update.go +++ b/actions/destinations/update.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/server/auth" "github.com/gin-gonic/gin" @@ -26,11 +27,11 @@ func (a *Action) update(c *gin.Context) { var requestBody UpdateDestination if err := c.Bind(&requestBody); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } if requestBody.ID == "" && requestBody.Address == "" && requestBody.LockingScript == "" { - c.JSON(http.StatusBadRequest, "One of the fields is required: id, address or lockingScript") + spverrors.ErrorResponse(c, spverrors.ErrOneOfTheFieldsIsRequired, a.Services.Logger) return } @@ -51,7 +52,7 @@ func (a *Action) update(c *gin.Context) { ) } if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/errors.go b/actions/errors.go deleted file mode 100644 index 8788fd83..00000000 --- a/actions/errors.go +++ /dev/null @@ -1,12 +0,0 @@ -package actions - -import "errors" - -// ErrXpubNotFound is when the xpub is not found (in Auth Header) -var ErrXpubNotFound = errors.New("xpub not found") - -// ErrTxConfigNotFound is when the transaction config is not found in request body -var ErrTxConfigNotFound = errors.New("transaction config not found") - -// ErrBadTxConfig is when the transaction config specified is not valid -var ErrBadTxConfig = errors.New("bad transaction config supplied") diff --git a/actions/transactions/broadcast_callback.go b/actions/transactions/broadcast_callback.go index ecc90d91..298bd4e3 100644 --- a/actions/transactions/broadcast_callback.go +++ b/actions/transactions/broadcast_callback.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/bitcoin-sv/go-broadcast-client/broadcast" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/gin-gonic/gin" ) @@ -22,14 +23,14 @@ func (a *Action) broadcastCallback(c *gin.Context) { err := c.Bind(&resp) if err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } err = a.Services.SpvWalletEngine.UpdateTransaction(c.Request.Context(), resp) if err != nil { a.Services.Logger.Err(err).Msgf("failed to update transaction - tx: %v", resp) - c.Status(http.StatusInternalServerError) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/transactions/count.go b/actions/transactions/count.go index a4a4dcb5..9b247885 100644 --- a/actions/transactions/count.go +++ b/actions/transactions/count.go @@ -3,6 +3,7 @@ package transactions import ( "net/http" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/models/filter" "github.com/bitcoin-sv/spv-wallet/server/auth" @@ -26,7 +27,7 @@ func (a *Action) count(c *gin.Context) { var reqParams filter.CountTransactions if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -37,7 +38,7 @@ func (a *Action) count(c *gin.Context) { reqParams.Conditions.ToDbConditions(), ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/transactions/get.go b/actions/transactions/get.go index c6833f16..a2946847 100644 --- a/actions/transactions/get.go +++ b/actions/transactions/get.go @@ -3,6 +3,7 @@ package transactions import ( "net/http" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/server/auth" "github.com/gin-gonic/gin" @@ -30,13 +31,13 @@ func (a *Action) get(c *gin.Context) { id, ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } else if transaction == nil { - c.JSON(http.StatusBadRequest, "not found") + spverrors.ErrorResponse(c, spverrors.ErrCouldNotFindTransaction, a.Services.Logger) return } else if !transaction.IsXpubIDAssociated(reqXPubID) { - c.JSON(http.StatusBadRequest, "unauthorized") + spverrors.ErrorResponse(c, spverrors.ErrAuthorization, a.Services.Logger) return } diff --git a/actions/transactions/new.go b/actions/transactions/new.go index 97f70b38..286ded7d 100644 --- a/actions/transactions/new.go +++ b/actions/transactions/new.go @@ -3,8 +3,8 @@ package transactions import ( "net/http" - "github.com/bitcoin-sv/spv-wallet/actions" "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/server/auth" "github.com/gin-gonic/gin" @@ -27,16 +27,16 @@ func (a *Action) newTransaction(c *gin.Context) { xPub, err := a.Services.SpvWalletEngine.GetXpub(c.Request.Context(), reqXPub) if err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } else if xPub == nil { - c.JSON(http.StatusBadRequest, actions.ErrXpubNotFound.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCouldNotFindXpub, a.Services.Logger) return } var requestBody NewTransaction if err = c.Bind(&requestBody); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -54,7 +54,7 @@ func (a *Action) newTransaction(c *gin.Context) { txConfig, opts..., ); err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/transactions/record.go b/actions/transactions/record.go index ede05b21..85c34cc1 100644 --- a/actions/transactions/record.go +++ b/actions/transactions/record.go @@ -3,8 +3,8 @@ package transactions import ( "net/http" - "github.com/bitcoin-sv/spv-wallet/actions" "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/server/auth" "github.com/gin-gonic/gin" @@ -27,16 +27,16 @@ func (a *Action) record(c *gin.Context) { var requestBody RecordTransaction if err := c.ShouldBindJSON(&requestBody); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } xPub, err := a.Services.SpvWalletEngine.GetXpub(c.Request.Context(), reqXPub) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } else if xPub == nil { - c.JSON(http.StatusBadRequest, actions.ErrXpubNotFound.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCouldNotFindXpub, a.Services.Logger) return } @@ -54,7 +54,7 @@ func (a *Action) record(c *gin.Context) { requestBody.ReferenceID, opts..., ); err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/transactions/search.go b/actions/transactions/search.go index 85702e14..7e1c2970 100644 --- a/actions/transactions/search.go +++ b/actions/transactions/search.go @@ -3,6 +3,7 @@ package transactions import ( "net/http" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoin-sv/spv-wallet/models/filter" @@ -27,7 +28,7 @@ func (a *Action) search(c *gin.Context) { var reqParams filter.SearchTransactions if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCouldNotFindXpub, a.Services.Logger) return } @@ -40,7 +41,7 @@ func (a *Action) search(c *gin.Context) { mappings.MapToQueryParams(reqParams.QueryParams), ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/transactions/update.go b/actions/transactions/update.go index 1e9f690c..bef91f81 100644 --- a/actions/transactions/update.go +++ b/actions/transactions/update.go @@ -3,6 +3,7 @@ package transactions import ( "net/http" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/server/auth" "github.com/gin-gonic/gin" @@ -25,7 +26,7 @@ func (a *Action) update(c *gin.Context) { var requestBody UpdateTransaction if err := c.Bind(&requestBody); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -37,12 +38,12 @@ func (a *Action) update(c *gin.Context) { requestBody.Metadata, ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } else if transaction == nil { - c.JSON(http.StatusBadRequest, "not found") + spverrors.ErrorResponse(c, spverrors.ErrCouldNotFindTransaction, a.Services.Logger) } else if !transaction.IsXpubIDAssociated(reqXPubID) { - c.JSON(http.StatusBadRequest, "unauthorized") + spverrors.ErrorResponse(c, spverrors.ErrAuthorization, a.Services.Logger) return } diff --git a/actions/utxos/count.go b/actions/utxos/count.go index f2a68a28..5d06450c 100644 --- a/actions/utxos/count.go +++ b/actions/utxos/count.go @@ -3,6 +3,7 @@ package utxos import ( "net/http" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/models/filter" "github.com/bitcoin-sv/spv-wallet/server/auth" @@ -26,13 +27,13 @@ func (a *Action) count(c *gin.Context) { var reqParams filter.CountUtxos if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } conditions, err := reqParams.Conditions.ToDbConditions() if err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrInvalidConditions, a.Services.Logger) return } @@ -49,7 +50,7 @@ func (a *Action) count(c *gin.Context) { mappings.MapToMetadata(reqParams.Metadata), dbConditions, ); err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/utxos/get.go b/actions/utxos/get.go index bb270e82..e3ea26ac 100644 --- a/actions/utxos/get.go +++ b/actions/utxos/get.go @@ -4,6 +4,7 @@ import ( "net/http" "strconv" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/server/auth" "github.com/gin-gonic/gin" @@ -28,7 +29,7 @@ func (a *Action) get(c *gin.Context) { outputIndex := c.Query("output_index") outputIndex64, err := strconv.ParseUint(outputIndex, 10, 32) if err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } @@ -39,7 +40,7 @@ func (a *Action) get(c *gin.Context) { uint32(outputIndex64), ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/utxos/search.go b/actions/utxos/search.go index 4ff632d5..34662537 100644 --- a/actions/utxos/search.go +++ b/actions/utxos/search.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoin-sv/spv-wallet/models/filter" @@ -28,7 +29,7 @@ func (a *Action) search(c *gin.Context) { var reqParams filter.SearchUtxos if err := c.Bind(&reqParams); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -46,7 +47,7 @@ func (a *Action) search(c *gin.Context) { conditions, mappings.MapToQueryParams(reqParams.QueryParams), ); err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/xpubs/get.go b/actions/xpubs/get.go index 86302bd1..d6ff75b7 100644 --- a/actions/xpubs/get.go +++ b/actions/xpubs/get.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/server/auth" "github.com/gin-gonic/gin" @@ -35,7 +36,7 @@ func (a *Action) get(c *gin.Context) { ) } if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCouldNotFindXpub, a.Services.Logger) return } diff --git a/engine/action_access_key.go b/engine/action_access_key.go index 26a54c15..8797ace0 100644 --- a/engine/action_access_key.go +++ b/engine/action_access_key.go @@ -5,6 +5,7 @@ import ( "time" "github.com/bitcoin-sv/spv-wallet/engine/datastore" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" ) @@ -29,7 +30,7 @@ func (c *Client) NewAccessKey(ctx context.Context, rawXpubKey string, opts ...Mo ); err != nil { return nil, err } else if xPub == nil { - return nil, ErrMissingXpub + return nil, spverrors.ErrCouldNotFindXpub } // Create the model & set the default options (gives options from client->model) @@ -59,12 +60,12 @@ func (c *Client) GetAccessKey(ctx context.Context, xPubID, id string) (*AccessKe if err != nil { return nil, err } else if accessKey == nil { - return nil, ErrAccessKeyNotFound + return nil, spverrors.ErrCouldNotFindAccessKey } // make sure this is the correct accessKey if accessKey.XpubID != xPubID { - return nil, utils.ErrXpubNoMatch + return nil, spverrors.ErrXpubNoMatch } // Return the model @@ -181,7 +182,7 @@ func (c *Client) RevokeAccessKey(ctx context.Context, rawXpubKey, id string, opt ); err != nil { return nil, err } else if xPub == nil { - return nil, ErrMissingXpub + return nil, spverrors.ErrCouldNotFindXpub } var accessKey *AccessKey @@ -191,13 +192,13 @@ func (c *Client) RevokeAccessKey(ctx context.Context, rawXpubKey, id string, opt return nil, err } if accessKey == nil { - return nil, ErrMissingAccessKey + return nil, spverrors.ErrCouldNotFindAccessKey } // make sure this is the correct accessKey xPubID := utils.Hash(rawXpubKey) if accessKey.XpubID != xPubID { - return nil, utils.ErrXpubNoMatch + return nil, spverrors.ErrXpubNoMatch } accessKey.RevokedAt.Valid = true diff --git a/engine/action_contact.go b/engine/action_contact.go index c19cb85f..de6360a4 100644 --- a/engine/action_contact.go +++ b/engine/action_contact.go @@ -2,19 +2,11 @@ package engine import ( "context" - "errors" "fmt" "github.com/bitcoin-sv/go-paymail" "github.com/bitcoin-sv/spv-wallet/engine/datastore" -) - -var ( - ErrInvalidRequesterXpub = errors.New("invalid requester xpub") - ErrAddingContactRequest = errors.New("adding contact request failed") - ErrMoreThanOnePaymailRegistered = errors.New("there are more than one paymail assigned to the xpub") - ErrContactNotFound = errors.New("contact not found") - ErrContactIncorrectStatus = errors.New("contact is in incorrect status to proceed") + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" ) func (c *Client) UpsertContact(ctx context.Context, ctcFName, ctcPaymail, requesterXPubID, requesterPaymail string, opts ...ModelOps) (*Contact, error) { @@ -48,7 +40,7 @@ func (c *Client) UpsertContact(ctx context.Context, ctcFName, ctcPaymail, reques Str("requestedContact", ctcPaymail). Msgf("adding contact request failed: %s", err.Error()) - return contact, ErrAddingContactRequest + return contact, spverrors.ErrAddingContactRequest } return contact, nil @@ -62,12 +54,14 @@ func (c *Client) AddContactRequest(ctx context.Context, fullName, paymailAdress, contactPm, err := pmSrvnt.GetSanitizedPaymail(paymailAdress) if err != nil { - return nil, fmt.Errorf("requested contact paymail is invalid. Reason: %w", err) + c.Logger().Error().Msgf("requested contact paymail is invalid. Reason: %s", err.Error()) + return nil, spverrors.ErrRequestedContactInvalid } contactPki, err := pmSrvnt.GetPkiForPaymail(ctx, contactPm) if err != nil { - return nil, fmt.Errorf("geting PKI for %s failed. Reason: %w", paymailAdress, err) + c.Logger().Error().Msgf("getting PKI for %s failed. Reason: %v", paymailAdress, err) + return nil, spverrors.ErrGettingPKIFailed } // check if exists already @@ -94,7 +88,8 @@ func (c *Client) AddContactRequest(ctx context.Context, fullName, paymailAdress, if save { if err = contact.Save(ctx); err != nil { - return nil, fmt.Errorf("adding %s contact failed. Reason: %w", paymailAdress, err) + c.Logger().Error().Msgf("adding %s contact failed. Reason: %v", paymailAdress, err) + return nil, spverrors.ErrSaveContact } } @@ -146,12 +141,12 @@ func (c *Client) GetContactsCount(ctx context.Context, metadata *Metadata, condi func (c *Client) UpdateContact(ctx context.Context, id, fullName string, metadata *Metadata) (*Contact, error) { contact, err := getContactByID(ctx, id, c.DefaultModelOptions()...) if err != nil { - c.logContactError("", "", fmt.Sprintf("error while geting contact: %s", err.Error())) + c.logContactError("", "", fmt.Sprintf("error while getting contact: %s", err.Error())) return nil, err } if contact == nil { - return nil, ErrContactNotFound + return nil, spverrors.ErrContactNotFound } contact.FullName = fullName @@ -159,7 +154,7 @@ func (c *Client) UpdateContact(ctx context.Context, id, fullName string, metadat if err = contact.Save(ctx); err != nil { c.logContactError(contact.OwnerXpubID, contact.Paymail, fmt.Sprintf("unexpected error while saving contact: %s", err.Error())) - return nil, err + return nil, spverrors.ErrSaveContact } return contact, nil @@ -168,12 +163,12 @@ func (c *Client) UpdateContact(ctx context.Context, id, fullName string, metadat func (c *Client) AdminChangeContactStatus(ctx context.Context, id string, status ContactStatus) (*Contact, error) { contact, err := getContactByID(ctx, id, c.DefaultModelOptions()...) if err != nil { - c.logContactError("", "", fmt.Sprintf("error while geting contact: %s", err.Error())) + c.logContactError("", "", fmt.Sprintf("error while getting contact: %s", err.Error())) return nil, err } if contact == nil { - return nil, ErrContactNotFound + return nil, spverrors.ErrContactNotFound } switch status { @@ -192,7 +187,7 @@ func (c *Client) AdminChangeContactStatus(ctx context.Context, id string, status if err = contact.Save(ctx); err != nil { c.logContactError(contact.OwnerXpubID, contact.Paymail, fmt.Sprintf("unexpected error while saving contact: %s", err.Error())) - return nil, err + return nil, spverrors.ErrSaveContact } return contact, nil } @@ -200,19 +195,19 @@ func (c *Client) AdminChangeContactStatus(ctx context.Context, id string, status func (c *Client) DeleteContact(ctx context.Context, id string) error { contact, err := getContactByID(ctx, id, c.DefaultModelOptions()...) if err != nil { - c.logContactError("", "", fmt.Sprintf("error while geting contact: %s", err.Error())) + c.logContactError("", "", fmt.Sprintf("error while getting contact: %s", err.Error())) return err } if contact == nil { - return ErrContactNotFound + return spverrors.ErrContactNotFound } contact.Delete() if err = contact.Save(ctx); err != nil { c.logContactError(contact.OwnerXpubID, contact.Paymail, fmt.Sprintf("unexpected error while saving contact: %s", err.Error())) - return err + return spverrors.ErrSaveContact } return nil @@ -221,21 +216,21 @@ func (c *Client) DeleteContact(ctx context.Context, id string) error { func (c *Client) AcceptContact(ctx context.Context, xPubID, paymail string) error { contact, err := getContact(ctx, paymail, xPubID, c.DefaultModelOptions()...) if err != nil { - c.logContactError(xPubID, paymail, fmt.Sprintf("unexpected error while geting contact: %s", err.Error())) + c.logContactError(xPubID, paymail, fmt.Sprintf("unexpected error while getting contact: %s", err.Error())) return err } if contact == nil { - return ErrContactNotFound + return spverrors.ErrContactNotFound } if err = contact.Accept(); err != nil { c.logContactWarining(xPubID, paymail, err.Error()) - return ErrContactIncorrectStatus + return spverrors.ErrContactIncorrectStatus } if err = contact.Save(ctx); err != nil { c.logContactError(xPubID, paymail, fmt.Sprintf("unexpected error while saving contact: %s", err.Error())) - return err + return spverrors.ErrSaveContact } return nil @@ -244,21 +239,21 @@ func (c *Client) AcceptContact(ctx context.Context, xPubID, paymail string) erro func (c *Client) RejectContact(ctx context.Context, xPubID, paymail string) error { contact, err := getContact(ctx, paymail, xPubID, c.DefaultModelOptions()...) if err != nil { - c.logContactError(xPubID, paymail, fmt.Sprintf("unexpected error while geting contact: %s", err.Error())) + c.logContactError(xPubID, paymail, fmt.Sprintf("unexpected error while getting contact: %s", err.Error())) return err } if contact == nil { - return ErrContactNotFound + return spverrors.ErrContactNotFound } if err = contact.Reject(); err != nil { c.logContactWarining(xPubID, paymail, err.Error()) - return ErrContactIncorrectStatus + return spverrors.ErrContactIncorrectStatus } if err = contact.Save(ctx); err != nil { c.logContactError(xPubID, paymail, fmt.Sprintf("unexpected error while saving contact: %s", err.Error())) - return err + return spverrors.ErrSaveContact } return nil @@ -267,21 +262,21 @@ func (c *Client) RejectContact(ctx context.Context, xPubID, paymail string) erro func (c *Client) ConfirmContact(ctx context.Context, xPubID, paymail string) error { contact, err := getContact(ctx, paymail, xPubID, c.DefaultModelOptions()...) if err != nil { - c.logContactError(xPubID, paymail, fmt.Sprintf("unexpected error while geting contact: %s", err.Error())) + c.logContactError(xPubID, paymail, fmt.Sprintf("unexpected error while getting contact: %s", err.Error())) return err } if contact == nil { - return ErrContactNotFound + return spverrors.ErrContactNotFound } if err = contact.Confirm(); err != nil { c.logContactWarining(xPubID, paymail, err.Error()) - return ErrContactIncorrectStatus + return spverrors.ErrContactIncorrectStatus } if err = contact.Save(ctx); err != nil { c.logContactError(xPubID, paymail, fmt.Sprintf("unexpected error while saving contact: %s", err.Error())) - return err + return spverrors.ErrSaveContact } return nil @@ -290,21 +285,21 @@ func (c *Client) ConfirmContact(ctx context.Context, xPubID, paymail string) err func (c *Client) UnconfirmContact(ctx context.Context, xPubID, paymail string) error { contact, err := getContact(ctx, paymail, xPubID, c.DefaultModelOptions()...) if err != nil { - c.logContactError(xPubID, paymail, fmt.Sprintf("unexpected error while geting contact: %s", err.Error())) + c.logContactError(xPubID, paymail, fmt.Sprintf("unexpected error while getting contact: %s", err.Error())) return err } if contact == nil { - return ErrContactNotFound + return spverrors.ErrContactNotFound } if err = contact.Unconfirm(); err != nil { c.logContactWarining(xPubID, paymail, err.Error()) - return ErrContactIncorrectStatus + return spverrors.ErrContactIncorrectStatus } if err = contact.Save(ctx); err != nil { c.logContactError(xPubID, paymail, fmt.Sprintf("unexpected error while saving contact: %s", err.Error())) - return err + return spverrors.ErrSaveContact } return nil @@ -318,7 +313,7 @@ func (c *Client) getPaymail(ctx context.Context, xpubID, paymailAddr string) (*P } if res == nil || res.XpubID != xpubID { - return nil, ErrInvalidRequesterXpub + return nil, spverrors.ErrInvalidRequesterXpub } return res, nil @@ -331,9 +326,9 @@ func (c *Client) getPaymail(ctx context.Context, xpubID, paymailAddr string) (*P return nil, err } if len(paymails) == 0 { - return nil, ErrInvalidRequesterXpub + return nil, spverrors.ErrInvalidRequesterXpub } else if len(paymails) > 1 { - return nil, ErrMoreThanOnePaymailRegistered + return nil, spverrors.ErrMoreThanOnePaymailRegistered } return paymails[0], nil @@ -342,7 +337,7 @@ func (c *Client) getPaymail(ctx context.Context, xpubID, paymailAddr string) (*P func (c *Client) upsertContact(ctx context.Context, pmSrvnt *PaymailServant, reqXPubID, ctcFName string, ctcPaymail *paymail.SanitisedPaymail, opts ...ModelOps) (*Contact, error) { contactPki, err := pmSrvnt.GetPkiForPaymail(ctx, ctcPaymail) if err != nil { - return nil, fmt.Errorf("geting PKI for %s failed. Reason: %w", ctcPaymail.Address, err) + return nil, spverrors.ErrGettingPKIFailed } // check if exists already @@ -368,7 +363,7 @@ func (c *Client) upsertContact(ctx context.Context, pmSrvnt *PaymailServant, req } if err = contact.Save(ctx); err != nil { - return nil, fmt.Errorf("adding %s contact failed. Reason: %w", ctcPaymail, err) + return nil, spverrors.ErrSaveContact } return contact, nil diff --git a/engine/action_contact_test.go b/engine/action_contact_test.go index c3c22a51..6c37e79b 100644 --- a/engine/action_contact_test.go +++ b/engine/action_contact_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/stretchr/testify/require" ) @@ -74,13 +75,13 @@ func TestAcceptContactErrorPath(t *testing.T) { testCases := []testCase{ { testID: 1, - name: "non existance contact, should return \"contact not found\" error", + name: "non existence contact, should return \"contact not found\" error", data: testCaseData{ xPub: xPubForNotFoundContact, paymail: paymailGeneric, contactStatus: ContactAwaitAccept.String(), }, - expectedErrorMessage: ErrContactNotFound, + expectedErrorMessage: spverrors.ErrContactNotFound, }, { testID: 2, @@ -90,7 +91,7 @@ func TestAcceptContactErrorPath(t *testing.T) { paymail: paymailGeneric, contactStatus: ContactAwaitAccept.String(), }, - expectedErrorMessage: ErrContactIncorrectStatus, + expectedErrorMessage: spverrors.ErrContactIncorrectStatus, }, { testID: 3, @@ -100,7 +101,7 @@ func TestAcceptContactErrorPath(t *testing.T) { paymail: paymailGeneric, contactStatus: ContactNotConfirmed.String(), }, - expectedErrorMessage: ErrContactIncorrectStatus, + expectedErrorMessage: spverrors.ErrContactIncorrectStatus, }, { testID: 4, @@ -110,7 +111,7 @@ func TestAcceptContactErrorPath(t *testing.T) { paymail: paymailGeneric, contactStatus: ContactRejected.String(), }, - expectedErrorMessage: ErrContactIncorrectStatus, + expectedErrorMessage: spverrors.ErrContactIncorrectStatus, }, { testID: 5, @@ -121,7 +122,7 @@ func TestAcceptContactErrorPath(t *testing.T) { contactStatus: ContactRejected.String(), deleted: true, }, - expectedErrorMessage: ErrContactNotFound, + expectedErrorMessage: spverrors.ErrContactNotFound, }, } @@ -189,13 +190,13 @@ func TestRejectContactErrorPath(t *testing.T) { testCases := []testCase{ { testID: 1, - name: "non existance contact, should return \"contact not found\" error", + name: "non existence contact, should return \"contact not found\" error", data: testCaseData{ xPub: xPubForNotFoundContact, paymail: paymailGeneric, contactStatus: ContactAwaitAccept.String(), }, - expectedErrorMessage: ErrContactNotFound, + expectedErrorMessage: spverrors.ErrContactNotFound, }, { testID: 2, @@ -205,7 +206,7 @@ func TestRejectContactErrorPath(t *testing.T) { paymail: paymailGeneric, contactStatus: ContactConfirmed.String(), }, - expectedErrorMessage: ErrContactIncorrectStatus, + expectedErrorMessage: spverrors.ErrContactIncorrectStatus, }, { testID: 3, @@ -215,7 +216,7 @@ func TestRejectContactErrorPath(t *testing.T) { paymail: paymailGeneric, contactStatus: ContactNotConfirmed.String(), }, - expectedErrorMessage: ErrContactIncorrectStatus, + expectedErrorMessage: spverrors.ErrContactIncorrectStatus, }, { testID: 4, @@ -225,7 +226,7 @@ func TestRejectContactErrorPath(t *testing.T) { paymail: paymailGeneric, contactStatus: ContactRejected.String(), }, - expectedErrorMessage: ErrContactIncorrectStatus, + expectedErrorMessage: spverrors.ErrContactIncorrectStatus, }, { testID: 5, @@ -236,7 +237,7 @@ func TestRejectContactErrorPath(t *testing.T) { contactStatus: ContactRejected.String(), deleted: true, }, - expectedErrorMessage: ErrContactNotFound, + expectedErrorMessage: spverrors.ErrContactNotFound, }} for _, tc := range testCases { @@ -306,14 +307,14 @@ func TestConfirmContactErrorPath(t *testing.T) { }{ { name: "contact doesn't exist - return not found error", - expectedError: ErrContactNotFound, + expectedError: spverrors.ErrContactNotFound, getContact: func() (*Contact, string, string) { return nil, "idontexist", "xpubID" }, }, { name: "already confirmed contact - return incorrect status error", - expectedError: ErrContactIncorrectStatus, + expectedError: spverrors.ErrContactIncorrectStatus, getContact: func() (*Contact, string, string) { cc := newContact("Paul Altreides", "paul@altreides.diune", "pki", "xpub", ContactNotConfirmed) cc.Confirm() @@ -323,7 +324,7 @@ func TestConfirmContactErrorPath(t *testing.T) { }, { name: "awaiting contact - return incorrect status error", - expectedError: ErrContactIncorrectStatus, + expectedError: spverrors.ErrContactIncorrectStatus, getContact: func() (*Contact, string, string) { cc := newContact("Alia Altreides", "alia@altreides.diune", "pki", "xpub", ContactAwaitAccept) @@ -332,7 +333,7 @@ func TestConfirmContactErrorPath(t *testing.T) { }, { name: "rejected contact - return not found error", - expectedError: ErrContactNotFound, + expectedError: spverrors.ErrContactNotFound, getContact: func() (*Contact, string, string) { cc := newContact("Alia Altreides", "alia@altreides.diune", "pki", "xpub", ContactAwaitAccept) cc.Reject() @@ -401,14 +402,14 @@ func TestUnconfirmContactErrorPath(t *testing.T) { }{ { name: "contact doesn't exist - return not found error", - expectedError: ErrContactNotFound, + expectedError: spverrors.ErrContactNotFound, getContact: func() (*Contact, string, string) { return nil, "idontexist", "xpubID" }, }, { name: "already unconfirmed contact - return incorrect status error", - expectedError: ErrContactIncorrectStatus, + expectedError: spverrors.ErrContactIncorrectStatus, getContact: func() (*Contact, string, string) { cc := newContact("Paul Altreides", "paul@altreides.diune", "pki", "xpub", ContactNotConfirmed) @@ -417,7 +418,7 @@ func TestUnconfirmContactErrorPath(t *testing.T) { }, { name: "awaiting contact - return incorrect status error", - expectedError: ErrContactIncorrectStatus, + expectedError: spverrors.ErrContactIncorrectStatus, getContact: func() (*Contact, string, string) { cc := newContact("Alia Altreides", "alia@altreides.diune", "pki", "xpub", ContactAwaitAccept) @@ -426,7 +427,7 @@ func TestUnconfirmContactErrorPath(t *testing.T) { }, { name: "rejected contact - return not found error", - expectedError: ErrContactNotFound, + expectedError: spverrors.ErrContactNotFound, getContact: func() (*Contact, string, string) { cc := newContact("Alia Altreides", "alia@altreides.diune", "pki", "xpub", ContactAwaitAccept) cc.Reject() diff --git a/engine/action_destination.go b/engine/action_destination.go index be3d13f4..b627678e 100644 --- a/engine/action_destination.go +++ b/engine/action_destination.go @@ -4,6 +4,7 @@ import ( "context" "github.com/bitcoin-sv/spv-wallet/engine/datastore" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" ) // NewDestination will get a new destination for an existing xPub @@ -24,7 +25,7 @@ func (c *Client) NewDestination(ctx context.Context, xPubKey string, chain uint3 ); err != nil { return nil, err } else if xPub == nil { - return nil, ErrMissingXpub + return nil, spverrors.ErrCouldNotFindXpub } // Get/create a new destination @@ -54,7 +55,7 @@ func (c *Client) NewDestinationForLockingScript(ctx context.Context, xPubID, loc // Ensure locking script isn't empty if len(lockingScript) == 0 { - return nil, ErrMissingLockingScript + return nil, spverrors.ErrMissingLockingScript } // Start the new destination - will detect type @@ -64,7 +65,7 @@ func (c *Client) NewDestinationForLockingScript(ctx context.Context, xPubID, loc ) if destination.Type == "" { - return nil, ErrUnknownLockingScript + return nil, spverrors.ErrUnknownLockingScript } // Save the destination @@ -166,7 +167,7 @@ func (c *Client) GetDestinationByID(ctx context.Context, xPubID, id string) (*De // Check that the id matches if destination.XpubID != xPubID { - return nil, ErrXpubIDMisMatch + return nil, spverrors.ErrXpubIDMisMatch } return destination, nil @@ -187,7 +188,7 @@ func (c *Client) GetDestinationByLockingScript(ctx context.Context, xPubID, lock // Check that the id matches if destination.XpubID != xPubID { - return nil, ErrXpubIDMisMatch + return nil, spverrors.ErrXpubIDMisMatch } return destination, nil @@ -208,7 +209,7 @@ func (c *Client) GetDestinationByAddress(ctx context.Context, xPubID, address st // Check that the id matches if destination.XpubID != xPubID { - return nil, ErrXpubIDMisMatch + return nil, spverrors.ErrXpubIDMisMatch } return destination, nil diff --git a/engine/action_destination_test.go b/engine/action_destination_test.go index 4a3f89d5..07f351cf 100644 --- a/engine/action_destination_test.go +++ b/engine/action_destination_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -69,7 +70,7 @@ func (ts *EmbeddedDBTestSuite) TestClient_NewDestination() { ) require.Error(t, err) require.Nil(t, destination) - assert.ErrorIs(t, err, ErrMissingXpub) + assert.ErrorIs(t, err, spverrors.ErrCouldNotFindXpub) }) } } @@ -122,7 +123,7 @@ func (ts *EmbeddedDBTestSuite) TestClient_NewDestinationForLockingScript() { ) require.Error(t, err) require.Nil(t, destination) - assert.ErrorIs(t, err, ErrMissingLockingScript) + assert.ErrorIs(t, err, spverrors.ErrMissingLockingScript) }) } } diff --git a/engine/action_paymails.go b/engine/action_paymails.go index 9bf1a86d..0d68edc3 100644 --- a/engine/action_paymails.go +++ b/engine/action_paymails.go @@ -2,10 +2,10 @@ package engine import ( "context" - "errors" "time" "github.com/bitcoin-sv/spv-wallet/engine/datastore" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" ) @@ -19,7 +19,7 @@ func (c *Client) GetPaymailAddress(ctx context.Context, address string, opts ... if err != nil { return nil, err } else if paymailAddress == nil { - return nil, ErrPaymailNotFound + return nil, spverrors.ErrCouldNotFindPaymail } return paymailAddress, nil @@ -105,7 +105,7 @@ func (c *Client) NewPaymailAddress(ctx context.Context, xPubKey, address, public // Check if the paymail address already exists paymail, err := getPaymailAddress(ctx, address, opts...) if paymail != nil { - return nil, errors.New("paymail address already exists") + return nil, spverrors.ErrPaymailAlreadyExists } if err != nil { return nil, err @@ -147,7 +147,7 @@ func (c *Client) DeletePaymailAddress(ctx context.Context, address string, opts if err != nil { return err } else if paymailAddress == nil { - return ErrPaymailNotFound + return spverrors.ErrCouldNotFindPaymail } // todo: make a better approach for deleting paymail addresses? @@ -179,7 +179,7 @@ func (c *Client) UpdatePaymailAddressMetadata(ctx context.Context, address strin if err != nil { return nil, err } else if paymailAddress == nil { - return nil, ErrPaymailNotFound + return nil, spverrors.ErrCouldNotFindPaymail } // Update the metadata @@ -205,7 +205,7 @@ func (c *Client) UpdatePaymailAddress(ctx context.Context, address, publicName, if err != nil { return nil, err } else if paymailAddress == nil { - return nil, ErrPaymailNotFound + return nil, spverrors.ErrCouldNotFindPaymail } // Update the public name diff --git a/engine/action_paymails_test.go b/engine/action_paymails_test.go index 1603f01a..672e3bdb 100644 --- a/engine/action_paymails_test.go +++ b/engine/action_paymails_test.go @@ -3,6 +3,7 @@ package engine import ( "testing" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -29,7 +30,7 @@ func (ts *EmbeddedDBTestSuite) TestClient_NewPaymailAddress() { var paymailAddress *PaymailAddress paymailAddress, err = tc.client.NewPaymailAddress(tc.ctx, testXPub, "", testPublicName, testAvatar, tc.client.DefaultModelOptions()...) - require.ErrorIs(t, err, ErrMissingPaymailAddress) + require.ErrorIs(t, err, spverrors.ErrMissingPaymailAddress) require.Nil(t, paymailAddress) }) @@ -81,7 +82,7 @@ func (ts *EmbeddedDBTestSuite) Test_DeletePaymailAddress() { paymail := "" err := tc.client.DeletePaymailAddress(tc.ctx, paymail, tc.client.DefaultModelOptions()...) - require.ErrorIs(t, err, ErrPaymailNotFound) + require.ErrorIs(t, err, spverrors.ErrCouldNotFindPaymail) }) ts.T().Run(testCase.name+" - delete unknown paymail address", func(t *testing.T) { @@ -89,7 +90,7 @@ func (ts *EmbeddedDBTestSuite) Test_DeletePaymailAddress() { defer tc.Close(tc.ctx) err := tc.client.DeletePaymailAddress(tc.ctx, testPaymail, tc.client.DefaultModelOptions()...) - require.ErrorIs(t, err, ErrPaymailNotFound) + require.ErrorIs(t, err, spverrors.ErrCouldNotFindPaymail) }) ts.T().Run(testCase.name+" - delete paymail address", func(t *testing.T) { diff --git a/engine/action_transaction.go b/engine/action_transaction.go index 1a2613c3..04baea15 100644 --- a/engine/action_transaction.go +++ b/engine/action_transaction.go @@ -3,13 +3,13 @@ package engine import ( "context" "errors" - "fmt" "math" "time" "github.com/bitcoin-sv/go-broadcast-client/broadcast" "github.com/bitcoin-sv/spv-wallet/engine/chainstate" "github.com/bitcoin-sv/spv-wallet/engine/datastore" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/libsv/go-bc" "github.com/libsv/go-bt/v2" @@ -25,7 +25,7 @@ func (c *Client) RecordTransaction(ctx context.Context, xPubKey, txHex, draftID tx, err := bt.NewTxFromString(txHex) if err != nil { - return nil, fmt.Errorf("invalid hex: %w", err) + return nil, spverrors.ErrInvalidHex } rts, err := getOutgoingTxRecordStrategy(xPubKey, tx, draftID) @@ -103,7 +103,7 @@ func (c *Client) GetTransaction(ctx context.Context, xPubID, txID string) (*Tran return nil, err } if transaction == nil { - return nil, ErrMissingTransaction + return nil, spverrors.ErrCouldNotFindTransaction } return transaction, nil @@ -264,7 +264,7 @@ func (c *Client) RevertTransaction(ctx context.Context, id string) error { // make sure the transaction is coming from SPV Wallet Engine if transaction.DraftID == "" { - return errors.New("not a spv wallet engine originating transaction, cannot revert") + return spverrors.ErrTxRevertEmptyDraftID } var draftTransaction *DraftTransaction @@ -272,18 +272,18 @@ func (c *Client) RevertTransaction(ctx context.Context, id string) error { return err } if draftTransaction == nil { - return errors.New("could not find the draft transaction for this transaction, cannot revert") + return spverrors.ErrTxRevertCouldNotFindDraftTx } // check whether transaction is not already on chain var info *chainstate.TransactionInfo if info, err = c.Chainstate().QueryTransaction(ctx, transaction.ID, chainstate.RequiredInMempool, 30*time.Second); err != nil { - if !errors.Is(err, chainstate.ErrTransactionNotFound) { + if !errors.Is(err, spverrors.ErrCouldNotFindTransaction) { return err } } if info != nil { - return errors.New("transaction was found on-chain, cannot revert") + return spverrors.ErrTxRevertNotFoundOnChain } // check that the utxos of this transaction have not been spent @@ -297,7 +297,7 @@ func (c *Client) RevertTransaction(ctx context.Context, id string) error { } for _, utxo := range utxos { if utxo.SpendingTxID.Valid { - return errors.New("utxo of this transaction has been spent, cannot revert") + return spverrors.ErrTxRevertUtxoAlreadySpent } } diff --git a/engine/action_utxo.go b/engine/action_utxo.go index b5a0edd0..bc004258 100644 --- a/engine/action_utxo.go +++ b/engine/action_utxo.go @@ -4,6 +4,7 @@ import ( "context" "github.com/bitcoin-sv/spv-wallet/engine/datastore" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" ) @@ -86,12 +87,12 @@ func (c *Client) GetUtxo(ctx context.Context, xPubKey, txID string, outputIndex if err != nil { return nil, err } else if utxo == nil { - return nil, ErrMissingUtxo + return nil, spverrors.ErrCouldNotFindUtxo } // Check that the id matches if utxo.XpubID != utils.Hash(xPubKey) { - return nil, ErrXpubIDMisMatch + return nil, spverrors.ErrXpubIDMisMatch } var tx *Transaction @@ -117,7 +118,7 @@ func (c *Client) GetUtxoByTransactionID(ctx context.Context, txID string, output if err != nil { return nil, err } else if utxo == nil { - return nil, ErrMissingUtxo + return nil, spverrors.ErrCouldNotFindUtxo } var tx *Transaction @@ -134,7 +135,7 @@ func (c *Client) GetUtxoByTransactionID(ctx context.Context, txID string, output // UnReserveUtxos remove the reservation on the utxos for the given draft ID func (c *Client) UnReserveUtxos(ctx context.Context, xPubID, draftID string) error { // Check for existing NewRelic transaction - ctx = c.GetOrStartTxn(ctx, "unreserve_uxtos_by_draft_id") + ctx = c.GetOrStartTxn(ctx, "unreserve_utxos_by_draft_id") return unReserveUtxos(ctx, xPubID, draftID, c.DefaultModelOptions()...) } diff --git a/engine/action_xpub_test.go b/engine/action_xpub_test.go index 15d37ce2..a1a5d325 100644 --- a/engine/action_xpub_test.go +++ b/engine/action_xpub_test.go @@ -3,7 +3,7 @@ package engine import ( "testing" - "github.com/bitcoin-sv/spv-wallet/engine/utils" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -68,10 +68,10 @@ func (ts *EmbeddedDBTestSuite) TestClient_NewXpub() { defer tc.Close(tc.ctx) _, err := tc.client.NewXpub(tc.ctx, "test", tc.client.DefaultModelOptions()...) - assert.ErrorIs(t, err, utils.ErrXpubInvalidLength) + assert.ErrorIs(t, err, spverrors.ErrXpubInvalidLength) _, err = tc.client.NewXpub(tc.ctx, "", tc.client.DefaultModelOptions()...) - assert.ErrorIs(t, err, utils.ErrXpubInvalidLength) + assert.ErrorIs(t, err, spverrors.ErrXpubInvalidLength) }) ts.T().Run(testCase.name+" - duplicate xPub", func(t *testing.T) { @@ -115,7 +115,7 @@ func (ts *EmbeddedDBTestSuite) TestClient_GetXpub() { xPub, err := tc.client.GetXpub(tc.ctx, "test") require.Error(t, err) require.Nil(t, xPub) - assert.ErrorIs(t, err, ErrMissingXpub) + assert.ErrorIs(t, err, spverrors.ErrCouldNotFindXpub) }) ts.T().Run(testCase.name+" - error - missing xpub", func(t *testing.T) { @@ -125,7 +125,7 @@ func (ts *EmbeddedDBTestSuite) TestClient_GetXpub() { xPub, err := tc.client.GetXpub(tc.ctx, testXPub) require.Error(t, err) require.Nil(t, xPub) - assert.ErrorIs(t, err, ErrMissingXpub) + assert.ErrorIs(t, err, spverrors.ErrCouldNotFindXpub) }) } } @@ -162,7 +162,7 @@ func (ts *EmbeddedDBTestSuite) TestClient_GetXpubByID() { xPub, err := tc.client.GetXpubByID(tc.ctx, testXPub) require.Error(t, err) require.Nil(t, xPub) - assert.ErrorIs(t, err, ErrMissingXpub) + assert.ErrorIs(t, err, spverrors.ErrCouldNotFindXpub) }) } } diff --git a/engine/authentication.go b/engine/authentication.go index f617a417..297ef7da 100644 --- a/engine/authentication.go +++ b/engine/authentication.go @@ -2,6 +2,8 @@ package engine import ( "context" + + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" ) // AuthenticateAccessKey check if access key exists @@ -10,9 +12,9 @@ func (c *Client) AuthenticateAccessKey(ctx context.Context, pubAccessKey string) if err != nil { return nil, err } else if accessKey == nil { - return nil, ErrUnknownAccessKey + return nil, spverrors.ErrCouldNotFindAccessKey } else if accessKey.RevokedAt.Valid { - return nil, ErrAccessKeyRevoked + return nil, spverrors.ErrAccessKeyRevoked } return accessKey, nil } diff --git a/engine/chainstate/chainstate.go b/engine/chainstate/chainstate.go index daacccdb..3601867d 100644 --- a/engine/chainstate/chainstate.go +++ b/engine/chainstate/chainstate.go @@ -6,6 +6,8 @@ package chainstate import ( "context" "time" + + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" ) // HexFormatFlag transaction hex format @@ -21,7 +23,7 @@ func (flag HexFormatFlag) Contains(other HexFormatFlag) bool { return (flag & other) == other } -// SupportedBroadcastFormats retuns supported formats based on active providers +// SupportedBroadcastFormats returns supported formats based on active providers func (c *Client) SupportedBroadcastFormats() HexFormatFlag { return RawTx | Ef } @@ -72,15 +74,15 @@ func (c *Client) QueryTransaction( // Basic validation if len(id) < 50 { - return nil, ErrInvalidTransactionID + return nil, spverrors.ErrInvalidTransactionID } else if !c.validRequirement(requiredIn) { - return nil, ErrInvalidRequirements + return nil, spverrors.ErrInvalidRequirements } // Try all providers and return the "first" valid response info := c.query(ctx, id, requiredIn, timeout) if info == nil { - return nil, ErrTransactionNotFound + return nil, spverrors.ErrCouldNotFindTransaction } return info, nil } @@ -93,15 +95,15 @@ func (c *Client) QueryTransactionFastest( ) (*TransactionInfo, error) { // Basic validation if len(id) < 50 { - return nil, ErrInvalidTransactionID + return nil, spverrors.ErrInvalidTransactionID } else if !c.validRequirement(requiredIn) { - return nil, ErrInvalidRequirements + return nil, spverrors.ErrInvalidRequirements } // Try all providers and return the "fastest" valid response info := c.fastestQuery(ctx, id, requiredIn, timeout) if info == nil { - return nil, ErrTransactionNotFound + return nil, spverrors.ErrCouldNotFindTransaction } return info, nil } diff --git a/engine/chainstate/errors.go b/engine/chainstate/errors.go index 070d9e0a..38b603ad 100644 --- a/engine/chainstate/errors.go +++ b/engine/chainstate/errors.go @@ -2,21 +2,6 @@ package chainstate import "errors" -// ErrInvalidTransactionID is when the transaction id is missing or invalid -var ErrInvalidTransactionID = errors.New("invalid transaction id") - -// ErrInvalidTransactionHex is when the transaction hex is missing or invalid -var ErrInvalidTransactionHex = errors.New("invalid transaction hex") - -// ErrTransactionIDMismatch is when the returned tx does not match the expected given tx id -var ErrTransactionIDMismatch = errors.New("result tx id did not match provided tx id") - -// ErrTransactionNotFound is when a transaction was not found in any on-chain provider -var ErrTransactionNotFound = errors.New("transaction not found using all chain providers") - -// ErrInvalidRequirements is when an invalid requirement was given -var ErrInvalidRequirements = errors.New("requirements are invalid or missing") - // ErrMissingBroadcastMiners is when broadcasting miners are missing var ErrMissingBroadcastMiners = errors.New("missing: broadcasting miners") diff --git a/engine/chainstate/transaction.go b/engine/chainstate/transaction.go index bdd5f5ba..bb34970c 100644 --- a/engine/chainstate/transaction.go +++ b/engine/chainstate/transaction.go @@ -7,6 +7,7 @@ import ( "sync" "time" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/libsv/go-bc" ) @@ -83,5 +84,5 @@ func queryBroadcastClient(ctx context.Context, client ClientInterface, id string BUMP: bump, }, nil } - return nil, ErrTransactionIDMismatch + return nil, spverrors.ErrTransactionIDMismatch } diff --git a/engine/chainstate/transaction_test.go b/engine/chainstate/transaction_test.go index 08d7264e..c0a1165a 100644 --- a/engine/chainstate/transaction_test.go +++ b/engine/chainstate/transaction_test.go @@ -6,6 +6,7 @@ import ( broadcast_client_mock "github.com/bitcoin-sv/go-broadcast-client/broadcast/broadcast-client-mock" broadcast_fixtures "github.com/bitcoin-sv/go-broadcast-client/broadcast/broadcast-client-mock/fixtures" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -29,7 +30,7 @@ func TestClient_Transaction(t *testing.T) { // then require.Error(t, err) require.Nil(t, info) - assert.ErrorIs(t, err, ErrInvalidTransactionID) + assert.ErrorIs(t, err, spverrors.ErrInvalidTransactionID) }) t.Run("error - missing requirements", func(t *testing.T) { @@ -45,7 +46,7 @@ func TestClient_Transaction(t *testing.T) { // then require.Error(t, err) require.Nil(t, info) - assert.ErrorIs(t, err, ErrInvalidRequirements) + assert.ErrorIs(t, err, spverrors.ErrInvalidRequirements) }) } diff --git a/engine/client.go b/engine/client.go index efa9388e..c0bfa83e 100644 --- a/engine/client.go +++ b/engine/client.go @@ -12,6 +12,7 @@ import ( "github.com/bitcoin-sv/spv-wallet/engine/logging" "github.com/bitcoin-sv/spv-wallet/engine/metrics" "github.com/bitcoin-sv/spv-wallet/engine/notifications" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/taskmanager" "github.com/mrz1836/go-cachestore" "github.com/newrelic/go-agent/v3/newrelic" @@ -208,7 +209,7 @@ func (c *Client) AddModels(ctx context.Context, autoMigrate bool, models ...inte // Ensure we have a datastore d := c.Datastore() if d == nil { - return ErrDatastoreRequired + return spverrors.ErrDatastoreRequired } // Apply the database migration with the new models diff --git a/engine/client_internal.go b/engine/client_internal.go index 9ba7b4a5..7fd7560b 100644 --- a/engine/client_internal.go +++ b/engine/client_internal.go @@ -2,6 +2,7 @@ package engine import ( "context" + "github.com/bitcoin-sv/go-paymail" "github.com/bitcoin-sv/go-paymail/server" "github.com/bitcoin-sv/spv-wallet/engine/chainstate" diff --git a/engine/contact_service_test.go b/engine/contact_service_test.go index 3c5aa7cf..13482cfa 100644 --- a/engine/contact_service_test.go +++ b/engine/contact_service_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/bitcoin-sv/go-paymail" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/jarcoal/httpmock" "github.com/stretchr/testify/require" @@ -55,7 +56,7 @@ func Test_ClientService_UpsertContact(t *testing.T) { res, err := client.UpsertContact(ctx, "Bran Stark", "bran_the_broken@winterfell.com", cs_xpubHash, "", client.DefaultModelOptions()...) // then - require.ErrorIs(t, err, ErrInvalidRequesterXpub) + require.ErrorIs(t, err, spverrors.ErrInvalidRequesterXpub) require.Nil(t, res) }) @@ -82,7 +83,7 @@ func Test_ClientService_UpsertContact(t *testing.T) { res, err := client.UpsertContact(ctx, "Bran Stark", paymailAddr, cs_xpubHash, "lady_stoneheart@winterfell.com", client.DefaultModelOptions()...) // then - require.ErrorIs(t, err, ErrAddingContactRequest) + require.ErrorIs(t, err, spverrors.ErrAddingContactRequest) require.NotNil(t, res) require.Equal(t, ContactNotConfirmed, res.Status) }) diff --git a/engine/db_model_transactions.go b/engine/db_model_transactions.go index b54c467c..d4ea2e51 100644 --- a/engine/db_model_transactions.go +++ b/engine/db_model_transactions.go @@ -5,6 +5,7 @@ import ( "github.com/bitcoin-sv/spv-wallet/engine/datastore" "github.com/bitcoin-sv/spv-wallet/engine/notifications" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" ) // GetModelTableName will get the db table name of the current model @@ -88,7 +89,7 @@ func (m *Transaction) AfterCreated(ctx context.Context) error { if err != nil { return err } else if xPub == nil { - return ErrMissingRequiredXpub + return spverrors.ErrMissingFieldXpub } if err = xPub.incrementBalance(ctx, balance); err != nil { return err diff --git a/engine/errors.go b/engine/errors.go index ad1efe19..0978934e 100644 --- a/engine/errors.go +++ b/engine/errors.go @@ -4,212 +4,8 @@ import ( "errors" ) -// ErrCannotConvertToIDs is the error when the conversion fails from interface into type IDs -var ErrCannotConvertToIDs = errors.New("cannot convert value to type IDs") - -// ErrMissingDestination is an error when a destination could not be found -var ErrMissingDestination = errors.New("destination could not be found") - -// ErrMissingTransaction is an error when a transaction could not be found -var ErrMissingTransaction = errors.New("transaction could not be found") - -// ErrMissingUtxo is an error when a given utxo could not be found -var ErrMissingUtxo = errors.New("utxo could not be found") - -// ErrMissingFieldID is an error when missing the id field -var ErrMissingFieldID = errors.New("missing required field: id") - // ErrMissingFieldHex is an error when missing the hex field of a transaction var ErrMissingFieldHex = errors.New("missing required field: hex") -// ErrMissingFieldHash is an error when missing the hex field of a transaction -var ErrMissingFieldHash = errors.New("missing required field: hash") - -// ErrMissingFieldScriptPubKey is when the field is required but missing -var ErrMissingFieldScriptPubKey = errors.New("missing required field: script_pub_key") - -// ErrMissingFieldSatoshis is when the field is required but missing -var ErrMissingFieldSatoshis = errors.New("missing required field: satoshis") - -// ErrMissingFieldTransactionID is when the field is required but missing -var ErrMissingFieldTransactionID = errors.New("missing required field: transaction_id") - -// ErrMissingFieldXpubID is when the field is required but missing -var ErrMissingFieldXpubID = errors.New("missing required field: xpub_id") - -// ErrXpubIDMisMatch is when the xPubID does not match -var ErrXpubIDMisMatch = errors.New("xpub_id mismatch") - -// ErrMissingXpub is when the field is required but missing -var ErrMissingXpub = errors.New("could not find xpub") - -// ErrAccessKeyNotFound is when the access key not found -var ErrAccessKeyNotFound = errors.New("access key not found") - -// ErrMissingLockingScript is when the field is required but missing -var ErrMissingLockingScript = errors.New("could not find locking script") - -// ErrUnknownLockingScript is when the field is unknown -var ErrUnknownLockingScript = errors.New("could not recognize locking script") - -// ErrMissingRequiredXpub is when the xpub should exist but was not found -var ErrMissingRequiredXpub = errors.New("xpub was not found but was expected") - -// ErrDatastoreRequired is when a datastore function is called without a datastore present -var ErrDatastoreRequired = errors.New("datastore is required") - -// ErrMissingTransactionOutputs is when the draft transaction has not outputs -var ErrMissingTransactionOutputs = errors.New("draft transaction configuration has no outputs") - -// ErrNotEnoughUtxos is when a draft transaction cannot be created because of lack of utxos -var ErrNotEnoughUtxos = errors.New("could not select enough outputs to satisfy transaction") - -// ErrInvalidLockingScript is when a locking script cannot be decoded -var ErrInvalidLockingScript = errors.New("invalid locking script") - -// ErrInvalidOpReturnOutput is when a locking script is not a valid op_return -var ErrInvalidOpReturnOutput = errors.New("invalid op_return output") - -// ErrInvalidScriptOutput is when a locking script is not a valid bitcoin script -var ErrInvalidScriptOutput = errors.New("invalid script output") - -// ErrInvalidTransactionID is when a transaction id cannot be decoded -var ErrInvalidTransactionID = errors.New("invalid transaction id") - -// ErrOutputValueNotRecognized is when there is an invalid output value given, or missing value -var ErrOutputValueNotRecognized = errors.New("output value is unrecognized") - -// ErrOutputValueTooLow is when the satoshis output is too low on a transaction -var ErrOutputValueTooLow = errors.New("output value is too low") - -// ErrOutputValueTooHigh is when the satoshis output is too high on a transaction -var ErrOutputValueTooHigh = errors.New("output value is too high") - -// ErrTransactionFeeInvalid is when the fee on the transaction is not the difference between inputs and outputs -var ErrTransactionFeeInvalid = errors.New("transaction fee is invalid") - -// ErrMissingUTXOsSpendable is when there are no utxos found from the "spendable utxos" -var ErrMissingUTXOsSpendable = errors.New("no utxos found using spendable") - -// ErrDuplicateUTXOs is when a transaction is created using the same utxo more than once -var ErrDuplicateUTXOs = errors.New("duplicate utxos found") - -// ErrPaymailAddressIsInvalid is when the paymail address is NOT alias@domain.com -var ErrPaymailAddressIsInvalid = errors.New("paymail address is invalid") - -// ErrPaymailNotFound is when paymaail could not be found -var ErrPaymailNotFound = errors.New("paymail could not be found") - -// ErrUtxoNotReserved is when the utxo is not reserved, but a transaction tries to spend it -var ErrUtxoNotReserved = errors.New("transaction utxo has not been reserved for spending") - -// ErrDraftIDMismatch is when the reference ID does not match the reservation id -var ErrDraftIDMismatch = errors.New("transaction draft id does not match utxo draft reservation id") - -// ErrMissingTxHex is when the hex is missing or invalid and creates an empty id -var ErrMissingTxHex = errors.New("transaction hex is empty or id is missing") - -// ErrUtxoAlreadySpent is when the utxo is already spent, but is trying to be used -var ErrUtxoAlreadySpent = errors.New("utxo has already been spent") - -// ErrDraftNotFound is when the requested draft transaction was not found -var ErrDraftNotFound = errors.New("corresponding draft transaction not found") - -// ErrTransactionNotParsed is when the transaction is not parsed but was expected -var ErrTransactionNotParsed = errors.New("transaction is not parsed") - // ErrNoMatchingOutputs is when the transaction does not match any known destinations var ErrNoMatchingOutputs = errors.New("transaction outputs do not match any known destinations") - -// ErrResolutionFailed is when the paymail resolution failed unexpectedly -var ErrResolutionFailed = errors.New("failed to return a resolution for paymail address") - -// ErrMissingAddressResolutionURL is when the paymail resolution url is missing from capabilities -var ErrMissingAddressResolutionURL = errors.New("missing address resolution url from capabilities") - -// ErrChangeStrategyNotImplemented is a temporary error until the feature is supported -var ErrChangeStrategyNotImplemented = errors.New("change strategy nominations not implemented yet") - -// ErrUnsupportedDestinationType is a destination type that is not currently supported -var ErrUnsupportedDestinationType = errors.New("unsupported destination type") - -// ErrMissingAuthHeader is when the authentication header is missing from the request -var ErrMissingAuthHeader = errors.New("missing authentication header") - -// ErrMissingSignature is when the signature is missing from the request -var ErrMissingSignature = errors.New("signature missing") - -// ErrAuhHashMismatch is when the auth hash does not match the body hash -var ErrAuhHashMismatch = errors.New("auth hash and body hash do not match") - -// ErrAuthAccessKeyNotFound is when the auth access key could not be found in the database -var ErrAuthAccessKeyNotFound = errors.New("auth access key could not be found") - -// ErrSignatureExpired is when the signature TTL expired -var ErrSignatureExpired = errors.New("signature has expired") - -// ErrNotAdminKey is when the xpub being used is not considered an admin key -var ErrNotAdminKey = errors.New("xpub provided is not an admin key") - -// ErrMissingXPriv is when the xPriv is missing -var ErrMissingXPriv = errors.New("missing xPriv key") - -// ErrMissingAccessKey is when the access key is missing -var ErrMissingAccessKey = errors.New("missing access key") - -// ErrMissingBody is when the body is missing -var ErrMissingBody = errors.New("missing body") - -// ErrSignatureInvalid is when the signature failed to be valid -var ErrSignatureInvalid = errors.New("signature invalid") - -// ErrUnknownAccessKey is when the access key is unknown or not found -var ErrUnknownAccessKey = errors.New("unknown access key") - -// ErrAccessKeyRevoked is when the access key has been revoked -var ErrAccessKeyRevoked = errors.New("access key has been revoked") - -// ErrMissingPaymailID missing id in paymail -var ErrMissingPaymailID = errors.New("missing id in paymail") - -// ErrMissingPaymailAddress missing alias in paymail -var ErrMissingPaymailAddress = errors.New("missing alias in paymail") - -// ErrMissingPaymailDomain missing domain in paymail -var ErrMissingPaymailDomain = errors.New("missing domain in paymail") - -// ErrMissingPaymailExternalXPub missing external xPub in paymail -var ErrMissingPaymailExternalXPub = errors.New("missing external xPub in paymail") - -// ErrMissingPaymailIdentityXPub missing identity xPub in paymail -// var ErrMissingPaymailIdentityXPub = errors.New("missing identity xPub in paymail") - -// ErrMissingPaymailXPubID missing xpub_id in paymail -var ErrMissingPaymailXPubID = errors.New("missing xpub_id in paymail") - -// ErrMissingClient missing client from model -var ErrMissingClient = errors.New("client is missing from model, cannot save") - -// ErrMissingContactID missing id in contact -var ErrMissingContactID = errors.New("missing id in contact") - -// ErrMissingContactFullName missing full name in contact -var ErrMissingContactFullName = errors.New("missing full_name in contact") - -// ErrMissingContactPaymail missing paymail in contact -var ErrMissingContactPaymail = errors.New("missing paymail in contact") - -// ErrMissingContactXPubKey missing XPubKey in contact -var ErrMissingContactXPubKey = errors.New("missing pubKey in contact") - -// ErrMissingContactStatus missing status in contact -var ErrMissingContactStatus = errors.New("status is required") - -// ErrMissingContactStatus missing status in contact -var ErrMissingContactOwnerXPubId = errors.New("contact must have owner") - -// ErrEmptyContactPubKey when pubKey is empty -var ErrEmptyContactPubKey = errors.New("pubKey is empty") - -// ErrEmptyContactPaymail when paymail is empty -var ErrEmptyContactPaymail = errors.New("paymail is empty") diff --git a/engine/model_access_keys.go b/engine/model_access_keys.go index e62b0dcc..38e9c8ab 100644 --- a/engine/model_access_keys.go +++ b/engine/model_access_keys.go @@ -8,6 +8,7 @@ import ( "github.com/bitcoin-sv/spv-wallet/engine/datastore" customTypes "github.com/bitcoin-sv/spv-wallet/engine/datastore/customtypes" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/bitcoinschema/go-bitcoin/v2" ) @@ -182,7 +183,7 @@ func (m *AccessKey) BeforeCreating(_ context.Context) error { // Make sure ID is valid if len(m.ID) == 0 { - return ErrMissingFieldID + return spverrors.ErrMissingFieldID } m.Client().Logger().Debug(). diff --git a/engine/model_contact.go b/engine/model_contact.go index 1291988f..fa81b4d0 100644 --- a/engine/model_contact.go +++ b/engine/model_contact.go @@ -9,6 +9,7 @@ import ( "github.com/bitcoin-sv/go-paymail" "github.com/bitcoin-sv/spv-wallet/engine/datastore" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/google/uuid" ) @@ -110,11 +111,11 @@ func getContactsByXPubIDCount(ctx context.Context, xPubID string, metadata *Meta func (c *Contact) validate() error { if c.ID == "" { - return ErrMissingContactID + return spverrors.ErrMissingContactID } if c.FullName == "" { - return ErrMissingContactFullName + return spverrors.ErrMissingContactFullName } if err := paymail.ValidatePaymail(c.Paymail); err != nil { @@ -122,15 +123,15 @@ func (c *Contact) validate() error { } if c.PubKey == "" { - return ErrMissingContactXPubKey + return spverrors.ErrMissingContactXPubKey } if c.Status == "" { - return ErrMissingContactStatus + return spverrors.ErrMissingContactStatus } if c.OwnerXpubID == "" { - return ErrMissingContactOwnerXPubId + return spverrors.ErrMissingContactOwnerXPubId } return nil diff --git a/engine/model_contact_test.go b/engine/model_contact_test.go index 3f967c07..f8d01a44 100644 --- a/engine/model_contact_test.go +++ b/engine/model_contact_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/stretchr/testify/require" ) @@ -35,7 +36,7 @@ func Test_contact_validate_returns_error(t *testing.T) { { name: "empty full name", contact: newContact("", "donot@know.who", "xpubblablablabla", "ownerspbubid", ContactNotConfirmed), - expetedError: ErrMissingContactFullName, + expetedError: spverrors.ErrMissingContactFullName, }, { name: "empty paymail", @@ -50,17 +51,17 @@ func Test_contact_validate_returns_error(t *testing.T) { { name: "empty pubKey", contact: newContact("Bart Simpson", "bart@springfield.com", "", "ownerspbubid", ContactNotConfirmed), - expetedError: ErrMissingContactXPubKey, + expetedError: spverrors.ErrMissingContactXPubKey, }, { name: "no owner id", contact: newContact("Lisa Simpson", "lisa@springfield.com", "xpubblablalisa", "", ContactNotConfirmed), - expetedError: ErrMissingContactOwnerXPubId, + expetedError: spverrors.ErrMissingContactOwnerXPubId, }, { name: "no status", contact: newContact("Margaret Simpson", "maggie@springfield.com", "xpubblablamaggie", "ownerspbubid", ""), - expetedError: ErrMissingContactStatus, + expetedError: spverrors.ErrMissingContactStatus, }, } @@ -265,7 +266,7 @@ func Test_getContacts(t *testing.T) { xpubID := "xpubid" - // fullfill db + // fulfill db saveContactsN(xpubID, ContactAwaitAccept, 10, client) saveContactsN(xpubID, ContactNotConfirmed, 13, client) @@ -294,7 +295,7 @@ func Test_getContacts(t *testing.T) { xpubID := "xpubid" - // fullfill db + // fulfill db saveContactsN(xpubID, ContactAwaitAccept, 10, client) saveContactsN(xpubID, ContactNotConfirmed, 13, client) @@ -314,7 +315,7 @@ func Test_getContacts(t *testing.T) { xpubID := "xpubid" - // fullfill db + // fulfill db saveContactsN(xpubID, ContactAwaitAccept, 10, client) saveContactsN("other-xpub", ContactNotConfirmed, 13, client) @@ -334,7 +335,7 @@ func Test_getContacts(t *testing.T) { xpubID := "xpubid" - // fullfill db + // fulfill db saveContactsN(xpubID, ContactAwaitAccept, 10, client) saveContactsDeletedN(xpubID, ContactNotConfirmed, 13, client) diff --git a/engine/model_destinations.go b/engine/model_destinations.go index bd084bf4..73304902 100644 --- a/engine/model_destinations.go +++ b/engine/model_destinations.go @@ -8,6 +8,7 @@ import ( "github.com/bitcoin-sv/spv-wallet/engine/cluster" "github.com/bitcoin-sv/spv-wallet/engine/datastore" "github.com/bitcoin-sv/spv-wallet/engine/notifications" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/bitcoinschema/go-bitcoin/v2" ) @@ -253,7 +254,7 @@ func getDestinationWithCache(ctx context.Context, client ClientInterface, cacheKey = fmt.Sprintf(cacheKeyDestinationModelByLockingScript, lockingScript) } if len(cacheKey) == 0 { - return nil, ErrMissingFieldID + return nil, spverrors.ErrMissingFieldID } // Attempt to get from cache @@ -287,7 +288,7 @@ func getDestinationWithCache(ctx context.Context, client ClientInterface, if err != nil { return nil, err } else if destination == nil { - return nil, ErrMissingDestination + return nil, spverrors.ErrCouldNotFindDestination } // Save to cache diff --git a/engine/model_destinations_test.go b/engine/model_destinations_test.go index d6505ce0..64e48aff 100644 --- a/engine/model_destinations_test.go +++ b/engine/model_destinations_test.go @@ -5,6 +5,7 @@ import ( "github.com/DATA-DOG/go-sqlmock" "github.com/bitcoin-sv/spv-wallet/engine/datastore" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/tester" "github.com/bitcoin-sv/spv-wallet/engine/utils" bscript2 "github.com/libsv/go-bt/v2/bscript" @@ -335,7 +336,7 @@ func TestClient_NewDestination(t *testing.T) { ) require.Error(t, err) require.Nil(t, destination) - assert.ErrorIs(t, err, ErrMissingXpub) + assert.ErrorIs(t, err, spverrors.ErrCouldNotFindXpub) }) t.Run("error - unsupported destination type", func(t *testing.T) { diff --git a/engine/model_draft_transactions.go b/engine/model_draft_transactions.go index db7aed0e..917fe8f1 100644 --- a/engine/model_draft_transactions.go +++ b/engine/model_draft_transactions.go @@ -5,12 +5,13 @@ import ( "crypto/rand" "encoding/hex" "fmt" - "github.com/bitcoin-sv/go-paymail" "math" "math/big" "time" + "github.com/bitcoin-sv/go-paymail" "github.com/bitcoin-sv/spv-wallet/engine/datastore" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/bitcoinschema/go-bitcoin/v2" "github.com/libsv/go-bk/bec" @@ -237,7 +238,7 @@ func (m *DraftTransaction) processConfigOutputs(ctx context.Context) error { func (m *DraftTransaction) createTransactionHex(ctx context.Context) (err error) { // Check that we have outputs if len(m.Configuration.Outputs) == 0 && m.Configuration.SendAllTo == nil { - return ErrMissingTransactionOutputs + return spverrors.ErrMissingTransactionOutputs } // Get the total satoshis needed to make this transaction @@ -288,7 +289,7 @@ func (m *DraftTransaction) calculateAndSetFee(ctx context.Context, satoshisReser fee := m.estimateFee(m.Configuration.FeeUnit, 0) if m.Configuration.SendAllTo != nil { if m.Configuration.Outputs[0].Satoshis <= dustLimit { - return ErrOutputValueTooLow + return spverrors.ErrOutputValueTooLow } m.Configuration.Fee = fee @@ -306,7 +307,7 @@ func (m *DraftTransaction) calculateAndSetFee(ctx context.Context, satoshisReser } if satoshisReserved < satoshisNeeded+fee { - return ErrNotEnoughUtxos + return spverrors.ErrNotEnoughUtxos } // if we have a remainder, add that to an output to our own wallet address @@ -436,7 +437,7 @@ func validateOutputsInputs(inputs []*TransactionInput, outputs []*TransactionOut for _, input := range inputs { if utils.StringInSlice(input.Utxo.ID, usedUtxos) { - return ErrDuplicateUTXOs + return spverrors.ErrDuplicateUTXOs } usedUtxos = append(usedUtxos, input.Utxo.ID) inputValue += input.Satoshis @@ -447,11 +448,11 @@ func validateOutputsInputs(inputs []*TransactionInput, outputs []*TransactionOut } if inputValue < outputValue { - return ErrOutputValueTooHigh + return spverrors.ErrOutputValueTooHigh } if inputValue-outputValue != fee { - return ErrTransactionFeeInvalid + return spverrors.ErrTransactionFeeInvalid } return nil } @@ -470,7 +471,7 @@ func (m *DraftTransaction) addIncludeUtxos(ctx context.Context) (uint64, error) if err != nil { return 0, err } else if utxoModel == nil { - return 0, ErrMissingUtxo + return 0, spverrors.ErrCouldNotFindUtxo } includeUtxos = append(includeUtxos, utxoModel) includeUtxoSatoshis += utxoModel.Satoshis @@ -491,7 +492,7 @@ func (m *DraftTransaction) processUtxos(ctx context.Context, utxos []*Utxo) erro return err } if destination == nil { - return ErrMissingDestination + return spverrors.ErrCouldNotFindDestination } m.Configuration.Inputs = append( m.Configuration.Inputs, &TransactionInput{ @@ -552,7 +553,7 @@ func (m *DraftTransaction) addOutputsToTx(tx *bt.Tx) (err error) { if scriptType == utils.ScriptTypeNullData { // op_return output - only one allowed to have 0 satoshi value ??? if sc.Satoshis > 0 { - return ErrInvalidOpReturnOutput + return spverrors.ErrInvalidOpReturnOutput } tx.AddOutput(&bt.Output{ @@ -562,7 +563,7 @@ func (m *DraftTransaction) addOutputsToTx(tx *bt.Tx) (err error) { } else if scriptType == utils.ScriptTypePubKeyHash { // sending to a p2pkh if sc.Satoshis == 0 { - return ErrOutputValueTooLow + return spverrors.ErrOutputValueTooLow } if err = tx.AddP2PKHOutputFromScript( @@ -662,7 +663,7 @@ func (m *DraftTransaction) getChangeSatoshis(satoshisChange uint64) (changeSatos changeUsed := uint64(0) if m.Configuration.ChangeDestinationsStrategy == ChangeStrategyNominations { - return nil, ErrChangeStrategyNotImplemented + return nil, spverrors.ErrChangeStrategyNotImplemented } else if m.Configuration.ChangeDestinationsStrategy == ChangeStrategyRandom { nDestinations := float64(len(m.Configuration.ChangeDestinations)) var a *big.Int @@ -713,7 +714,7 @@ func (m *DraftTransaction) setChangeDestinations(ctx context.Context, numberOfDe ); err != nil { return err } else if xPub == nil { - return ErrMissingXpub + return spverrors.ErrMissingFieldXpub } if num, err = xPub.incrementNextNum( @@ -752,14 +753,14 @@ func (m *DraftTransaction) getInputsFromUtxos(reservedUtxos []*Utxo) ([]*bt.UTXO if lockingScript, err = bscript.NewFromHexString( utxo.ScriptPubKey, ); err != nil { - return nil, 0, errors.Wrap(ErrInvalidLockingScript, err.Error()) + return nil, 0, spverrors.ErrInvalidLockingScript } var txIDBytes []byte if txIDBytes, err = hex.DecodeString( utxo.TransactionID, ); err != nil { - return nil, 0, errors.Wrap(ErrInvalidTransactionID, err.Error()) + return nil, 0, spverrors.ErrInvalidTransactionID } inputUtxos = append(inputUtxos, &bt.UTXO{ diff --git a/engine/model_draft_transactions_test.go b/engine/model_draft_transactions_test.go index 852f331c..7225a882 100644 --- a/engine/model_draft_transactions_test.go +++ b/engine/model_draft_transactions_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/bitcoin-sv/spv-wallet/engine/chainstate" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/bitcoinschema/go-bitcoin/v2" "github.com/jarcoal/httpmock" @@ -259,7 +260,7 @@ func TestDraftTransaction_createTransaction(t *testing.T) { _, client, deferMe := CreateTestSQLiteClient(t, false, false, withTaskManagerMockup()) defer deferMe() _, err := newDraftTransaction(testXPub, &TransactionConfig{}, append(client.DefaultModelOptions(), New())...) - require.ErrorIs(t, err, ErrMissingTransactionOutputs) + require.ErrorIs(t, err, spverrors.ErrMissingTransactionOutputs) }) t.Run("transaction with no utxos", func(t *testing.T) { @@ -271,7 +272,7 @@ func TestDraftTransaction_createTransaction(t *testing.T) { Satoshis: 1000, }}, }, append(client.DefaultModelOptions(), New())...) - require.ErrorIs(t, err, ErrNotEnoughUtxos) + require.ErrorIs(t, err, spverrors.ErrNotEnoughUtxos) }) t.Run("transaction with utxos", func(t *testing.T) { @@ -745,7 +746,7 @@ func TestDraftTransaction_createTransaction(t *testing.T) { Satoshis: 1500, }}, }, append(client.DefaultModelOptions(), New())...) - require.ErrorIs(t, err, ErrDuplicateUTXOs) + require.ErrorIs(t, err, spverrors.ErrDuplicateUTXOs) }) } @@ -769,7 +770,7 @@ func TestDraftTransaction_setChangeDestination(t *testing.T) { } _, err := draftTransaction.setChangeDestination(ctx, 100, 200) - require.ErrorIs(t, err, ErrMissingXpub) + require.ErrorIs(t, err, spverrors.ErrCouldNotFindXpub) }) t.Run("set valid destination", func(t *testing.T) { @@ -898,7 +899,7 @@ func TestDraftTransaction_getInputsFromUtxos(t *testing.T) { ScriptPubKey: "testLockingScript", }} inputUtxos, satoshisReserved, err := draftTransaction.getInputsFromUtxos(reservedUtxos) - require.ErrorIs(t, err, ErrInvalidLockingScript) + require.ErrorIs(t, err, spverrors.ErrInvalidLockingScript) assert.Nil(t, inputUtxos) assert.Equal(t, uint64(0), satoshisReserved) }) @@ -915,7 +916,7 @@ func TestDraftTransaction_getInputsFromUtxos(t *testing.T) { ScriptPubKey: testLockingScript, }} inputUtxos, satoshisReserved, err := draftTransaction.getInputsFromUtxos(reservedUtxos) - require.ErrorIs(t, err, ErrInvalidTransactionID) + require.ErrorIs(t, err, spverrors.ErrInvalidTransactionID) assert.Nil(t, inputUtxos) assert.Equal(t, uint64(0), satoshisReserved) }) @@ -1062,7 +1063,7 @@ func TestDraftTransaction_addOutputsToTx(t *testing.T) { } tx := bt.NewTx() err := draft.addOutputsToTx(tx) - require.ErrorIs(t, err, ErrOutputValueTooLow) + require.ErrorIs(t, err, spverrors.ErrOutputValueTooLow) assert.Len(t, tx.Outputs, 0) }) @@ -1119,7 +1120,7 @@ func TestDraftTransaction_addOutputsToTx(t *testing.T) { } tx := bt.NewTx() err := draft.addOutputsToTx(tx) - require.ErrorIs(t, err, ErrInvalidOpReturnOutput) + require.ErrorIs(t, err, spverrors.ErrInvalidOpReturnOutput) }) } diff --git a/engine/model_paymail_addresses.go b/engine/model_paymail_addresses.go index de6be0cd..e4343e64 100644 --- a/engine/model_paymail_addresses.go +++ b/engine/model_paymail_addresses.go @@ -8,6 +8,7 @@ import ( "github.com/bitcoin-sv/go-paymail" "github.com/bitcoin-sv/spv-wallet/engine/datastore" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/bitcoinschema/go-bitcoin/v2" "github.com/libsv/go-bk/bip32" @@ -291,19 +292,19 @@ func (m *PaymailAddress) BeforeCreating(_ context.Context) (err error) { Msgf("starting: %s BeforeCreate hook...", m.Name()) if m.ID == "" { - return ErrMissingPaymailID + return spverrors.ErrMissingFieldID } if len(m.Alias) == 0 { - return ErrMissingPaymailAddress + return spverrors.ErrMissingPaymailAddress } if len(m.Domain) == 0 { - return ErrMissingPaymailDomain + return spverrors.ErrMissingPaymailDomain } if len(m.ExternalXpubKey) == 0 { - return ErrMissingPaymailExternalXPub + return spverrors.ErrMissingPaymailExternalXPub } else if len(m.externalXpubKeyDecrypted) > 0 { if _, err = utils.ValidateXPub(m.externalXpubKeyDecrypted); err != nil { return @@ -311,7 +312,7 @@ func (m *PaymailAddress) BeforeCreating(_ context.Context) (err error) { } if len(m.XpubID) == 0 { - return ErrMissingPaymailXPubID + return spverrors.ErrMissingPaymailXPubID } m.Client().Logger().Debug(). diff --git a/engine/model_paymail_addresses_test.go b/engine/model_paymail_addresses_test.go index 992c3eb6..6721230f 100644 --- a/engine/model_paymail_addresses_test.go +++ b/engine/model_paymail_addresses_test.go @@ -153,7 +153,7 @@ func TestNewPaymail(t *testing.T) { derivationForSecondXpub := pm.XpubDerivationSeq // then - require.Equal(t, derivationNumber, pm.ExternalXpubKeyNum, "ExternalXpubKeyNum MUST NOT be changed durring any key rotation") + require.Equal(t, derivationNumber, pm.ExternalXpubKeyNum, "ExternalXpubKeyNum MUST NOT be changed during any key rotation") require.Equal(t, uint32(1), derivationForFirstXpub, "XpubDerivationSeq after first rotation MUST equal 1") require.Equal(t, uint32(2), derivationForSecondXpub, "XpubDerivationSeq after second rotation MUST equal 2") @@ -192,7 +192,7 @@ func TestNewPaymail(t *testing.T) { // then require.Equal(t, initialDerivationSeq, pm.XpubDerivationSeq, "XpubDerivationSeq cannot be changed") - require.Equal(t, derivationNumber, pm.ExternalXpubKeyNum, "ExternalXpubKeyNum MUST NOT be changed durring any key rotation") + require.Equal(t, derivationNumber, pm.ExternalXpubKeyNum, "ExternalXpubKeyNum MUST NOT be changed during any key rotation") require.Equal(t, firstPubKey, secondPubKey, "PubKeys must be equal") @@ -234,7 +234,7 @@ func TestNewPaymail(t *testing.T) { // then require.Greater(t, pm.XpubDerivationSeq, initialDerivationSeq, "XpubDerivationSeq must be incremented after rotation") - require.Equal(t, derivationNumber, pm.ExternalXpubKeyNum, "ExternalXpubKeyNum MUST NOT be changed durring any key rotation") + require.Equal(t, derivationNumber, pm.ExternalXpubKeyNum, "ExternalXpubKeyNum MUST NOT be changed during any key rotation") require.NotEqual(t, firstPubKey, secondPubKey) diff --git a/engine/model_save.go b/engine/model_save.go index 53c62737..1da215e2 100644 --- a/engine/model_save.go +++ b/engine/model_save.go @@ -5,6 +5,7 @@ import ( "time" "github.com/bitcoin-sv/spv-wallet/engine/datastore" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/pkg/errors" ) @@ -13,13 +14,13 @@ func Save(ctx context.Context, model ModelInterface) (err error) { // Check for a client c := model.Client() if c == nil { - return ErrMissingClient + return spverrors.ErrMissingClient } // Check for a datastore ds := c.Datastore() if ds == nil { - return ErrDatastoreRequired + return spverrors.ErrDatastoreRequired } // Create new Datastore transaction // @siggi: we need this to be in a callback context for Mongo diff --git a/engine/model_sync_transactions.go b/engine/model_sync_transactions.go index f7cf274c..3d774759 100644 --- a/engine/model_sync_transactions.go +++ b/engine/model_sync_transactions.go @@ -4,6 +4,7 @@ import ( "context" "github.com/bitcoin-sv/spv-wallet/engine/datastore" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" ) // SyncTransaction is an object representing the chain-state sync configuration and results for a given transaction @@ -88,7 +89,7 @@ func (m *SyncTransaction) BeforeCreating(_ context.Context) error { // Make sure ID is valid if len(m.ID) == 0 { - return ErrMissingFieldID + return spverrors.ErrMissingFieldID } m.Client().Logger().Debug(). diff --git a/engine/model_transaction_config.go b/engine/model_transaction_config.go index 11b59b55..e6392681 100644 --- a/engine/model_transaction_config.go +++ b/engine/model_transaction_config.go @@ -11,6 +11,7 @@ import ( "time" "github.com/bitcoin-sv/go-paymail" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" magic "github.com/bitcoinschema/go-map" "github.com/libsv/go-bt/v2/bscript" @@ -178,12 +179,12 @@ func (t *TransactionOutput) processOutput(ctx context.Context, cacheStore caches // Check for Paymail, Bitcoin Address or OP Return if len(t.To) > 0 && strings.Contains(t.To, "@") { // Paymail output if checkSatoshis && t.Satoshis <= 0 { - return ErrOutputValueTooLow + return spverrors.ErrOutputValueTooLow } return t.processPaymailOutput(ctx, cacheStore, paymailClient, defaultFromSender) } else if len(t.To) > 0 { // Standard Bitcoin Address if checkSatoshis && t.Satoshis <= 0 { - return ErrOutputValueTooLow + return spverrors.ErrOutputValueTooLow } return t.processAddressOutput() } else if t.OpReturn != nil { // OP_RETURN output @@ -193,7 +194,7 @@ func (t *TransactionOutput) processOutput(ctx context.Context, cacheStore caches } // No value set in either ToPaymail or ToAddress - return ErrOutputValueNotRecognized + return spverrors.ErrOutputValueNotRecognized } // processPaymailOutput will detect how to process the Paymail output given @@ -203,7 +204,7 @@ func (t *TransactionOutput) processPaymailOutput(ctx context.Context, cacheStore // Standardize the paymail address (break into parts) alias, domain, paymailAddress := paymail.SanitizePaymail(t.To) if len(paymailAddress) == 0 { - return ErrPaymailAddressIsInvalid + return spverrors.ErrPaymailAddressIsInvalid } // Set the sanitized version of the paymail address provided @@ -309,7 +310,7 @@ func (t *TransactionOutput) processAddressOutput() (err error) { // processScriptOutput will process a custom bitcoin script output func (t *TransactionOutput) processScriptOutput() (err error) { if t.Script == "" { - return ErrInvalidScriptOutput + return spverrors.ErrInvalidScriptOutput } // check whether go-bt parses the script correctly @@ -392,7 +393,7 @@ func (t *TransactionOutput) processOpReturnOutput() (err error) { } script = s.String() } else { - return ErrInvalidOpReturnOutput + return spverrors.ErrInvalidOpReturnOutput } // Append the script diff --git a/engine/model_transaction_config_test.go b/engine/model_transaction_config_test.go index 64a88033..233074f1 100644 --- a/engine/model_transaction_config_test.go +++ b/engine/model_transaction_config_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/bitcoin-sv/spv-wallet/engine/chainstate" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" magic "github.com/bitcoinschema/go-map" "github.com/rs/zerolog" @@ -170,7 +171,7 @@ func TestTransactionConfig_processOutput(t *testing.T) { defaultSenderPaymail, true, ) require.Error(t, err) - assert.ErrorIs(t, err, ErrOutputValueNotRecognized) + assert.ErrorIs(t, err, spverrors.ErrOutputValueNotRecognized) }) t.Run("error - invalid paymail given", func(t *testing.T) { @@ -186,7 +187,7 @@ func TestTransactionConfig_processOutput(t *testing.T) { defaultSenderPaymail, true, ) require.Error(t, err) - assert.ErrorIs(t, err, ErrPaymailAddressIsInvalid) + assert.ErrorIs(t, err, spverrors.ErrPaymailAddressIsInvalid) }) t.Run("basic paymail address resolution - valid response", func(t *testing.T) { @@ -332,7 +333,7 @@ func TestTransactionConfig_processOpReturnOutput(t *testing.T) { OpReturn: &OpReturn{}, } err := output.processOpReturnOutput() - require.ErrorIs(t, err, ErrInvalidOpReturnOutput) + require.ErrorIs(t, err, spverrors.ErrInvalidOpReturnOutput) }) t.Run("op_return hex", func(t *testing.T) { @@ -442,7 +443,7 @@ func TestTransactionConfig_processScriptOutput(t *testing.T) { Script: script, } err := output.processScriptOutput() - require.ErrorIs(t, err, ErrInvalidScriptOutput) + require.ErrorIs(t, err, spverrors.ErrInvalidScriptOutput) }) t.Run("invalid hex", func(t *testing.T) { diff --git a/engine/model_transactions.go b/engine/model_transactions.go index 2db1259a..c834f090 100644 --- a/engine/model_transactions.go +++ b/engine/model_transactions.go @@ -4,6 +4,7 @@ import ( "context" "github.com/bitcoin-sv/spv-wallet/engine/chainstate" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/libsv/go-bt/v2" ) @@ -147,7 +148,7 @@ func (m *Transaction) setXPubID() { // UpdateTransactionMetadata will update the transaction metadata by xPubID func (m *Transaction) UpdateTransactionMetadata(xPubID string, metadata Metadata) error { if xPubID == "" { - return ErrXpubIDMisMatch + return spverrors.ErrXpubIDMisMatch } // transaction metadata is saved per xPubID diff --git a/engine/model_transactions_test.go b/engine/model_transactions_test.go index 1ea81902..d4d2ec04 100644 --- a/engine/model_transactions_test.go +++ b/engine/model_transactions_test.go @@ -7,6 +7,7 @@ import ( "github.com/bitcoin-sv/spv-wallet/engine/datastore" customTypes "github.com/bitcoin-sv/spv-wallet/engine/datastore/customtypes" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/libsv/go-bt/v2" "github.com/libsv/go-bt/v2/bscript" @@ -449,7 +450,7 @@ func TestTransaction_processInputs(t *testing.T) { ctx := context.Background() err = transaction._processInputs(ctx) - require.ErrorIs(t, err, ErrUtxoAlreadySpent) + require.ErrorIs(t, err, spverrors.ErrUtxoAlreadySpent) }) t.Run("not reserved utxo", func(t *testing.T) { @@ -477,7 +478,7 @@ func TestTransaction_processInputs(t *testing.T) { ctx := context.Background() err = transaction._processInputs(ctx) - require.ErrorIs(t, err, ErrUtxoNotReserved) + require.ErrorIs(t, err, spverrors.ErrUtxoNotReserved) }) t.Run("incorrect reservation ID of utxo", func(t *testing.T) { @@ -509,7 +510,7 @@ func TestTransaction_processInputs(t *testing.T) { ctx := context.Background() err = transaction._processInputs(ctx) - require.ErrorIs(t, err, ErrDraftIDMismatch) + require.ErrorIs(t, err, spverrors.ErrDraftIDMismatch) }) t.Run("inputUtxoChecksOff", func(t *testing.T) { diff --git a/engine/model_utxos.go b/engine/model_utxos.go index 1372f711..70f51fff 100644 --- a/engine/model_utxos.go +++ b/engine/model_utxos.go @@ -7,6 +7,7 @@ import ( "github.com/bitcoin-sv/spv-wallet/engine/datastore" customTypes "github.com/bitcoin-sv/spv-wallet/engine/datastore/customtypes" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/pkg/errors" ) @@ -75,10 +76,10 @@ func getSpendableUtxos(ctx context.Context, xPubID, utxoType string, queryParams if err != nil { return nil, err } else if utxo == nil { - return nil, ErrMissingUtxo + return nil, spverrors.ErrCouldNotFindUtxo } if utxo.XpubID != xPubID || utxo.SpendingTxID.Valid { - return nil, ErrUtxoAlreadySpent + return nil, spverrors.ErrUtxoAlreadySpent } models = append(models, *utxo) } @@ -97,7 +98,7 @@ func getSpendableUtxos(ctx context.Context, xPubID, utxoType string, queryParams // No utxos found? if len(models) == 0 { - return nil, ErrMissingUTXOsSpendable + return nil, spverrors.ErrMissingUTXOsSpendable } // Loop and enrich @@ -228,16 +229,16 @@ reserveUtxoLoop: if err = unReserveUtxos( ctx, xPubID, draftID, m.GetOptions(false)..., ); err != nil { - return nil, errors.Wrap(err, ErrNotEnoughUtxos.Error()) + return nil, spverrors.ErrNotEnoughUtxos } - return nil, ErrNotEnoughUtxos + return nil, spverrors.ErrNotEnoughUtxos } // check whether an utxo was used twice, this is not valid usedUtxos := make([]string, 0) for _, utxo := range *utxos { if utils.StringInSlice(utxo.ID, usedUtxos) { - return nil, ErrDuplicateUTXOs + return nil, spverrors.ErrDuplicateUTXOs } usedUtxos = append(usedUtxos, utxo.ID) } @@ -395,15 +396,15 @@ func (m *Utxo) BeforeCreating(_ context.Context) error { // Test for required field(s) if len(m.ScriptPubKey) == 0 { - return ErrMissingFieldScriptPubKey + return spverrors.ErrMissingFieldScriptPubKey } else if m.Satoshis == 0 { - return ErrMissingFieldSatoshis + return spverrors.ErrMissingFieldSatoshis } else if len(m.TransactionID) == 0 { - return ErrMissingFieldTransactionID + return spverrors.ErrMissingFieldTransactionID } if len(m.XpubID) == 0 { - return ErrMissingFieldXpubID + return spverrors.ErrMissingFieldXpubID } // Set the new pointer? diff --git a/engine/model_utxos_test.go b/engine/model_utxos_test.go index aea8248f..f9cfa796 100644 --- a/engine/model_utxos_test.go +++ b/engine/model_utxos_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/bitcoin-sv/spv-wallet/engine/datastore" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -224,7 +225,7 @@ func TestUtxo_ReserveUtxos(t *testing.T) { require.NoError(t, err) _, err = reserveUtxos(ctx, testXPubID, testDraftID2, 20000, 0.5, nil, client.DefaultModelOptions()...) - require.Error(t, err, ErrNotEnoughUtxos) + require.Error(t, err, spverrors.ErrNotEnoughUtxos) }) t.Run("reserve fromUtxos", func(t *testing.T) { @@ -287,7 +288,7 @@ func TestUtxo_ReserveUtxos(t *testing.T) { OutputIndex: 16, }} _, err = reserveUtxos(ctx, testXPubID, testDraftID2, 2000, 0.5, fromUtxos, client.DefaultModelOptions()...) - require.Error(t, err, ErrNotEnoughUtxos) + require.Error(t, err, spverrors.ErrNotEnoughUtxos) }) t.Run("reserve utxos paginated", func(t *testing.T) { @@ -320,7 +321,7 @@ func TestUtxo_ReserveUtxos(t *testing.T) { }} _, err = reserveUtxos(ctx, testXPubID, testDraftID2, 2200, 0.05, fromUtxos, client.DefaultModelOptions()...) - require.ErrorIs(t, err, ErrDuplicateUTXOs) + require.ErrorIs(t, err, spverrors.ErrDuplicateUTXOs) }) } @@ -402,7 +403,7 @@ func TestUtxo_Save(t *testing.T) { defer deferMe() _utxo := newUtxo("", "", "", 0, 0, append(client.DefaultModelOptions(), New())...) err := _utxo.Save(ctx) - assert.ErrorIs(t, err, ErrMissingFieldScriptPubKey) + assert.ErrorIs(t, err, spverrors.ErrMissingFieldScriptPubKey) }) t.Run("Save", func(t *testing.T) { diff --git a/engine/model_xpubs.go b/engine/model_xpubs.go index 3f197a42..bfcca4b2 100644 --- a/engine/model_xpubs.go +++ b/engine/model_xpubs.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/bitcoin-sv/spv-wallet/engine/datastore" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" ) @@ -84,7 +85,7 @@ func getXpubWithCache(ctx context.Context, client ClientInterface, xPubID = utils.Hash(key) opts = append(opts, WithXPub(key)) // Add the xPub option which will set it on the model } else if len(xPubID) == 0 { - return nil, ErrMissingFieldXpubID + return nil, spverrors.ErrMissingFieldXpubID } cacheKey := fmt.Sprintf(cacheKeyXpubModel, xPubID) @@ -108,7 +109,7 @@ func getXpubWithCache(ctx context.Context, client ClientInterface, ); err != nil { return nil, err } else if xPub == nil { - return nil, ErrMissingXpub + return nil, spverrors.ErrCouldNotFindXpub } // Save to cache @@ -170,7 +171,7 @@ func (m *Xpub) getNewDestination(ctx context.Context, chain uint32, destinationT // Check the type // todo: support more types of destinations if destinationType != utils.ScriptTypePubKeyHash { - return nil, ErrUnsupportedDestinationType + return nil, spverrors.ErrUnsupportedDestinationType } // Increment the next num @@ -286,7 +287,7 @@ func (m *Xpub) BeforeCreating(_ context.Context) error { // Make sure we have an ID if len(m.ID) == 0 { - return ErrMissingFieldID + return spverrors.ErrMissingFieldID } m.Client().Logger().Debug(). diff --git a/engine/model_xpubs_test.go b/engine/model_xpubs_test.go index f66f293d..01593286 100644 --- a/engine/model_xpubs_test.go +++ b/engine/model_xpubs_test.go @@ -6,6 +6,7 @@ import ( "github.com/DATA-DOG/go-sqlmock" "github.com/bitcoin-sv/spv-wallet/engine/datastore" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/tester" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/mrz1836/go-cache" @@ -90,7 +91,7 @@ func TestXpub_getNewDestination(t *testing.T) { "test-key": "test-value", } _, err = xPub.getNewDestination(ctx, utils.ChainInternal, utils.ScriptTypePubKeyHash, append(client.DefaultModelOptions(), WithMetadatas(metaData))...) - assert.ErrorIs(t, utils.ErrXpubInvalidLength, err) + assert.ErrorIs(t, spverrors.ErrXpubInvalidLength, err) }) t.Run("new internal destination", func(t *testing.T) { diff --git a/engine/models_internal.go b/engine/models_internal.go index 671485dd..08cae7bf 100644 --- a/engine/models_internal.go +++ b/engine/models_internal.go @@ -5,6 +5,7 @@ import ( "time" "github.com/bitcoin-sv/spv-wallet/engine/notifications" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" ) // AfterDeleted will fire after a successful delete in the Datastore @@ -143,7 +144,7 @@ func incrementField(ctx context.Context, model ModelInterface, fieldName string, // Check for client c := model.Client() if c == nil { - return 0, ErrMissingClient + return 0, spverrors.ErrMissingClient } // Increment diff --git a/engine/paymail_servant.go b/engine/paymail_servant.go index d24de003..e3642320 100644 --- a/engine/paymail_servant.go +++ b/engine/paymail_servant.go @@ -2,18 +2,12 @@ package engine import ( "context" - "errors" - "fmt" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/go-paymail" "github.com/mrz1836/go-cachestore" ) -var ( - ErrCapabilitiesPkiUnsupported = errors.New("server doesn't support PKI") - ErrCapabilitiesPikeUnsupported = errors.New("server doesn't support PIKE") -) - type PaymailServant struct { cs cachestore.ClientInterface pc paymail.ClientInterface @@ -24,26 +18,26 @@ func (s *PaymailServant) GetSanitizedPaymail(addr string) (*paymail.SanitisedPay return nil, err } - sanitised := &paymail.SanitisedPaymail{} - sanitised.Alias, sanitised.Domain, sanitised.Address = paymail.SanitizePaymail(addr) + sanitized := &paymail.SanitisedPaymail{} + sanitized.Alias, sanitized.Domain, sanitized.Address = paymail.SanitizePaymail(addr) - return sanitised, nil + return sanitized, nil } func (s *PaymailServant) GetPkiForPaymail(ctx context.Context, sPaymail *paymail.SanitisedPaymail) (*paymail.PKIResponse, error) { capabilities, err := getCapabilities(ctx, s.cs, s.pc, sPaymail.Domain) if err != nil { - return nil, fmt.Errorf("failed to get paymail capability: %w", err) + return nil, spverrors.ErrGetCapabilities } if !capabilities.Has(paymail.BRFCPki, paymail.BRFCPkiAlternate) { - return nil, ErrCapabilitiesPkiUnsupported + return nil, spverrors.ErrCapabilitiesPkiUnsupported } url := capabilities.GetString(paymail.BRFCPki, paymail.BRFCPkiAlternate) pki, err := s.pc.GetPKI(url, sPaymail.Alias, sPaymail.Domain) if err != nil { - return nil, fmt.Errorf("error getting PKI: %w", err) + return nil, err } return pki, nil @@ -52,17 +46,17 @@ func (s *PaymailServant) GetPkiForPaymail(ctx context.Context, sPaymail *paymail func (s *PaymailServant) AddContactRequest(ctx context.Context, receiverPaymail *paymail.SanitisedPaymail, contactData *paymail.PikeContactRequestPayload) (*paymail.PikeContactRequestResponse, error) { capabilities, err := getCapabilities(ctx, s.cs, s.pc, receiverPaymail.Domain) if err != nil { - return nil, fmt.Errorf("failed to get paymail capability: %w", err) + return nil, spverrors.ErrGetCapabilities } if !capabilities.Has(paymail.BRFCPike, "") { - return nil, ErrCapabilitiesPikeUnsupported + return nil, spverrors.ErrCapabilitiesPikeUnsupported } url := capabilities.ExtractPikeInviteURL() response, err := s.pc.AddContactRequest(url, receiverPaymail.Alias, receiverPaymail.Domain, contactData) if err != nil { - return nil, fmt.Errorf("error during requesting new contact: %w", err) + return nil, err } return response, nil diff --git a/engine/paymail_service_provider.go b/engine/paymail_service_provider.go index 51975664..97192260 100644 --- a/engine/paymail_service_provider.go +++ b/engine/paymail_service_provider.go @@ -9,8 +9,8 @@ import ( "github.com/bitcoin-sv/go-paymail/beef" "github.com/bitcoin-sv/go-paymail/server" "github.com/bitcoin-sv/go-paymail/spv" - "github.com/bitcoin-sv/spv-wallet/engine/chainstate" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/bitcoinschema/go-bitcoin/v2" "github.com/libsv/go-bk/bec" @@ -58,7 +58,7 @@ func (p *PaymailDefaultServiceProvider) GetPaymailByAlias( return nil, err } if pm == nil { - return nil, ErrPaymailNotFound + return nil, spverrors.ErrCouldNotFindPaymail } pk, err := pm.GetPubKey() @@ -219,7 +219,7 @@ func (p *PaymailDefaultServiceProvider) AddContact( return } if reqPaymail == nil { - err = ErrInvalidRequesterXpub + err = spverrors.ErrInvalidRequesterXpub return } @@ -233,7 +233,7 @@ func (p *PaymailDefaultServiceProvider) getDestinationForPaymail(ctx context.Con return nil, err } if pm == nil { - return nil, ErrPaymailNotFound + return nil, spverrors.ErrCouldNotFindPaymail } dst, err := createDestination( diff --git a/engine/pike/example_test.go b/engine/pike/example_test.go index 90461c1d..b0f89aad 100644 --- a/engine/pike/example_test.go +++ b/engine/pike/example_test.go @@ -4,9 +4,8 @@ import ( "encoding/hex" "fmt" - "github.com/libsv/go-bk/bec" - "github.com/bitcoin-sv/spv-wallet/engine/pike" + "github.com/libsv/go-bk/bec" ) func Example_generateLockingScripts() { diff --git a/engine/pike/pike.go b/engine/pike/pike.go index a1b99e03..a508b5a1 100644 --- a/engine/pike/pike.go +++ b/engine/pike/pike.go @@ -16,11 +16,11 @@ package pike import ( "fmt" - "github.com/libsv/go-bk/bec" - "github.com/libsv/go-bt/v2/bscript" "github.com/bitcoin-sv/spv-wallet/engine/script/template" "github.com/bitcoin-sv/spv-wallet/engine/types/type42" + "github.com/libsv/go-bk/bec" + "github.com/libsv/go-bt/v2/bscript" ) // GenerateOutputsTemplate creates a Pike output template diff --git a/engine/pike/pike_test.go b/engine/pike/pike_test.go index ec678aa4..59ece394 100644 --- a/engine/pike/pike_test.go +++ b/engine/pike/pike_test.go @@ -4,10 +4,9 @@ import ( "encoding/hex" "testing" + "github.com/bitcoin-sv/spv-wallet/engine/script/template" "github.com/libsv/go-bk/bec" assert "github.com/stretchr/testify/require" - - "github.com/bitcoin-sv/spv-wallet/engine/script/template" ) func TestGenerateLockingScriptsFromTemplates(t *testing.T) { diff --git a/engine/pike_service_provider.go b/engine/pike_service_provider.go index 1800e539..9c9b01f4 100644 --- a/engine/pike_service_provider.go +++ b/engine/pike_service_provider.go @@ -3,10 +3,12 @@ package engine import ( "context" "encoding/hex" + "github.com/bitcoin-sv/go-paymail" "github.com/bitcoin-sv/go-paymail/server" "github.com/bitcoin-sv/spv-wallet/engine/pike" "github.com/bitcoin-sv/spv-wallet/engine/script/template" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/libsv/go-bk/bec" ) @@ -45,7 +47,7 @@ func (p *PikeContactServiceProvider) AddContact( return } if reqPaymail == nil { - err = ErrInvalidRequesterXpub + err = spverrors.ErrInvalidRequesterXpub return } diff --git a/engine/record_tx_strategy_external_incoming_tx.go b/engine/record_tx_strategy_external_incoming_tx.go index 27aa65fa..3d3fe899 100644 --- a/engine/record_tx_strategy_external_incoming_tx.go +++ b/engine/record_tx_strategy_external_incoming_tx.go @@ -23,7 +23,8 @@ func (strategy *externalIncomingTx) Execute(ctx context.Context, c ClientInterfa logger := c.Logger() transaction, err := _createExternalTxToRecord(ctx, strategy, c, opts) if err != nil { - return nil, fmt.Errorf("creation of external incoming tx failed. Reason: %w", err) + logger.Error().Msgf("creation of external incoming tx failed. Reason: %v", err) + return nil, err } logger.Info(). @@ -38,13 +39,16 @@ func (strategy *externalIncomingTx) Execute(ctx context.Context, c ClientInterfa Str("txID", transaction.ID). Msgf("broadcasting failed, transaction rejected! Reason: %s", err) - return nil, fmt.Errorf("broadcasting failed, transaction rejected! Reason: %w", err) + return nil, err } } // record if err = transaction.Save(ctx); err != nil { - return nil, fmt.Errorf("saving of Transaction failed. Reason: %w", err) + logger.Error(). + Str("txID", transaction.ID). + Msgf("saving of Transaction failed. Reason: %v", err) + return nil, err } logger.Info(). diff --git a/engine/record_tx_strategy_internal_incoming_tx.go b/engine/record_tx_strategy_internal_incoming_tx.go index 15e256b8..605b2991 100644 --- a/engine/record_tx_strategy_internal_incoming_tx.go +++ b/engine/record_tx_strategy_internal_incoming_tx.go @@ -2,8 +2,8 @@ package engine import ( "context" - "errors" "fmt" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/libsv/go-bt/v2" "github.com/rs/zerolog" @@ -28,8 +28,10 @@ func (strategy *internalIncomingTx) Execute(ctx context.Context, c ClientInterfa // process transaction := strategy.Tx syncTx, err := GetSyncTransactionByID(ctx, transaction.ID, transaction.GetOptions(false)...) - if err != nil || syncTx == nil { - return nil, fmt.Errorf("getting syncTx failed. Reason: %w", err) + if err != nil { + return nil, err + } else if syncTx == nil { + return nil, spverrors.ErrCouldNotFindSyncTx } if strategy.broadcastNow || syncTx.BroadcastStatus == SyncStatusReady { @@ -42,7 +44,7 @@ func (strategy *internalIncomingTx) Execute(ctx context.Context, c ClientInterfa Str("txID", transaction.ID). Msgf("broadcasting failed, transaction rejected! Reason: %s", err) - return nil, fmt.Errorf("broadcasting failed, transaction rejected! Reason: %w", err) + return nil, err } } @@ -54,11 +56,11 @@ func (strategy *internalIncomingTx) Execute(ctx context.Context, c ClientInterfa func (strategy *internalIncomingTx) Validate() error { if strategy.Tx == nil { - return errors.New("tx cannot be nil") + return spverrors.ErrEmptyTx } if _, err := bt.NewTxFromString(strategy.Tx.Hex); err != nil { - return fmt.Errorf("invalid hex: %w", err) + return spverrors.ErrInvalidHex } return nil // is valid diff --git a/engine/record_tx_strategy_outgoing_tx.go b/engine/record_tx_strategy_outgoing_tx.go index bb67a7a5..d78de1b0 100644 --- a/engine/record_tx_strategy_outgoing_tx.go +++ b/engine/record_tx_strategy_outgoing_tx.go @@ -2,9 +2,9 @@ package engine import ( "context" - "errors" "fmt" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/libsv/go-bt/v2" "github.com/rs/zerolog" ) @@ -29,11 +29,17 @@ func (strategy *outgoingTx) Execute(ctx context.Context, c ClientInterface, opts // create transaction, err := _createOutgoingTxToRecord(ctx, strategy, c, opts) if err != nil { - return nil, fmt.Errorf("creation of outgoing tx failed. Reason: %w", err) + logger.Error(). + Str("txID", strategy.TxID()). + Msgf("creation of outgoing tx failed. Reason: %v", err) + return nil, spverrors.ErrCreateOutgoingTxFailed } if err = transaction.Save(ctx); err != nil { - return nil, fmt.Errorf("saving of Transaction failed. Reason: %w", err) + logger.Error(). + Str("txID", strategy.TxID()). + Msgf("saving of Transaction failed. Reason: %v", err) + return nil, spverrors.ErrDuringSaveTx } // process @@ -47,7 +53,7 @@ func (strategy *outgoingTx) Execute(ctx context.Context, c ClientInterface, opts if revertErr := c.RevertTransaction(ctx, transaction.ID); revertErr != nil { logger.Error(). Str("txID", transaction.ID). - Msgf("FATAL! Reverting transaction after failed P2P notification failed. Reason: %s", err) + Msgf("FATAL! Reverting transaction after failed P2P notification failed. Reason: %s", revertErr) } return nil, err @@ -57,7 +63,10 @@ func (strategy *outgoingTx) Execute(ctx context.Context, c ClientInterface, opts // get newest syncTx from DB - if it's an internal tx it could be broadcasted by us already syncTx, err := GetSyncTransactionByID(ctx, transaction.ID, transaction.GetOptions(false)...) if err != nil || syncTx == nil { - return nil, fmt.Errorf("getting syncTx failed. Reason: %w", err) + logger.Error(). + Str("txID", transaction.ID). + Msgf("getting syncTx failed. Reason: %v", err) + return nil, spverrors.ErrCouldNotFindSyncTx } if syncTx.BroadcastStatus == SyncStatusReady { @@ -77,11 +86,11 @@ func (strategy *outgoingTx) Validate() error { } if strategy.RelatedDraftID == "" { - return errors.New("empty RelatedDraftID") + return spverrors.ErrEmptyRelatedDraftId } if strategy.XPubKey == "" { - return errors.New("empty xPubKey") + return spverrors.ErrEmptyXpubKey } return nil // is valid @@ -125,11 +134,11 @@ func _hydrateOutgoingWithDraft(ctx context.Context, tx *Transaction) error { } if draft == nil { - return ErrDraftNotFound + return spverrors.ErrCouldNotFindDraftTx } if len(draft.Configuration.Outputs) == 0 { - return errors.New("corresponding draft transaction has no outputs") + return spverrors.ErrDraftTxHasNoOutputs } if draft.Configuration.Sync == nil { @@ -199,7 +208,7 @@ func _outgoingNotifyP2p(ctx context.Context, logger *zerolog.Logger, tx *Transac Str("txID", tx.ID). Msgf("processP2PTransaction failed. Reason: %s", err) - return err + return spverrors.ErrProcessP2PTx } logger.Info(). diff --git a/engine/spv_wallet_engine_suite_test.go b/engine/spv_wallet_engine_suite_test.go index da640873..76f637cd 100644 --- a/engine/spv_wallet_engine_suite_test.go +++ b/engine/spv_wallet_engine_suite_test.go @@ -6,14 +6,13 @@ import ( "testing" "time" - "github.com/rs/zerolog" - "github.com/DATA-DOG/go-sqlmock" broadcast_client_mock "github.com/bitcoin-sv/go-broadcast-client/broadcast/broadcast-client-mock" "github.com/bitcoin-sv/spv-wallet/engine/datastore" "github.com/bitcoin-sv/spv-wallet/engine/taskmanager" "github.com/bitcoin-sv/spv-wallet/engine/tester" embeddedPostgres "github.com/fergusstrange/embedded-postgres" + "github.com/rs/zerolog" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) diff --git a/engine/spverrors/definitions.go b/engine/spverrors/definitions.go new file mode 100644 index 00000000..27f09ec4 --- /dev/null +++ b/engine/spverrors/definitions.go @@ -0,0 +1,333 @@ +package spverrors + +import "github.com/bitcoin-sv/spv-wallet/models" + +// How the Codes are generated? +// 1. "error" - like mandatory prefix for all error codes +// 2. (optional) {error group} - e.g. "unauthorized", "bind" +// 3. (optional) {subject} - name of model (with or without specific field) or some noun e.g. "body", "auth-header", "transaction", "paymail-address" +// 4. (optional) {reason} - what happened, e.g. "not-found", "missing", "invalid" + +//////////////////////////////////// AUTHORIZATION ERRORS + +// ErrAuthorization is basic auth error +var ErrAuthorization = models.SPVError{Message: "unauthorized", StatusCode: 401, Code: "error-unauthorized"} + +// ErrMissingAuthHeader is when request does not have auth header +var ErrMissingAuthHeader = models.SPVError{Message: "missing auth header", StatusCode: 401, Code: "error-unauthorized-auth-header-missing"} + +// ErrNotAnAdminKey is when xpub from auth header is not an admin key +var ErrNotAnAdminKey = models.SPVError{Message: "xpub provided is not an admin key", StatusCode: 401, Code: "error-unauthorized-xpub-not-an-admin-key"} + +// ErrMissingBody is when request is missing body +var ErrMissingBody = models.SPVError{Message: "missing body", StatusCode: 401, Code: "error-unauthorized-body-missing"} + +// ErrInvalidOrMissingToken is when callback token from headers is invalid or missing +var ErrInvalidOrMissingToken = models.SPVError{Message: "invalid or missing bearer token", StatusCode: 401, Code: "error-unauthorized-token-invalid-or-missing"} + +// ErrInvalidToken is when callback token from headers is invalid +var ErrInvalidToken = models.SPVError{Message: "invalid authorization token", StatusCode: 401, Code: "error-unauthorized-token-invalid"} + +// ErrInvalidSignature is when signature is invalid +var ErrInvalidSignature = models.SPVError{Message: "invalid signature", StatusCode: 401, Code: "error-unauthorized-signature-invalid"} + +// ErrMissingSignature is when signature is missing in authorization process +var ErrMissingSignature = models.SPVError{Message: "missing signature", StatusCode: 401, Code: "error-unauthorized-signature-missing"} + +// ErrHashesDoNotMatch is when two hashes do not match +var ErrHashesDoNotMatch = models.SPVError{Message: "auth hash and body hash do not match", StatusCode: 401, Code: "error-unauthorized-hashes-do-not-match"} + +// ErrSignatureExpired is when given signature is expired +var ErrSignatureExpired = models.SPVError{Message: "signature has expired", StatusCode: 401, Code: "error-unauthorized-signature-expired"} + +// ErrGettingHdKeyFromXpub is when error occurred during getting hd key from xpub +var ErrGettingHdKeyFromXpub = models.SPVError{Message: "error getting hd key from xpub", StatusCode: 401, Code: "error-unauthorized-xpub-failed-to-get-from-hd-key"} + +// ErrDeriveChildKey is when error occurred during deriving child key +var ErrDeriveChildKey = models.SPVError{Message: "error deriving child key", StatusCode: 401, Code: "error-unauthorized-derive-child-key"} + +// ErrGettingAddressFromHdKey is when error occurred during getting address from hd key +var ErrGettingAddressFromHdKey = models.SPVError{Message: "error getting address from hd key", StatusCode: 401, Code: "error-unauthorized-address-failed-to-get-from-hd-key"} + +// ErrGettingAddressFromPublicKey is when error occurred during getting address from public key +var ErrGettingAddressFromPublicKey = models.SPVError{Message: "error getting address from public key", StatusCode: 401, Code: "error-unauthorized-address-failed-to-get-from-public-key"} + +// ErrValidateXPub is when validation xpub +var ErrValidateXPub = models.SPVError{Message: "error validation xpub", StatusCode: 401, Code: "error-unauthorized-xpub-invalid"} + +//////////////////////////////////// BINDING ERRORS + +// ErrCannotBindRequest is when request body cannot be bind into struct +var ErrCannotBindRequest = models.SPVError{Message: "cannot bind request body", StatusCode: 400, Code: "error-bind-body-invalid"} + +// ErrInvalidConditions is when request has invalid conditions +var ErrInvalidConditions = models.SPVError{Message: "invalid conditions", StatusCode: 400, Code: "error-bind-conditions-invalid"} + +//////////////////////////////////// ACCESS KEY ERRORS + +// ErrCouldNotFindAccessKey is when could not find xpub +var ErrCouldNotFindAccessKey = models.SPVError{Message: "access key not found", StatusCode: 404, Code: "error-access-key-not-found"} + +// ErrAccessKeyRevoked is when the access key has been revoked +var ErrAccessKeyRevoked = models.SPVError{Message: "access key has been revoked", StatusCode: 400, Code: "error-access-key-revoked"} + +//////////////////////////////////// DESTINATION ERRORS + +// ErrCouldNotFindDestination is an error when a destination could not be found +var ErrCouldNotFindDestination = models.SPVError{Message: "destination not found", StatusCode: 404, Code: "error-destination-not-found"} + +// ErrUnsupportedDestinationType is a destination type that is not currently supported +var ErrUnsupportedDestinationType = models.SPVError{Message: "unsupported destination type", StatusCode: 400, Code: "error-destination-unsupported-type"} + +// ErrUnknownLockingScript is when the field is unknown +var ErrUnknownLockingScript = models.SPVError{Message: "could not recognize locking script", StatusCode: 400, Code: "error-destination-unknown-locking-script"} + +//////////////////////////////////// CONTACT ERRORS + +// ErrContactNotFound is when contact cannot be found +var ErrContactNotFound = models.SPVError{Message: "contact not found", StatusCode: 404, Code: "error-contact-not-found"} + +// ErrInvalidRequesterXpub is when requester xpub is not connected with given paymail +var ErrInvalidRequesterXpub = models.SPVError{Message: "invalid requester xpub", StatusCode: 400, Code: "error-contact-invalid-requester-xpub"} + +// ErrAddingContactRequest is when error occurred while adding contact +var ErrAddingContactRequest = models.SPVError{Message: "adding contact request failed", StatusCode: 500, Code: "error-contact-request-failed"} + +// ErrMoreThanOnePaymailRegistered is when user who want to add contact has more than one paymail address +var ErrMoreThanOnePaymailRegistered = models.SPVError{Message: "there are more than one paymail assigned to the xpub", StatusCode: 400, Code: "error-contact-more-than-one-paymail-registered"} + +// ErrContactIncorrectStatus is when contact is in incorrect status to make a change +var ErrContactIncorrectStatus = models.SPVError{Message: "contact is in incorrect status to proceed", StatusCode: 400, Code: "error-contact-status-incorrect"} + +// ErrMissingContactID is when id is missing in contact +var ErrMissingContactID = models.SPVError{Message: "missing id in contact", StatusCode: 400, Code: "error-contact-id-missing"} + +// ErrMissingContactFullName is when full name is missing in contact +var ErrMissingContactFullName = models.SPVError{Message: "missing full name in contact", StatusCode: 400, Code: "error-contact-full-name-missing"} + +// ErrMissingContactPaymail is when paymail is missing in contact +var ErrMissingContactPaymail = models.SPVError{Message: "missing paymail in contact", StatusCode: 400, Code: "error-contact-paymail-missing"} + +// ErrMissingContactXPubKey is when XPubKey is missing in contact +var ErrMissingContactXPubKey = models.SPVError{Message: "missing pubKey in contact", StatusCode: 400, Code: "error-contact-xpub-missing"} + +// ErrMissingContactStatus is when status is missing in contact +var ErrMissingContactStatus = models.SPVError{Message: "status is required", StatusCode: 400, Code: "error-contact-status-missing"} + +// ErrMissingContactOwnerXPubId is when owner XPubId is missing in contact +var ErrMissingContactOwnerXPubId = models.SPVError{Message: "contact must have owner", StatusCode: 400, Code: "error-contact-owner-xpub-id-missing"} + +// ErrRequestedContactInvalid is when the requested contact is invalid +var ErrRequestedContactInvalid = models.SPVError{Message: "requested contact paymail is invalid", StatusCode: 400, Code: "error-contact-requested-contact-invalid"} + +// ErrGettingPKIFailed is when getting PKI for contact paymail failed +var ErrGettingPKIFailed = models.SPVError{Message: "getting PKI for contact failed", StatusCode: 400, Code: "error-contact-getting-pki-failed"} + +// ErrSaveContact is when saving new contact failed +var ErrSaveContact = models.SPVError{Message: "adding contact failed", StatusCode: 400, Code: "error-contact-adding-contact-failed"} + +//////////////////////////////////// PAYMAIL ERRORS + +// ErrCouldNotFindPaymail is when paymail could not be found +var ErrCouldNotFindPaymail = models.SPVError{Message: "paymail not found", StatusCode: 404, Code: "error-paymail-not-found"} + +// ErrPaymailAddressIsInvalid is when the paymail address is NOT alias@domain.com +var ErrPaymailAddressIsInvalid = models.SPVError{Message: "paymail address is invalid", StatusCode: 400, Code: "error-paymail-address-invalid"} + +// ErrMissingPaymailID is when id is missing in paymail +var ErrMissingPaymailID = models.SPVError{Message: "missing id in paymail", StatusCode: 400, Code: "error-paymail-id-missing"} + +// ErrMissingPaymailAddress is when alias is missing in paymail +var ErrMissingPaymailAddress = models.SPVError{Message: "missing alias in paymail", StatusCode: 400, Code: "error-paymail-address-missing"} + +// ErrMissingPaymailDomain is when domain is missing in paymail +var ErrMissingPaymailDomain = models.SPVError{Message: "missing domain in paymail", StatusCode: 400, Code: "error-paymail-domain-missing"} + +// ErrMissingPaymailExternalXPub is when external xPub is missing in paymail +var ErrMissingPaymailExternalXPub = models.SPVError{Message: "missing external xPub in paymail", StatusCode: 400, Code: "error-paymail-external-xpub-missing"} + +// ErrMissingPaymailXPubID is when xpub_id is missing in paymail +var ErrMissingPaymailXPubID = models.SPVError{Message: "missing xpub_id in paymail", StatusCode: 400, Code: "error-paymail-xpub-id-missing"} + +// ErrPaymailAlreadyExists is when paymail with given data already exists in db +var ErrPaymailAlreadyExists = models.SPVError{Message: "paymail already exists", StatusCode: 409, Code: "error-paymail-already-exists"} + +//////////////////////////////////// CAPABILITIES ERRORS + +// ErrCapabilitiesPkiUnsupported is when PKI is not supported for given paymail domain +var ErrCapabilitiesPkiUnsupported = models.SPVError{Message: "server doesn't support PKI", StatusCode: 400, Code: "error-capabilities-pki-unsupported"} + +// ErrCapabilitiesPikeUnsupported is when PIKE is not supported for given paymail domain +var ErrCapabilitiesPikeUnsupported = models.SPVError{Message: "server doesn't support PIKE", StatusCode: 400, Code: "error-capabilities-pike-unsupported"} + +// ErrGetCapabilities is when getting capabilities failed +var ErrGetCapabilities = models.SPVError{Message: "failed to get paymail capabilities", StatusCode: 400, Code: "error-capabilities-failed-to-get"} + +//////////////////////////////////// TRANSACTION ERRORS + +// ErrCouldNotFindTransaction is an error when a transaction could not be found +var ErrCouldNotFindTransaction = models.SPVError{Message: "transaction not found", StatusCode: 404, Code: "error-transaction-not-found"} + +// ErrCouldNotFindSyncTx is an error when a given utxo could not be found +var ErrCouldNotFindSyncTx = models.SPVError{Message: "sync tx not found", StatusCode: 404, Code: "error-transaction-sync-tx-not-found"} + +// ErrCouldNotFindDraftTx is an error when a given draft tx could not be found +var ErrCouldNotFindDraftTx = models.SPVError{Message: "draft tx not found", StatusCode: 404, Code: "error-transaction-draft-tx-not-found"} + +// ErrInvalidTransactionID is when a transaction id cannot be decoded +var ErrInvalidTransactionID = models.SPVError{Message: "invalid transaction id", StatusCode: 400, Code: "error-transaction-id-invalid"} + +// ErrInvalidRequirements is when an invalid requirement was given +var ErrInvalidRequirements = models.SPVError{Message: "requirements are invalid or missing", StatusCode: 400, Code: "error-transaction-requirements-invalid"} + +// ErrTransactionIDMismatch is when the returned tx does not match the expected given tx id +var ErrTransactionIDMismatch = models.SPVError{Message: "result tx id did not match provided tx id", StatusCode: 400, Code: "error-transaction-id-mismatch"} + +// ErrMissingTransactionOutputs is when the draft transaction has no outputs +var ErrMissingTransactionOutputs = models.SPVError{Message: "draft transaction configuration has no outputs", StatusCode: 400, Code: "error-transaction-outputs-missing"} + +// ErrOutputValueTooLow is when the satoshis output is too low on a transaction +var ErrOutputValueTooLow = models.SPVError{Message: "output value is too low", StatusCode: 400, Code: "error-transaction-output-value-too-low"} + +// ErrOutputValueTooHigh is when the satoshis output is too high on a transaction +var ErrOutputValueTooHigh = models.SPVError{Message: "output value is too high", StatusCode: 400, Code: "error-transaction-output-value-too-high"} + +// ErrInvalidOpReturnOutput is when a locking script is not a valid op_return +var ErrInvalidOpReturnOutput = models.SPVError{Message: "invalid op_return output", StatusCode: 400, Code: "error-transaction-invalid-op-return-output"} + +// ErrInvalidLockingScript is when a locking script cannot be decoded +var ErrInvalidLockingScript = models.SPVError{Message: "invalid locking script", StatusCode: 400, Code: "error-transaction-locking-script-invalid"} + +// ErrOutputValueNotRecognized is when there is an invalid output value given, or missing value +var ErrOutputValueNotRecognized = models.SPVError{Message: "output value is unrecognized", StatusCode: 400, Code: "error-transaction-output-value-unrecognized"} + +// ErrInvalidScriptOutput is when a locking script is not a valid bitcoin script +var ErrInvalidScriptOutput = models.SPVError{Message: "invalid script output", StatusCode: 400, Code: "error-transaction-script-output-invalid"} + +// ErrDraftIDMismatch is when the reference ID does not match the reservation id +var ErrDraftIDMismatch = models.SPVError{Message: "transaction draft id does not match utxo draft reservation id", StatusCode: 400, Code: "error-transaction-draft-id-mismatch"} + +// ErrMissingTxHex is when the hex is missing or invalid and creates an empty id +var ErrMissingTxHex = models.SPVError{Message: "transaction hex is empty or id is missing", StatusCode: 400, Code: "error-transaction-hex-missing"} + +// ErrNoMatchingOutputs is when the transaction does not match any known destinations +var ErrNoMatchingOutputs = models.SPVError{Message: "transaction outputs do not match any known destinations", StatusCode: 400, Code: "error-transaction-outputs-no-matching"} + +// ErrCreateOutgoingTxFailed is when error occurred during creation of outgoing tx +var ErrCreateOutgoingTxFailed = models.SPVError{Message: "creation of outgoing tx failed", StatusCode: 500, Code: "error-transaction-create-outgoing-tx-failed"} + +// ErrDuringSaveTx is when error occurred during save tx +var ErrDuringSaveTx = models.SPVError{Message: "error during saving tx", StatusCode: 500, Code: "error-transaction-save-failed"} + +// ErrTransactionRejectedByP2PProvider is an error when a tx was rejected by P2P Provider +var ErrTransactionRejectedByP2PProvider = models.SPVError{Message: "transaction rejected by P2P provider", StatusCode: 400, Code: "error-transaction-rejected"} + +// ErrDraftTxHasNoOutputs is when draft transaction has no outputs +var ErrDraftTxHasNoOutputs = models.SPVError{Message: "corresponding draft transaction has no outputs", StatusCode: 400, Code: "error-transaction-draft-has-no-outputs"} + +// ErrProcessP2PTx is when error occurred during processing p2p tx +var ErrProcessP2PTx = models.SPVError{Message: "error during processing p2p transaction", StatusCode: 500, Code: "error-transaction-process-p2p"} + +// ErrInvalidHex is when cannot create tx from hex +var ErrInvalidHex = models.SPVError{Message: "invalid hex", StatusCode: 400, Code: "error-transaction-hex-invalid"} + +// ErrEmptyRelatedDraftId is when related draft id is empty +var ErrEmptyRelatedDraftId = models.SPVError{Message: "empty RelatedDraftID", StatusCode: 400, Code: "error-transaction-related-draft-id-empty"} + +// ErrEmptyXpubKey is when xpub key is empty +var ErrEmptyXpubKey = models.SPVError{Message: "empty xPubKey", StatusCode: 400, Code: "error-transaction-xpub-key-empty"} + +// ErrEmptyTx is when tx is empty +var ErrEmptyTx = models.SPVError{Message: "empty tx", StatusCode: 400, Code: "error-transaction-empty"} + +// ErrTxRevertEmptyDraftID is when draft id is empty this means that tx is not from spv-wallet +var ErrTxRevertEmptyDraftID = models.SPVError{Message: "not a spv wallet engine originating transaction, cannot revert", StatusCode: 400, Code: "error-transaction-revert-draft-id-empty"} + +// ErrTxRevertCouldNotFindDraftTx is when draft tx could not be found +var ErrTxRevertCouldNotFindDraftTx = models.SPVError{Message: "could not find the draft transaction for this transaction, cannot revert", StatusCode: 400, Code: "error-transaction-revert-draft-tx-not-found"} + +// ErrTxRevertNotFoundOnChain is when tx was not found on chain +var ErrTxRevertNotFoundOnChain = models.SPVError{Message: "transaction was found on-chain, cannot revert", StatusCode: 400, Code: "error-transaction-revert-not-found-on-chain"} + +// ErrTxRevertUtxoAlreadySpent is when utxo from tx was already spent +var ErrTxRevertUtxoAlreadySpent = models.SPVError{Message: "utxo of this transaction has been spent, cannot revert", StatusCode: 400, Code: "error-transaction-revert-utxo-already-spent"} + +//////////////////////////////////// UTXO ERRORS + +// ErrCouldNotFindUtxo is an error when a given utxo could not be found +var ErrCouldNotFindUtxo = models.SPVError{Message: "utxo could not be found", StatusCode: 404, Code: "error-utxo-not-found"} + +// ErrUtxoAlreadySpent is when the utxo is already spent, but is trying to be used +var ErrUtxoAlreadySpent = models.SPVError{Message: "utxo has already been spent", StatusCode: 400, Code: "error-utxo-already-spent"} + +// ErrMissingUTXOsSpendable is when there are no utxos found from the "spendable utxos" +var ErrMissingUTXOsSpendable = models.SPVError{Message: "no utxos found using spendable", StatusCode: 404, Code: "error-utxo-spendable-missing"} + +// ErrNotEnoughUtxos is when a draft transaction cannot be created because of lack of utxos +var ErrNotEnoughUtxos = models.SPVError{Message: "could not select enough outputs to satisfy transaction", StatusCode: 400, Code: "error-utxo-not-enough"} + +// ErrDuplicateUTXOs is when a transaction is created using the same utxo more than once +var ErrDuplicateUTXOs = models.SPVError{Message: "duplicate utxos found", StatusCode: 400, Code: "error-utxo-duplicate"} + +// ErrTransactionFeeInvalid is when the fee on the transaction is not the difference between inputs and outputs +var ErrTransactionFeeInvalid = models.SPVError{Message: "transaction fee is invalid", StatusCode: 400, Code: "error-utxo-transaction-fee-invalid"} + +// ErrChangeStrategyNotImplemented is a temporary error until the feature is supported +var ErrChangeStrategyNotImplemented = models.SPVError{Message: "change strategy nominations not implemented yet", StatusCode: 501, Code: "error-utxo-change-strategy-not-implemented"} + +// ErrUtxoNotReserved is when the utxo is not reserved, but a transaction tries to spend it +var ErrUtxoNotReserved = models.SPVError{Message: "transaction utxo has not been reserved for spending", StatusCode: 400, Code: "error-utxo-not-reserved"} + +//////////////////////////////////// XPUB ERRORS + +// ErrCouldNotFindXpub is when could not find xpub +var ErrCouldNotFindXpub = models.SPVError{Message: "xpub not found", StatusCode: 404, Code: "error-xpub-not-found"} + +// ErrXpubInvalidLength is when the length of the xpub does not match the desired length +var ErrXpubInvalidLength = models.SPVError{Message: "xpub is an invalid length", StatusCode: 400, Code: "error-xpub-length-invalid"} + +// ErrXpubNoMatch is when the derived xpub key does not match the key given +var ErrXpubNoMatch = models.SPVError{Message: "xpub key does not match raw key", StatusCode: 400, Code: "error-xpub-key-no-match"} + +// ErrXpubIDMisMatch is when the xPubID does not match +var ErrXpubIDMisMatch = models.SPVError{Message: "xpub_id mismatch", StatusCode: 400, Code: "error-xpub-id-mismatch"} + +//////////////////////////////////// MISSING FIELDS + +// ErrOneOfTheFieldsIsRequired is when all of required fields are missing +var ErrOneOfTheFieldsIsRequired = models.SPVError{Message: "missing all of the fields, one of them is required", StatusCode: 400, Code: "error-missing-field-all-required"} + +// ErrMissingAccessKey is when the access key field is required but missing +var ErrMissingAccessKey = models.SPVError{Message: "missing required field: access key", StatusCode: 400, Code: "error-missing-field-access-key"} + +// ErrMissingFieldID is when the id field is required but missing +var ErrMissingFieldID = models.SPVError{Message: "missing required field: id", StatusCode: 400, Code: "error-missing-field-id"} + +// ErrMissingFieldXpubID is when the xpub_id field is required but missing +var ErrMissingFieldXpubID = models.SPVError{Message: "missing required field: xpub_id", StatusCode: 400, Code: "error-missing-field-xpub-id"} + +// ErrMissingFieldXpub is when the xpub field is required but missing +var ErrMissingFieldXpub = models.SPVError{Message: "missing required field: xpub", StatusCode: 400, Code: "error-missing-field-xpub"} + +// ErrMissingAddress is when the address field address is required but missing +var ErrMissingAddress = models.SPVError{Message: "missing required field: address", StatusCode: 400, Code: "error-missing-field-address"} + +// ErrMissingFieldScriptPubKey is when the field is required but missing +var ErrMissingFieldScriptPubKey = models.SPVError{Message: "missing required field: script_pub_key", StatusCode: 400, Code: "error-missing-field-script-pub-key"} + +// ErrMissingFieldSatoshis is when the field satoshis is required but missing +var ErrMissingFieldSatoshis = models.SPVError{Message: "missing required field: satoshis", StatusCode: 400, Code: "error-missing-field-satoshis"} + +// ErrMissingFieldTransactionID is when the field transaction id is required but missing +var ErrMissingFieldTransactionID = models.SPVError{Message: "missing required field: transaction_id", StatusCode: 400, Code: "error-missing-field-transaction-id"} + +// ErrMissingLockingScript is when the field locking script is required but missing +var ErrMissingLockingScript = models.SPVError{Message: "missing required field: locking script", StatusCode: 400, Code: "error-missing-field-locking-script"} + +//////////////////////////////////// SAVE ERROR + +// ErrMissingClient is when client is missing from model, cannot save +var ErrMissingClient = models.SPVError{Message: "client is missing from model, cannot save", StatusCode: 400, Code: "error-client-missing"} + +// ErrDatastoreRequired is when a datastore function is called without a datastore present +var ErrDatastoreRequired = models.SPVError{Message: "datastore is required", StatusCode: 500, Code: "error-datastore-required"} diff --git a/engine/spverrors/http_response.go b/engine/spverrors/http_response.go new file mode 100644 index 00000000..94dac20b --- /dev/null +++ b/engine/spverrors/http_response.go @@ -0,0 +1,42 @@ +package spverrors + +import ( + "errors" + "github.com/bitcoin-sv/spv-wallet/models" + "github.com/gin-gonic/gin" + "github.com/rs/zerolog" +) + +// ErrorResponse is searching for error and setting it up in gin context +func ErrorResponse(c *gin.Context, err error, log *zerolog.Logger) { + response, statusCode := getError(err, log) + c.JSON(statusCode, response) +} + +// AbortWithErrorResponse is searching for error and abort with error set +func AbortWithErrorResponse(c *gin.Context, err error, log *zerolog.Logger) { + response, statusCode := getError(err, log) + c.AbortWithStatusJSON(statusCode, response) +} + +func getError(err error, log *zerolog.Logger) (models.ResponseError, int) { + var extendedErr models.ExtendedError + if errors.As(err, &extendedErr) { + return models.ResponseError{ + Code: extendedErr.GetCode(), + Message: extendedErr.GetMessage(), + }, extendedErr.GetStatusCode() + } + + logError(log, err) + return models.ResponseError{ + Code: models.UnknownErrorCode, + Message: "Unable to get information about error", + }, 500 +} + +func logError(log *zerolog.Logger, err error) { + if log != nil { + log.Warn().Str("module", "spv-errors").Msgf("Unable to get information about error, details: %s", err.Error()) + } +} diff --git a/engine/sync_tx_repository.go b/engine/sync_tx_repository.go index dc36237d..149be7e2 100644 --- a/engine/sync_tx_repository.go +++ b/engine/sync_tx_repository.go @@ -6,6 +6,7 @@ import ( "errors" "github.com/bitcoin-sv/spv-wallet/engine/datastore" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/libsv/go-bt/v2" ) @@ -82,7 +83,7 @@ func getTransactionsToBroadcast(ctx context.Context, queryParams *datastore.Quer if err != nil { return nil, err } else if sTx.transaction == nil { - return nil, ErrMissingTransaction + return nil, spverrors.ErrCouldNotFindTransaction } parentsBroadcast, err := _areParentsBroadcasted(ctx, sTx.transaction, opts...) diff --git a/engine/sync_tx_service.go b/engine/sync_tx_service.go index 1652f855..e2cdea56 100644 --- a/engine/sync_tx_service.go +++ b/engine/sync_tx_service.go @@ -13,6 +13,7 @@ import ( "github.com/bitcoin-sv/spv-wallet/engine/chainstate" "github.com/bitcoin-sv/spv-wallet/engine/datastore" "github.com/bitcoin-sv/spv-wallet/engine/notifications" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" ) // processSyncTransactions will process sync transaction records @@ -189,7 +190,7 @@ func _syncTxDataFromChain(ctx context.Context, syncTx *SyncTransaction, transact if transaction == nil { if transaction, err = _getTransaction(ctx, syncTx.ID, syncTx.GetOptions(false)); err != nil { - return ErrMissingTransaction + return spverrors.ErrCouldNotFindTransaction } } @@ -198,7 +199,7 @@ func _syncTxDataFromChain(ctx context.Context, syncTx *SyncTransaction, transact if txInfo, err = syncTx.Client().Chainstate().QueryTransaction( ctx, syncTx.ID, chainstate.RequiredOnChain, defaultQueryTxTimeout, ); err != nil { - if errors.Is(err, chainstate.ErrTransactionNotFound) { + if errors.Is(err, spverrors.ErrCouldNotFindTransaction) { syncTx.Client().Logger().Info(). Str("txID", syncTx.ID). Msgf("Transaction not found on-chain, will try again later") @@ -219,7 +220,7 @@ func _getTransaction(ctx context.Context, id string, opts []ModelOps) (*Transact } if transaction == nil { - return nil, ErrMissingTransaction + return nil, spverrors.ErrCouldNotFindTransaction } return transaction, nil diff --git a/engine/tx_service.go b/engine/tx_service.go index 7655626e..d244bd1e 100644 --- a/engine/tx_service.go +++ b/engine/tx_service.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "fmt" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" ) @@ -14,7 +15,7 @@ func saveRawTransaction(ctx context.Context, c ClientInterface, allowUnknown boo newOpts := c.DefaultModelOptions(append(opts, New())...) tx, err := txFromHex(txHex, newOpts...) if err != nil { - return nil, ErrMissingTxHex + return nil, spverrors.ErrMissingTxHex } // Create the lock and set the release for after the function completes @@ -27,7 +28,7 @@ func saveRawTransaction(ctx context.Context, c ClientInterface, allowUnknown boo } if !allowUnknown && !tx.hasOneKnownDestination(ctx, c) { - return nil, ErrNoMatchingOutputs + return nil, spverrors.ErrNoMatchingOutputs } if err = tx.processUtxos(ctx); err != nil { @@ -95,7 +96,7 @@ func (m *Transaction) _processInputs(ctx context.Context) (err error) { // Is Spent? if len(utxo.SpendingTxID.String) > 0 { - return ErrUtxoAlreadySpent + return spverrors.ErrUtxoAlreadySpent } // Only if IUC is enabled (or client is nil which means its enabled by default) @@ -107,10 +108,10 @@ func (m *Transaction) _processInputs(ctx context.Context) (err error) { // Check whether the spending transaction was reserved by the draft transaction (in the utxo) if !isReserved { - return ErrUtxoNotReserved + return spverrors.ErrUtxoNotReserved } if !matchesDraft { - return ErrDraftIDMismatch + return spverrors.ErrDraftIDMismatch } } diff --git a/engine/utils/errors.go b/engine/utils/errors.go index b04da0f3..7ce4e85e 100644 --- a/engine/utils/errors.go +++ b/engine/utils/errors.go @@ -4,12 +4,6 @@ import ( "errors" ) -// ErrXpubInvalidLength is when the length of the xpub does not match the desired length -var ErrXpubInvalidLength = errors.New("xpub is an invalid length") - -// ErrXpubNoMatch is when the derived xpub key does not match the key given -var ErrXpubNoMatch = errors.New("xpub key does not match raw key") - // ErrHDKeyNil is when the HD Key is nil var ErrHDKeyNil = errors.New("hd key is nil") diff --git a/engine/utils/keys.go b/engine/utils/keys.go index 3de6e461..5a0ff48e 100644 --- a/engine/utils/keys.go +++ b/engine/utils/keys.go @@ -1,6 +1,7 @@ package utils import ( + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoinschema/go-bitcoin/v2" "github.com/libsv/go-bk/bec" "github.com/libsv/go-bk/bip32" @@ -45,7 +46,7 @@ func ValidateXPub(rawKey string) (*bip32.ExtendedKey, error) { // Validate the xpub (length) if len(rawKey) != XpubKeyLength { - return nil, ErrXpubInvalidLength + return nil, spverrors.ErrXpubInvalidLength } // Parse the xPub into an HD key @@ -53,7 +54,7 @@ func ValidateXPub(rawKey string) (*bip32.ExtendedKey, error) { if err != nil { return nil, err } else if hdKey.String() != rawKey { // Sanity check (might not be needed) - return nil, ErrXpubNoMatch + return nil, spverrors.ErrXpubNoMatch } return hdKey, nil } diff --git a/models/apierrors/errors_def.go b/models/apierrors/errors_def.go deleted file mode 100644 index 1f4a278d..00000000 --- a/models/apierrors/errors_def.go +++ /dev/null @@ -1,13 +0,0 @@ -// Package apierrors contains errors that can be returned by spv-wallet api -package apierrors - -import "errors" - -// ErrDraftNotFound is when the requested draft transaction was not found -var ErrDraftNotFound = errors.New("corresponding draft transaction not found") - -// ErrMissingXPriv is when the xPriv is missing -var ErrMissingXPriv = errors.New("missing xPriv key") - -// ErrMissingAccessKey is when the access key is missing -var ErrMissingAccessKey = errors.New("missing access key") diff --git a/models/errors.go b/models/errors.go new file mode 100644 index 00000000..543000c1 --- /dev/null +++ b/models/errors.go @@ -0,0 +1,43 @@ +package models + +type ExtendedError interface { + error + GetCode() string + GetMessage() string + GetStatusCode() int +} + +// SPVError is extended error which holds information about http status and code that should be returned +type SPVError struct { + Code string + Message string + StatusCode int +} + +// ResponseError is an error which will be returned in HTTP response +type ResponseError struct { + Code string `json:"code"` + Message string `json:"message"` +} + +const UnknownErrorCode = "error-unknown" + +// Error returns the error message string for SPVError, satisfying the error interface +func (e SPVError) Error() string { + return e.Message +} + +// GetCode returns the error code string for SPVError +func (e SPVError) GetCode() string { + return e.Code +} + +// GetMessage returns the error message string for SPVError +func (e SPVError) GetMessage() string { + return e.Message +} + +// GetStatusCode returns the error status code for SPVError +func (e SPVError) GetStatusCode() int { + return e.StatusCode +} diff --git a/models/filter/utils.go b/models/filter/utils.go index 1db1ea59..745a4833 100644 --- a/models/filter/utils.go +++ b/models/filter/utils.go @@ -25,7 +25,7 @@ func checkStrOption(value string, options ...string) (string, error) { return opt, nil } } - return "", errors.New("Invalid option: " + value) + return "", errors.New("invalid filter option") } func checkAndApplyStrOption(conditions map[string]interface{}, columnName string, value *string, options ...string) error { diff --git a/models/go.mod b/models/go.mod index 4a948a49..d363ab8e 100644 --- a/models/go.mod +++ b/models/go.mod @@ -2,10 +2,41 @@ module github.com/bitcoin-sv/spv-wallet/models go 1.22.4 -require github.com/stretchr/testify v1.9.0 require ( + github.com/stretchr/testify v1.9.0 +) + +require ( + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gin-gonic/gin v1.10.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rs/zerolog v1.33.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/models/go.sum b/models/go.sum index 60ce688a..b353a157 100644 --- a/models/go.sum +++ b/models/go.sum @@ -1,10 +1,101 @@ +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/server/auth/middleware.go b/server/auth/middleware.go index c4630def..cc7d8591 100644 --- a/server/auth/middleware.go +++ b/server/auth/middleware.go @@ -3,7 +3,6 @@ package auth import ( "bytes" "context" - "errors" "fmt" "io" "net/http" @@ -12,8 +11,8 @@ import ( "time" "github.com/bitcoin-sv/spv-wallet/config" - "github.com/bitcoin-sv/spv-wallet/dictionary" "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoinschema/go-bitcoin/v2" @@ -83,7 +82,7 @@ func BasicMiddleware(engine engine.ClientInterface, appConfig *config.AppConfig) xPub := strings.TrimSpace(c.GetHeader(models.AuthHeader)) authAccessKey := strings.TrimSpace(c.GetHeader(models.AuthAccessKey)) if len(xPub) == 0 && len(authAccessKey) == 0 { - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "missing authentication header"}) + spverrors.AbortWithErrorResponse(c, spverrors.ErrMissingAuthHeader, nil) return } @@ -92,7 +91,7 @@ func BasicMiddleware(engine engine.ClientInterface, appConfig *config.AppConfig) if xPub != "" { // Validate that the xPub is an HD key (length, validation) if _, err := utils.ValidateXPub(xPub); err != nil { - c.AbortWithStatusJSON(http.StatusUnauthorized, err.Error()) + spverrors.AbortWithErrorResponse(c, spverrors.ErrAuthorization, nil) return } @@ -105,7 +104,7 @@ func BasicMiddleware(engine engine.ClientInterface, appConfig *config.AppConfig) } else if authAccessKey != "" { accessKey, err := engine.AuthenticateAccessKey(context.Background(), utils.Hash(authAccessKey)) if err != nil || accessKey == nil { - c.AbortWithStatusJSON(http.StatusUnauthorized, err.Error()) + spverrors.AbortWithErrorResponse(c, spverrors.ErrAuthorization, nil) return } @@ -125,7 +124,7 @@ func AdminMiddleware() gin.HandlerFunc { if c.GetBool(ParamAdminRequest) { c.Next() } else { - c.AbortWithStatusJSON(http.StatusUnauthorized, "xpub provided is not an admin key") + spverrors.AbortWithErrorResponse(c, spverrors.ErrNotAnAdminKey, nil) } } } @@ -134,7 +133,7 @@ func AdminMiddleware() gin.HandlerFunc { func SignatureMiddleware(appConfig *config.AppConfig, requireSigning, adminRequired bool) gin.HandlerFunc { return func(c *gin.Context) { if c.Request.Body == nil { - c.AbortWithStatusJSON(http.StatusUnauthorized, "missing body") + spverrors.AbortWithErrorResponse(c, spverrors.ErrMissingBody, nil) return } defer func() { @@ -142,7 +141,7 @@ func SignatureMiddleware(appConfig *config.AppConfig, requireSigning, adminRequi }() b, err := io.ReadAll(c.Request.Body) if err != nil { - c.AbortWithStatusJSON(http.StatusUnauthorized, err.Error()) + spverrors.AbortWithErrorResponse(c, spverrors.ErrAuthorization, nil) } c.Request.Body = io.NopCloser(bytes.NewReader(b)) @@ -161,7 +160,7 @@ func SignatureMiddleware(appConfig *config.AppConfig, requireSigning, adminRequi // adminRequired will always force checking of a signature if (requireSigning || adminRequired) && !appConfig.Authentication.SigningDisabled { if err = checkSignature(authData); err != nil { - c.AbortWithStatusJSON(http.StatusUnauthorized, err.Error()) + spverrors.AbortWithErrorResponse(c, err, nil) } c.Set(ParamAuthSigned, true) } else { @@ -171,7 +170,7 @@ func SignatureMiddleware(appConfig *config.AppConfig, requireSigning, adminRequi // NOTE: you can not use an access key if signing is invalid - ever if authData.accessKey != "" && err != nil { - c.AbortWithStatusJSON(http.StatusUnauthorized, err.Error()) + spverrors.AbortWithErrorResponse(c, err, nil) } } c.Next() @@ -184,19 +183,16 @@ func CallbackTokenMiddleware(appConfig *config.AppConfig) gin.HandlerFunc { const BearerSchema = "Bearer " authHeader := c.GetHeader("Authorization") if authHeader == "" { - err := dictionary.GetError(dictionary.ErrorAuthenticationCallback, "missing auth header") - c.AbortWithStatusJSON(err.StatusCode, err) + spverrors.AbortWithErrorResponse(c, spverrors.ErrMissingAuthHeader, nil) } if !strings.HasPrefix(authHeader, BearerSchema) || len(authHeader) <= len(BearerSchema) { - err := dictionary.GetError(dictionary.ErrorAuthenticationCallback, "invalid or missing bearer token") - c.AbortWithStatusJSON(err.StatusCode, err) + spverrors.AbortWithErrorResponse(c, spverrors.ErrInvalidOrMissingToken, nil) } providedToken := authHeader[len(BearerSchema):] if providedToken != appConfig.Nodes.Callback.Token { - err := dictionary.GetError(dictionary.ErrorAuthenticationCallback, "invalid authorization token") - c.AbortWithStatusJSON(err.StatusCode, err) + spverrors.AbortWithErrorResponse(c, spverrors.ErrInvalidToken, nil) } c.Next() @@ -218,16 +214,16 @@ func checkSignature(auth *Payload) error { // checkSignatureRequirements will check the payload for basic signature requirements func checkSignatureRequirements(auth *Payload) error { if auth == nil || auth.Signature == "" { - return errors.New("missing signature") + return spverrors.ErrMissingSignature } bodyHash := createBodyHash(auth.BodyContents) if auth.AuthHash != bodyHash { - return errors.New("auth hash and body hash do not match") + return spverrors.ErrHashesDoNotMatch } if time.Now().UTC().After(time.UnixMilli(auth.AuthTime).Add(models.AuthSignatureTTL)) { - return errors.New("signature has expired") + return spverrors.ErrSignatureExpired } return nil } @@ -240,29 +236,25 @@ func createBodyHash(bodyContents string) string { // verifyKeyXPub will verify the xPub key and the signature payload func verifyKeyXPub(xPub string, auth *Payload) error { if _, err := utils.ValidateXPub(xPub); err != nil { - err := fmt.Errorf("error occurred while validating xPub key: %w", err) - return err + return spverrors.ErrValidateXPub } if auth == nil { - return errors.New("missing signature") + return spverrors.ErrMissingSignature } key, err := bitcoin.GetHDKeyFromExtendedPublicKey(xPub) if err != nil { - err = fmt.Errorf("error occurred while getting HD key from xPub: %w", err) - return err + return spverrors.ErrGettingHdKeyFromXpub } if key, err = utils.DeriveChildKeyFromHex(key, auth.AuthNonce); err != nil { - err = fmt.Errorf("error occurred while deriving child key: %w", err) - return err + return spverrors.ErrDeriveChildKey } var address *bscript.Address if address, err = bitcoin.GetAddressFromHDKey(key); err != nil { - err = fmt.Errorf("error occurred while getting address from HD key: %w", err) - return err // Should never error + return spverrors.ErrGettingAddressFromHdKey } message := getSigningMessage(xPub, auth) @@ -271,7 +263,7 @@ func verifyKeyXPub(xPub string, auth *Payload) error { auth.Signature, message, ); err != nil { - return errors.New("signature invalid") + return spverrors.ErrInvalidSignature } return nil } @@ -282,8 +274,7 @@ func verifyMessageAndSignature(key string, auth *Payload) error { key, true, ) if err != nil { - err = fmt.Errorf("error occurred while getting address from public key: %w", err) - return err + return spverrors.ErrGettingAddressFromPublicKey } if err := bitcoin.VerifyMessage( @@ -291,7 +282,7 @@ func verifyMessageAndSignature(key string, auth *Payload) error { auth.Signature, getSigningMessage(key, auth), ); err != nil { - return errors.New("signature invalid") + return spverrors.ErrInvalidSignature } return nil }