From 740dcf6a11b33e6eb697f9bdd43834b05a0a1994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Lewandowski?= Date: Wed, 26 Jun 2024 10:57:25 +0200 Subject: [PATCH] chore(SPV-846): refactor errors --- authentication.go | 14 ++--- errors.go | 22 ++++--- examples/go.mod | 2 +- go.mod | 9 +-- go.sum | 2 + http.go | 146 +++++++++++++++++++++++----------------------- search.go | 8 +-- 7 files changed, 104 insertions(+), 99 deletions(-) diff --git a/authentication.go b/authentication.go index a09da8c..a8389d8 100644 --- a/authentication.go +++ b/authentication.go @@ -3,13 +3,11 @@ package walletclient import ( "encoding/hex" "fmt" - "github.com/bitcoin-sv/spv-wallet/spverrors" "net/http" "time" "github.com/bitcoin-sv/spv-wallet-go-client/utils" "github.com/bitcoin-sv/spv-wallet/models" - "github.com/bitcoin-sv/spv-wallet/models/apierrors" "github.com/bitcoinschema/go-bitcoin/v2" "github.com/libsv/go-bk/bec" "github.com/libsv/go-bk/bip32" @@ -19,7 +17,7 @@ import ( ) // SetSignature will set the signature on the header for the request -func setSignature(header *http.Header, xPriv *bip32.ExtendedKey, bodyString string) *spverrors.SPVError { +func setSignature(header *http.Header, xPriv *bip32.ExtendedKey, bodyString string) *models.SPVError { // Create the signature authData, err := createSignature(xPriv, bodyString) if err != nil { @@ -29,7 +27,9 @@ func setSignature(header *http.Header, xPriv *bip32.ExtendedKey, bodyString stri // Set the auth header header.Set(models.AuthHeader, authData.XPub) - return setSignatureHeaders(header, authData) + setSignatureHeaders(header, authData) + + return nil } // GetSignedHex will sign all the inputs using the given xPriv key @@ -135,7 +135,7 @@ func getUnlockingScript(tx *bt.Tx, inputIndex uint32, privateKey *bec.PrivateKey func createSignature(xPriv *bip32.ExtendedKey, bodyString string) (payload *models.AuthPayload, err error) { // No key? if xPriv == nil { - err = apierrors.ErrMissingXPriv + err = CreateErrorResponse("error-unauthorized-missing-xpriv", "missing xpriv") return } @@ -200,7 +200,7 @@ func getSigningMessage(xPub string, auth *models.AuthPayload) string { return fmt.Sprintf("%s%s%s%d", xPub, auth.AuthHash, auth.AuthNonce, auth.AuthTime) } -func setSignatureHeaders(header *http.Header, authData *models.AuthPayload) *spverrors.SPVError { +func setSignatureHeaders(header *http.Header, authData *models.AuthPayload) { // Create the auth header hash header.Set(models.AuthHeaderHash, authData.AuthHash) @@ -212,6 +212,4 @@ func setSignatureHeaders(header *http.Header, authData *models.AuthPayload) *spv // Set the signature header.Set(models.AuthSignature, authData.Signature) - - return nil } diff --git a/errors.go b/errors.go index 92c7e1f..3cb8496 100644 --- a/errors.go +++ b/errors.go @@ -3,7 +3,7 @@ package walletclient import ( "encoding/json" "errors" - "github.com/bitcoin-sv/spv-wallet/spverrors" + "github.com/bitcoin-sv/spv-wallet/models" "net/http" ) @@ -14,34 +14,42 @@ var ErrAdminKey = errors.New("an admin key must be set to be able to create an x var ErrNoClientSet = errors.New("no transport client set") // WrapError wraps an error into SPVError -func WrapError(err error) *spverrors.SPVError { +func WrapError(err error) *models.SPVError { if err == nil { return nil } - return &spverrors.SPVError{ + return &models.SPVError{ StatusCode: http.StatusInternalServerError, Message: err.Error(), - Code: spverrors.UnknownErrorCode, + Code: models.UnknownErrorCode, } } // WrapResponseError wraps a http response into SPVError -func WrapResponseError(res *http.Response) *spverrors.SPVError { +func WrapResponseError(res *http.Response) *models.SPVError { if res == nil { return nil } - var resError *spverrors.ResponseError + var resError *models.ResponseError err := json.NewDecoder(res.Body).Decode(&resError) if err != nil { return WrapError(err) } - return &spverrors.SPVError{ + return &models.SPVError{ StatusCode: res.StatusCode, Code: resError.Code, Message: resError.Message, } } + +func CreateErrorResponse(code string, message string) *models.SPVError { + return &models.SPVError{ + StatusCode: http.StatusInternalServerError, + Code: code, + Message: message, + } +} diff --git a/examples/go.mod b/examples/go.mod index 458eab3..3100d17 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -1,6 +1,6 @@ module github.com/bitcoin-sv/spv-wallet-go-client/examples -go 1.21 +go 1.22.4 replace github.com/bitcoin-sv/spv-wallet-go-client => ../ diff --git a/go.mod b/go.mod index 7546afe..6bef1b1 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,9 @@ module github.com/bitcoin-sv/spv-wallet-go-client -go 1.22.3 - -toolchain go1.22.4 +go 1.22.4 require ( - github.com/bitcoin-sv/spv-wallet/models v1.0.0-beta.13 + github.com/bitcoin-sv/spv-wallet/models v1.0.0-beta.14.0.20240626082725-2c073c5330a6 github.com/bitcoinschema/go-bitcoin/v2 v2.0.5 github.com/libsv/go-bk v0.1.6 github.com/libsv/go-bt/v2 v2.2.5 @@ -15,7 +13,6 @@ require ( ) require ( - github.com/bitcoin-sv/spv-wallet/spverrors v0.0.0-00010101000000-000000000000 // indirect github.com/bitcoinsv/bsvd v0.0.0-20190609155523-4c29707f7173 // indirect github.com/boombuler/barcode v1.0.1 // indirect github.com/bytedance/sonic v1.11.6 // indirect @@ -51,4 +48,4 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace github.com/bitcoin-sv/spv-wallet/spverrors => ../spv-wallet/spverrors +replace github.com/bitcoin-sv/spv-wallet/models => ../spv-wallet/models diff --git a/go.sum b/go.sum index 58aeeda..5054e71 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/bitcoin-sv/spv-wallet/models v1.0.0-beta.13 h1:rBscs3Gbz0RWY03eI3Z9AwD7/MxajdJF54oy3xMqKRQ= github.com/bitcoin-sv/spv-wallet/models v1.0.0-beta.13/go.mod h1:i3txysriHpprqYd3u97wEQsC4/jn+KHcyFOmuFYMw8M= +github.com/bitcoin-sv/spv-wallet/models v1.0.0-beta.14.0.20240626082725-2c073c5330a6 h1:ZTEHuSNbXszs+5TKN0uiW6DY7JdWIM5m6NQpBJZyre4= +github.com/bitcoin-sv/spv-wallet/models v1.0.0-beta.14.0.20240626082725-2c073c5330a6/go.mod h1:u3gnRDS3uHWZNM2qbYATTpN+mAphyozCJrYIKGwBX7k= github.com/bitcoinschema/go-bitcoin/v2 v2.0.5 h1:Sgh5Eb746Zck/46rFDrZZEXZWyO53fMuWYhNoZa1tck= github.com/bitcoinschema/go-bitcoin/v2 v2.0.5/go.mod h1:JjO1ivfZv6vhK0uAXzyH08AAHlzNMAfnyK1Fiv9r4ZA= github.com/bitcoinsv/bsvd v0.0.0-20190609155523-4c29707f7173 h1:2yTIV9u7H0BhRDGXH5xrAwAz7XibWJtX2dNezMeNsUo= diff --git a/http.go b/http.go index ab5ef65..e7884a6 100644 --- a/http.go +++ b/http.go @@ -7,13 +7,11 @@ import ( "encoding/json" "errors" "fmt" - "github.com/bitcoin-sv/spv-wallet/spverrors" "net/http" "strconv" "github.com/bitcoin-sv/spv-wallet-go-client/utils" "github.com/bitcoin-sv/spv-wallet/models" - "github.com/bitcoin-sv/spv-wallet/models/apierrors" "github.com/bitcoin-sv/spv-wallet/models/filter" "github.com/bitcoinschema/go-bitcoin/v2" "github.com/libsv/go-bk/bec" @@ -36,7 +34,7 @@ func (wc *WalletClient) SetAdminKey(adminKey *bip32.ExtendedKey) { } // GetXPub will get the xpub of the current xpub -func (wc *WalletClient) GetXPub(ctx context.Context) (*models.Xpub, *spverrors.SPVError) { +func (wc *WalletClient) GetXPub(ctx context.Context) (*models.Xpub, *models.SPVError) { var xPub models.Xpub if err := wc.doHTTPRequest( ctx, http.MethodGet, "/xpub", nil, wc.xPriv, true, &xPub, @@ -48,7 +46,7 @@ func (wc *WalletClient) GetXPub(ctx context.Context) (*models.Xpub, *spverrors.S } // UpdateXPubMetadata update the metadata of the logged in xpub -func (wc *WalletClient) UpdateXPubMetadata(ctx context.Context, metadata map[string]any) (*models.Xpub, *spverrors.SPVError) { +func (wc *WalletClient) UpdateXPubMetadata(ctx context.Context, metadata map[string]any) (*models.Xpub, *models.SPVError) { jsonStr, err := json.Marshal(map[string]interface{}{ FieldMetadata: metadata, }) @@ -67,7 +65,7 @@ func (wc *WalletClient) UpdateXPubMetadata(ctx context.Context, metadata map[str } // GetAccessKey will get an access key by id -func (wc *WalletClient) GetAccessKey(ctx context.Context, id string) (*models.AccessKey, *spverrors.SPVError) { +func (wc *WalletClient) GetAccessKey(ctx context.Context, id string) (*models.AccessKey, *models.SPVError) { var accessKey models.AccessKey if err := wc.doHTTPRequest( ctx, http.MethodGet, "/access-key?"+FieldID+"="+id, nil, wc.xPriv, true, &accessKey, @@ -84,7 +82,7 @@ func (wc *WalletClient) GetAccessKeys( conditions *filter.AccessKeyFilter, metadata map[string]any, queryParams *filter.QueryParams, -) ([]*models.AccessKey, *spverrors.SPVError) { +) ([]*models.AccessKey, *models.SPVError) { return Search[filter.AccessKeyFilter, []*models.AccessKey]( ctx, http.MethodPost, "/access-key/search", @@ -101,7 +99,7 @@ func (wc *WalletClient) GetAccessKeysCount( ctx context.Context, conditions *filter.AccessKeyFilter, metadata map[string]any, -) (int64, *spverrors.SPVError) { +) (int64, *models.SPVError) { return Count[filter.AccessKeyFilter]( ctx, http.MethodPost, "/access-key/count", @@ -113,7 +111,7 @@ func (wc *WalletClient) GetAccessKeysCount( } // RevokeAccessKey will revoke an access key by id -func (wc *WalletClient) RevokeAccessKey(ctx context.Context, id string) (*models.AccessKey, *spverrors.SPVError) { +func (wc *WalletClient) RevokeAccessKey(ctx context.Context, id string) (*models.AccessKey, *models.SPVError) { var accessKey models.AccessKey if err := wc.doHTTPRequest( ctx, http.MethodDelete, "/access-key?"+FieldID+"="+id, nil, wc.xPriv, true, &accessKey, @@ -125,7 +123,7 @@ func (wc *WalletClient) RevokeAccessKey(ctx context.Context, id string) (*models } // CreateAccessKey will create new access key -func (wc *WalletClient) CreateAccessKey(ctx context.Context, metadata map[string]any) (*models.AccessKey, *spverrors.SPVError) { +func (wc *WalletClient) CreateAccessKey(ctx context.Context, metadata map[string]any) (*models.AccessKey, *models.SPVError) { jsonStr, err := json.Marshal(map[string]interface{}{ FieldMetadata: metadata, }) @@ -143,7 +141,7 @@ func (wc *WalletClient) CreateAccessKey(ctx context.Context, metadata map[string } // GetDestinationByID will get a destination by id -func (wc *WalletClient) GetDestinationByID(ctx context.Context, id string) (*models.Destination, *spverrors.SPVError) { +func (wc *WalletClient) GetDestinationByID(ctx context.Context, id string) (*models.Destination, *models.SPVError) { var destination models.Destination if err := wc.doHTTPRequest( ctx, http.MethodGet, fmt.Sprintf("/destination?%s=%s", FieldID, id), nil, wc.xPriv, true, &destination, @@ -155,7 +153,7 @@ func (wc *WalletClient) GetDestinationByID(ctx context.Context, id string) (*mod } // GetDestinationByAddress will get a destination by address -func (wc *WalletClient) GetDestinationByAddress(ctx context.Context, address string) (*models.Destination, *spverrors.SPVError) { +func (wc *WalletClient) GetDestinationByAddress(ctx context.Context, address string) (*models.Destination, *models.SPVError) { var destination models.Destination if err := wc.doHTTPRequest( ctx, http.MethodGet, "/destination?"+FieldAddress+"="+address, nil, wc.xPriv, true, &destination, @@ -167,7 +165,7 @@ func (wc *WalletClient) GetDestinationByAddress(ctx context.Context, address str } // GetDestinationByLockingScript will get a destination by locking script -func (wc *WalletClient) GetDestinationByLockingScript(ctx context.Context, lockingScript string) (*models.Destination, *spverrors.SPVError) { +func (wc *WalletClient) GetDestinationByLockingScript(ctx context.Context, lockingScript string) (*models.Destination, *models.SPVError) { var destination models.Destination if err := wc.doHTTPRequest( ctx, http.MethodGet, "/destination?"+FieldLockingScript+"="+lockingScript, nil, wc.xPriv, true, &destination, @@ -179,7 +177,7 @@ func (wc *WalletClient) GetDestinationByLockingScript(ctx context.Context, locki } // GetDestinations will get all destinations matching the metadata filter -func (wc *WalletClient) GetDestinations(ctx context.Context, conditions *filter.DestinationFilter, metadata map[string]any, queryParams *filter.QueryParams) ([]*models.Destination, *spverrors.SPVError) { +func (wc *WalletClient) GetDestinations(ctx context.Context, conditions *filter.DestinationFilter, metadata map[string]any, queryParams *filter.QueryParams) ([]*models.Destination, *models.SPVError) { return Search[filter.DestinationFilter, []*models.Destination]( ctx, http.MethodPost, "/destination/search", @@ -192,7 +190,7 @@ func (wc *WalletClient) GetDestinations(ctx context.Context, conditions *filter. } // GetDestinationsCount will get the count of destinations matching the metadata filter -func (wc *WalletClient) GetDestinationsCount(ctx context.Context, conditions *filter.DestinationFilter, metadata map[string]any) (int64, *spverrors.SPVError) { +func (wc *WalletClient) GetDestinationsCount(ctx context.Context, conditions *filter.DestinationFilter, metadata map[string]any) (int64, *models.SPVError) { return Count( ctx, http.MethodPost, @@ -205,7 +203,7 @@ func (wc *WalletClient) GetDestinationsCount(ctx context.Context, conditions *fi } // NewDestination will create a new destination and return it -func (wc *WalletClient) NewDestination(ctx context.Context, metadata map[string]any) (*models.Destination, *spverrors.SPVError) { +func (wc *WalletClient) NewDestination(ctx context.Context, metadata map[string]any) (*models.Destination, *models.SPVError) { jsonStr, err := json.Marshal(map[string]interface{}{ FieldMetadata: metadata, }) @@ -223,7 +221,7 @@ func (wc *WalletClient) NewDestination(ctx context.Context, metadata map[string] } // UpdateDestinationMetadataByID updates the destination metadata by id -func (wc *WalletClient) UpdateDestinationMetadataByID(ctx context.Context, id string, metadata map[string]any) (*models.Destination, *spverrors.SPVError) { +func (wc *WalletClient) UpdateDestinationMetadataByID(ctx context.Context, id string, metadata map[string]any) (*models.Destination, *models.SPVError) { jsonStr, err := json.Marshal(map[string]interface{}{ FieldID: id, FieldMetadata: metadata, @@ -243,7 +241,7 @@ func (wc *WalletClient) UpdateDestinationMetadataByID(ctx context.Context, id st } // UpdateDestinationMetadataByAddress updates the destination metadata by address -func (wc *WalletClient) UpdateDestinationMetadataByAddress(ctx context.Context, address string, metadata map[string]any) (*models.Destination, *spverrors.SPVError) { +func (wc *WalletClient) UpdateDestinationMetadataByAddress(ctx context.Context, address string, metadata map[string]any) (*models.Destination, *models.SPVError) { jsonStr, err := json.Marshal(map[string]interface{}{ FieldAddress: address, FieldMetadata: metadata, @@ -263,7 +261,7 @@ func (wc *WalletClient) UpdateDestinationMetadataByAddress(ctx context.Context, } // UpdateDestinationMetadataByLockingScript updates the destination metadata by locking script -func (wc *WalletClient) UpdateDestinationMetadataByLockingScript(ctx context.Context, lockingScript string, metadata map[string]any) (*models.Destination, *spverrors.SPVError) { +func (wc *WalletClient) UpdateDestinationMetadataByLockingScript(ctx context.Context, lockingScript string, metadata map[string]any) (*models.Destination, *models.SPVError) { jsonStr, err := json.Marshal(map[string]interface{}{ FieldLockingScript: lockingScript, FieldMetadata: metadata, @@ -283,7 +281,7 @@ func (wc *WalletClient) UpdateDestinationMetadataByLockingScript(ctx context.Con } // GetTransaction will get a transaction by ID -func (wc *WalletClient) GetTransaction(ctx context.Context, txID string) (*models.Transaction, *spverrors.SPVError) { +func (wc *WalletClient) GetTransaction(ctx context.Context, txID string) (*models.Transaction, *models.SPVError) { var transaction models.Transaction if err := wc.doHTTPRequest(ctx, http.MethodGet, "/transaction?"+FieldID+"="+txID, nil, wc.xPriv, wc.signRequest, &transaction); err != nil { return nil, err @@ -298,7 +296,7 @@ func (wc *WalletClient) GetTransactions( conditions *filter.TransactionFilter, metadata map[string]any, queryParams *filter.QueryParams, -) ([]*models.Transaction, *spverrors.SPVError) { +) ([]*models.Transaction, *models.SPVError) { return Search[filter.TransactionFilter, []*models.Transaction]( ctx, http.MethodPost, "/transaction/search", @@ -315,7 +313,7 @@ func (wc *WalletClient) GetTransactionsCount( ctx context.Context, conditions *filter.TransactionFilter, metadata map[string]any, -) (int64, *spverrors.SPVError) { +) (int64, *models.SPVError) { return Count[filter.TransactionFilter]( ctx, http.MethodPost, "/transaction/count", @@ -327,7 +325,7 @@ func (wc *WalletClient) GetTransactionsCount( } // DraftToRecipients is a draft transaction to a slice of recipients -func (wc *WalletClient) DraftToRecipients(ctx context.Context, recipients []*Recipients, metadata map[string]any) (*models.DraftTransaction, *spverrors.SPVError) { +func (wc *WalletClient) DraftToRecipients(ctx context.Context, recipients []*Recipients, metadata map[string]any) (*models.DraftTransaction, *models.SPVError) { outputs := make([]map[string]interface{}, 0) for _, recipient := range recipients { outputs = append(outputs, map[string]interface{}{ @@ -346,7 +344,7 @@ func (wc *WalletClient) DraftToRecipients(ctx context.Context, recipients []*Rec } // DraftTransaction is a draft transaction -func (wc *WalletClient) DraftTransaction(ctx context.Context, transactionConfig *models.TransactionConfig, metadata map[string]any) (*models.DraftTransaction, *spverrors.SPVError) { +func (wc *WalletClient) DraftTransaction(ctx context.Context, transactionConfig *models.TransactionConfig, metadata map[string]any) (*models.DraftTransaction, *models.SPVError) { return wc.createDraftTransaction(ctx, map[string]interface{}{ FieldConfig: transactionConfig, FieldMetadata: metadata, @@ -356,7 +354,7 @@ func (wc *WalletClient) DraftTransaction(ctx context.Context, transactionConfig // createDraftTransaction will create a draft transaction func (wc *WalletClient) createDraftTransaction(ctx context.Context, jsonData map[string]interface{}, -) (*models.DraftTransaction, *spverrors.SPVError) { +) (*models.DraftTransaction, *models.SPVError) { jsonStr, err := json.Marshal(jsonData) if err != nil { return nil, WrapError(err) @@ -369,14 +367,14 @@ func (wc *WalletClient) createDraftTransaction(ctx context.Context, return nil, err } if draftTransaction == nil { - return nil, WrapError(apierrors.ErrDraftNotFound) + return nil, CreateErrorResponse("error-draft-transaction-not-found", "draft transaction not found") } return draftTransaction, nil } // RecordTransaction will record a transaction -func (wc *WalletClient) RecordTransaction(ctx context.Context, hex, referenceID string, metadata map[string]any) (*models.Transaction, *spverrors.SPVError) { +func (wc *WalletClient) RecordTransaction(ctx context.Context, hex, referenceID string, metadata map[string]any) (*models.Transaction, *models.SPVError) { jsonStr, err := json.Marshal(map[string]interface{}{ FieldHex: hex, FieldReferenceID: referenceID, @@ -397,7 +395,7 @@ func (wc *WalletClient) RecordTransaction(ctx context.Context, hex, referenceID } // UpdateTransactionMetadata update the metadata of a transaction -func (wc *WalletClient) UpdateTransactionMetadata(ctx context.Context, txID string, metadata map[string]any) (*models.Transaction, *spverrors.SPVError) { +func (wc *WalletClient) UpdateTransactionMetadata(ctx context.Context, txID string, metadata map[string]any) (*models.Transaction, *models.SPVError) { jsonStr, err := json.Marshal(map[string]interface{}{ FieldID: txID, FieldMetadata: metadata, @@ -417,7 +415,7 @@ func (wc *WalletClient) UpdateTransactionMetadata(ctx context.Context, txID stri } // SetSignatureFromAccessKey will set the signature on the header for the request from an access key -func SetSignatureFromAccessKey(header *http.Header, privateKeyHex, bodyString string) *spverrors.SPVError { +func SetSignatureFromAccessKey(header *http.Header, privateKeyHex, bodyString string) *models.SPVError { // Create the signature authData, err := createSignatureAccessKey(privateKeyHex, bodyString) if err != nil { @@ -427,11 +425,13 @@ func SetSignatureFromAccessKey(header *http.Header, privateKeyHex, bodyString st // Set the auth header header.Set(models.AuthAccessKey, authData.AccessKey) - return setSignatureHeaders(header, authData) + setSignatureHeaders(header, authData) + + return nil } // GetUtxo will get a utxo by transaction ID -func (wc *WalletClient) GetUtxo(ctx context.Context, txID string, outputIndex uint32) (*models.Utxo, *spverrors.SPVError) { +func (wc *WalletClient) GetUtxo(ctx context.Context, txID string, outputIndex uint32) (*models.Utxo, *models.SPVError) { outputIndexStr := strconv.FormatUint(uint64(outputIndex), 10) url := fmt.Sprintf("/utxo?%s=%s&%s=%s", FieldTransactionID, txID, FieldOutputIndex, outputIndexStr) @@ -447,7 +447,7 @@ func (wc *WalletClient) GetUtxo(ctx context.Context, txID string, outputIndex ui } // GetUtxos will get a list of utxos filtered by conditions and metadata -func (wc *WalletClient) GetUtxos(ctx context.Context, conditions *filter.UtxoFilter, metadata map[string]any, queryParams *filter.QueryParams) ([]*models.Utxo, *spverrors.SPVError) { +func (wc *WalletClient) GetUtxos(ctx context.Context, conditions *filter.UtxoFilter, metadata map[string]any, queryParams *filter.QueryParams) ([]*models.Utxo, *models.SPVError) { return Search[filter.UtxoFilter, []*models.Utxo]( ctx, http.MethodPost, "/utxo/search", @@ -460,7 +460,7 @@ func (wc *WalletClient) GetUtxos(ctx context.Context, conditions *filter.UtxoFil } // GetUtxosCount will get the count of utxos filtered by conditions and metadata -func (wc *WalletClient) GetUtxosCount(ctx context.Context, conditions *filter.UtxoFilter, metadata map[string]any) (int64, *spverrors.SPVError) { +func (wc *WalletClient) GetUtxosCount(ctx context.Context, conditions *filter.UtxoFilter, metadata map[string]any) (int64, *models.SPVError) { return Count[filter.UtxoFilter]( ctx, http.MethodPost, "/utxo/count", @@ -475,7 +475,7 @@ func (wc *WalletClient) GetUtxosCount(ctx context.Context, conditions *filter.Ut func createSignatureAccessKey(privateKeyHex, bodyString string) (payload *models.AuthPayload, err error) { // No key? if privateKeyHex == "" { - err = apierrors.ErrMissingAccessKey + err = CreateErrorResponse("error-unauthorized-missing-access-key", "missing access key") return } @@ -504,7 +504,7 @@ func createSignatureAccessKey(privateKeyHex, bodyString string) (payload *models // doHTTPRequest will create and submit the HTTP request func (wc *WalletClient) doHTTPRequest(ctx context.Context, method string, path string, rawJSON []byte, xPriv *bip32.ExtendedKey, sign bool, responseJSON interface{}, -) *spverrors.SPVError { +) *models.SPVError { req, err := http.NewRequestWithContext(ctx, method, wc.server+path, bytes.NewBuffer(rawJSON)) if err != nil { return WrapError(err) @@ -547,7 +547,7 @@ func (wc *WalletClient) doHTTPRequest(ctx context.Context, method string, path s return nil } -func (wc *WalletClient) authenticateWithXpriv(sign bool, req *http.Request, xPriv *bip32.ExtendedKey, rawJSON []byte) *spverrors.SPVError { +func (wc *WalletClient) authenticateWithXpriv(sign bool, req *http.Request, xPriv *bip32.ExtendedKey, rawJSON []byte) *models.SPVError { if sign { if err := addSignature(&req.Header, xPriv, string(rawJSON)); err != nil { return err @@ -564,12 +564,12 @@ func (wc *WalletClient) authenticateWithXpriv(sign bool, req *http.Request, xPri return nil } -func (wc *WalletClient) authenticateWithAccessKey(req *http.Request, rawJSON []byte) *spverrors.SPVError { +func (wc *WalletClient) authenticateWithAccessKey(req *http.Request, rawJSON []byte) *models.SPVError { return SetSignatureFromAccessKey(&req.Header, hex.EncodeToString(wc.accessKey.Serialise()), string(rawJSON)) } // AcceptContact will accept the contact associated with the paymail -func (wc *WalletClient) AcceptContact(ctx context.Context, paymail string) *spverrors.SPVError { +func (wc *WalletClient) AcceptContact(ctx context.Context, paymail string) *models.SPVError { if err := wc.doHTTPRequest( ctx, http.MethodPatch, "/contact/accepted/"+paymail, nil, wc.xPriv, wc.signRequest, nil, ); err != nil { @@ -580,7 +580,7 @@ func (wc *WalletClient) AcceptContact(ctx context.Context, paymail string) *spve } // RejectContact will reject the contact associated with the paymail -func (wc *WalletClient) RejectContact(ctx context.Context, paymail string) *spverrors.SPVError { +func (wc *WalletClient) RejectContact(ctx context.Context, paymail string) *models.SPVError { if err := wc.doHTTPRequest( ctx, http.MethodPatch, "/contact/rejected/"+paymail, nil, wc.xPriv, wc.signRequest, nil, ); err != nil { @@ -591,7 +591,7 @@ func (wc *WalletClient) RejectContact(ctx context.Context, paymail string) *spve } // ConfirmContact will confirm the contact associated with the paymail -func (wc *WalletClient) ConfirmContact(ctx context.Context, contact *models.Contact, passcode, requesterPaymail string, period, digits uint) *spverrors.SPVError { +func (wc *WalletClient) ConfirmContact(ctx context.Context, contact *models.Contact, passcode, requesterPaymail string, period, digits uint) *models.SPVError { isTotpValid, err := wc.ValidateTotpForContact(contact, passcode, requesterPaymail, period, digits) if err != nil { return WrapError(fmt.Errorf("totp validation failed: %w", err)) @@ -611,7 +611,7 @@ func (wc *WalletClient) ConfirmContact(ctx context.Context, contact *models.Cont } // GetContacts will get contacts by conditions -func (wc *WalletClient) GetContacts(ctx context.Context, conditions *filter.ContactFilter, metadata map[string]any, queryParams *filter.QueryParams) (*models.SearchContactsResponse, *spverrors.SPVError) { +func (wc *WalletClient) GetContacts(ctx context.Context, conditions *filter.ContactFilter, metadata map[string]any, queryParams *filter.QueryParams) (*models.SearchContactsResponse, *models.SPVError) { return Search[filter.ContactFilter, *models.SearchContactsResponse]( ctx, http.MethodPost, "/contact/search", @@ -624,12 +624,12 @@ func (wc *WalletClient) GetContacts(ctx context.Context, conditions *filter.Cont } // UpsertContact add or update contact. When adding a new contact, the system utilizes Paymail's PIKE capability to dispatch an invitation request, asking the counterparty to include the current user in their contacts. -func (wc *WalletClient) UpsertContact(ctx context.Context, paymail, fullName string, metadata map[string]any) (*models.Contact, *spverrors.SPVError) { +func (wc *WalletClient) UpsertContact(ctx context.Context, paymail, fullName string, metadata map[string]any) (*models.Contact, *models.SPVError) { return wc.UpsertContactForPaymail(ctx, paymail, fullName, metadata, "") } // UpsertContactForPaymail add or update contact. When adding a new contact, the system utilizes Paymail's PIKE capability to dispatch an invitation request, asking the counterparty to include the current user in their contacts. -func (wc *WalletClient) UpsertContactForPaymail(ctx context.Context, paymail, fullName string, metadata map[string]any, requesterPaymail string) (*models.Contact, *spverrors.SPVError) { +func (wc *WalletClient) UpsertContactForPaymail(ctx context.Context, paymail, fullName string, metadata map[string]any, requesterPaymail string) (*models.Contact, *models.SPVError) { payload := map[string]interface{}{ "fullName": fullName, FieldMetadata: metadata, @@ -655,7 +655,7 @@ func (wc *WalletClient) UpsertContactForPaymail(ctx context.Context, paymail, fu } // GetSharedConfig gets the shared config -func (wc *WalletClient) GetSharedConfig(ctx context.Context) (*models.SharedConfig, *spverrors.SPVError) { +func (wc *WalletClient) GetSharedConfig(ctx context.Context) (*models.SharedConfig, *models.SPVError) { var model *models.SharedConfig key := wc.xPriv @@ -675,7 +675,7 @@ func (wc *WalletClient) GetSharedConfig(ctx context.Context) (*models.SharedConf } // AdminNewXpub will register an xPub -func (wc *WalletClient) AdminNewXpub(ctx context.Context, rawXPub string, metadata map[string]any) *spverrors.SPVError { +func (wc *WalletClient) AdminNewXpub(ctx context.Context, rawXPub string, metadata map[string]any) *models.SPVError { // Adding a xpub needs to be signed by an admin key if wc.adminXPriv == nil { return WrapError(ErrAdminKey) @@ -697,7 +697,7 @@ func (wc *WalletClient) AdminNewXpub(ctx context.Context, rawXPub string, metada } // AdminGetStatus get whether admin key is valid -func (wc *WalletClient) AdminGetStatus(ctx context.Context) (bool, *spverrors.SPVError) { +func (wc *WalletClient) AdminGetStatus(ctx context.Context) (bool, *models.SPVError) { var status bool if err := wc.doHTTPRequest( ctx, http.MethodGet, "/admin/status", nil, wc.adminXPriv, true, &status, @@ -709,7 +709,7 @@ func (wc *WalletClient) AdminGetStatus(ctx context.Context) (bool, *spverrors.SP } // AdminGetStats get admin stats -func (wc *WalletClient) AdminGetStats(ctx context.Context) (*models.AdminStats, *spverrors.SPVError) { +func (wc *WalletClient) AdminGetStats(ctx context.Context) (*models.AdminStats, *models.SPVError) { var stats *models.AdminStats if err := wc.doHTTPRequest( ctx, http.MethodGet, "/admin/stats", nil, wc.adminXPriv, true, &stats, @@ -726,7 +726,7 @@ func (wc *WalletClient) AdminGetAccessKeys( conditions *filter.AdminAccessKeyFilter, metadata map[string]any, queryParams *filter.QueryParams, -) ([]*models.AccessKey, *spverrors.SPVError) { +) ([]*models.AccessKey, *models.SPVError) { return Search[filter.AdminAccessKeyFilter, []*models.AccessKey]( ctx, http.MethodPost, "/admin/access-keys/search", @@ -743,7 +743,7 @@ func (wc *WalletClient) AdminGetAccessKeysCount( ctx context.Context, conditions *filter.AdminAccessKeyFilter, metadata map[string]any, -) (int64, *spverrors.SPVError) { +) (int64, *models.SPVError) { return Count[filter.AdminAccessKeyFilter]( ctx, http.MethodPost, "/admin/access-keys/count", @@ -760,7 +760,7 @@ func (wc *WalletClient) AdminGetBlockHeaders( conditions map[string]interface{}, metadata map[string]any, queryParams *filter.QueryParams, -) ([]*models.BlockHeader, *spverrors.SPVError) { +) ([]*models.BlockHeader, *models.SPVError) { var models []*models.BlockHeader if err := wc.adminGetModels(ctx, conditions, metadata, queryParams, "/admin/block-headers/search", &models); err != nil { return nil, err @@ -774,14 +774,14 @@ func (wc *WalletClient) AdminGetBlockHeadersCount( ctx context.Context, conditions map[string]interface{}, metadata map[string]any, -) (int64, *spverrors.SPVError) { +) (int64, *models.SPVError) { return wc.adminCount(ctx, conditions, metadata, "/admin/block-headers/count") } // AdminGetDestinations get all block destinations filtered by conditions func (wc *WalletClient) AdminGetDestinations(ctx context.Context, conditions *filter.DestinationFilter, metadata map[string]any, queryParams *filter.QueryParams, -) ([]*models.Destination, *spverrors.SPVError) { +) ([]*models.Destination, *models.SPVError) { return Search[filter.DestinationFilter, []*models.Destination]( ctx, http.MethodPost, "/admin/destinations/search", @@ -794,7 +794,7 @@ func (wc *WalletClient) AdminGetDestinations(ctx context.Context, conditions *fi } // AdminGetDestinationsCount get a count of all the destinations filtered by conditions -func (wc *WalletClient) AdminGetDestinationsCount(ctx context.Context, conditions *filter.DestinationFilter, metadata map[string]any) (int64, *spverrors.SPVError) { +func (wc *WalletClient) AdminGetDestinationsCount(ctx context.Context, conditions *filter.DestinationFilter, metadata map[string]any) (int64, *models.SPVError) { return Count( ctx, http.MethodPost, @@ -807,7 +807,7 @@ func (wc *WalletClient) AdminGetDestinationsCount(ctx context.Context, condition } // AdminGetPaymail get a paymail by address -func (wc *WalletClient) AdminGetPaymail(ctx context.Context, address string) (*models.PaymailAddress, *spverrors.SPVError) { +func (wc *WalletClient) AdminGetPaymail(ctx context.Context, address string) (*models.PaymailAddress, *models.SPVError) { jsonStr, err := json.Marshal(map[string]interface{}{ FieldAddress: address, }) @@ -831,7 +831,7 @@ func (wc *WalletClient) AdminGetPaymails( conditions *filter.AdminPaymailFilter, metadata map[string]any, queryParams *filter.QueryParams, -) ([]*models.PaymailAddress, *spverrors.SPVError) { +) ([]*models.PaymailAddress, *models.SPVError) { return Search[filter.AdminPaymailFilter, []*models.PaymailAddress]( ctx, http.MethodPost, "/admin/paymails/search", @@ -844,7 +844,7 @@ func (wc *WalletClient) AdminGetPaymails( } // AdminGetPaymailsCount get a count of all the paymails filtered by conditions -func (wc *WalletClient) AdminGetPaymailsCount(ctx context.Context, conditions *filter.AdminPaymailFilter, metadata map[string]any) (int64, *spverrors.SPVError) { +func (wc *WalletClient) AdminGetPaymailsCount(ctx context.Context, conditions *filter.AdminPaymailFilter, metadata map[string]any) (int64, *models.SPVError) { return Count( ctx, http.MethodPost, "/admin/paymails/count", @@ -856,7 +856,7 @@ func (wc *WalletClient) AdminGetPaymailsCount(ctx context.Context, conditions *f } // AdminCreatePaymail create a new paymail for a xpub -func (wc *WalletClient) AdminCreatePaymail(ctx context.Context, rawXPub string, address string, publicName string, avatar string) (*models.PaymailAddress, *spverrors.SPVError) { +func (wc *WalletClient) AdminCreatePaymail(ctx context.Context, rawXPub string, address string, publicName string, avatar string) (*models.PaymailAddress, *models.SPVError) { jsonStr, err := json.Marshal(map[string]interface{}{ FieldXpubKey: rawXPub, FieldAddress: address, @@ -878,7 +878,7 @@ func (wc *WalletClient) AdminCreatePaymail(ctx context.Context, rawXPub string, } // AdminDeletePaymail delete a paymail address from the database -func (wc *WalletClient) AdminDeletePaymail(ctx context.Context, address string) *spverrors.SPVError { +func (wc *WalletClient) AdminDeletePaymail(ctx context.Context, address string) *models.SPVError { jsonStr, err := json.Marshal(map[string]interface{}{ FieldAddress: address, }) @@ -901,7 +901,7 @@ func (wc *WalletClient) AdminGetTransactions( conditions *filter.TransactionFilter, metadata map[string]any, queryParams *filter.QueryParams, -) ([]*models.Transaction, *spverrors.SPVError) { +) ([]*models.Transaction, *models.SPVError) { return Search[filter.TransactionFilter, []*models.Transaction]( ctx, http.MethodPost, "/admin/transactions/search", @@ -918,7 +918,7 @@ func (wc *WalletClient) AdminGetTransactionsCount( ctx context.Context, conditions *filter.TransactionFilter, metadata map[string]any, -) (int64, *spverrors.SPVError) { +) (int64, *models.SPVError) { return Count[filter.TransactionFilter]( ctx, http.MethodPost, "/admin/transactions/count", @@ -935,7 +935,7 @@ func (wc *WalletClient) AdminGetUtxos( conditions *filter.AdminUtxoFilter, metadata map[string]any, queryParams *filter.QueryParams, -) ([]*models.Utxo, *spverrors.SPVError) { +) ([]*models.Utxo, *models.SPVError) { return Search[filter.AdminUtxoFilter, []*models.Utxo]( ctx, http.MethodPost, "/admin/utxos/search", @@ -952,7 +952,7 @@ func (wc *WalletClient) AdminGetUtxosCount( ctx context.Context, conditions *filter.AdminUtxoFilter, metadata map[string]any, -) (int64, *spverrors.SPVError) { +) (int64, *models.SPVError) { return Count[filter.AdminUtxoFilter]( ctx, http.MethodPost, "/admin/utxos/count", @@ -966,7 +966,7 @@ func (wc *WalletClient) AdminGetUtxosCount( // AdminGetXPubs get all block xpubs filtered by conditions func (wc *WalletClient) AdminGetXPubs(ctx context.Context, conditions *filter.XpubFilter, metadata map[string]any, queryParams *filter.QueryParams, -) ([]*models.Xpub, *spverrors.SPVError) { +) ([]*models.Xpub, *models.SPVError) { return Search[filter.XpubFilter, []*models.Xpub]( ctx, http.MethodPost, "/admin/xpubs/search", @@ -983,7 +983,7 @@ func (wc *WalletClient) AdminGetXPubsCount( ctx context.Context, conditions *filter.XpubFilter, metadata map[string]any, -) (int64, *spverrors.SPVError) { +) (int64, *models.SPVError) { return Count[filter.XpubFilter]( ctx, http.MethodPost, "/admin/xpubs/count", @@ -1001,7 +1001,7 @@ func (wc *WalletClient) adminGetModels( queryParams *filter.QueryParams, path string, models interface{}, -) *spverrors.SPVError { +) *models.SPVError { jsonStr, err := json.Marshal(map[string]interface{}{ FieldConditions: conditions, FieldMetadata: metadata, @@ -1020,7 +1020,7 @@ func (wc *WalletClient) adminGetModels( return nil } -func (wc *WalletClient) adminCount(ctx context.Context, conditions map[string]interface{}, metadata map[string]any, path string) (int64, *spverrors.SPVError) { +func (wc *WalletClient) adminCount(ctx context.Context, conditions map[string]interface{}, metadata map[string]any, path string) (int64, *models.SPVError) { jsonStr, err := json.Marshal(map[string]interface{}{ FieldConditions: conditions, FieldMetadata: metadata, @@ -1040,7 +1040,7 @@ func (wc *WalletClient) adminCount(ctx context.Context, conditions map[string]in } // AdminRecordTransaction will record a transaction as an admin -func (wc *WalletClient) AdminRecordTransaction(ctx context.Context, hex string) (*models.Transaction, *spverrors.SPVError) { +func (wc *WalletClient) AdminRecordTransaction(ctx context.Context, hex string) (*models.Transaction, *models.SPVError) { jsonStr, err := json.Marshal(map[string]interface{}{ FieldHex: hex, }) @@ -1059,7 +1059,7 @@ func (wc *WalletClient) AdminRecordTransaction(ctx context.Context, hex string) } // AdminGetContacts executes an HTTP POST request to search for contacts based on specified conditions, metadata, and query parameters. -func (wc *WalletClient) AdminGetContacts(ctx context.Context, conditions *filter.ContactFilter, metadata map[string]any, queryParams *filter.QueryParams) (*models.SearchContactsResponse, *spverrors.SPVError) { +func (wc *WalletClient) AdminGetContacts(ctx context.Context, conditions *filter.ContactFilter, metadata map[string]any, queryParams *filter.QueryParams) (*models.SearchContactsResponse, *models.SPVError) { return Search[filter.ContactFilter, *models.SearchContactsResponse]( ctx, http.MethodPost, "/admin/contact/search", @@ -1072,7 +1072,7 @@ func (wc *WalletClient) AdminGetContacts(ctx context.Context, conditions *filter } // AdminUpdateContact executes an HTTP PATCH request to update a specific contact's full name using their ID. -func (wc *WalletClient) AdminUpdateContact(ctx context.Context, id, fullName string, metadata map[string]any) (*models.Contact, *spverrors.SPVError) { +func (wc *WalletClient) AdminUpdateContact(ctx context.Context, id, fullName string, metadata map[string]any) (*models.Contact, *models.SPVError) { jsonStr, err := json.Marshal(map[string]interface{}{ "fullName": fullName, FieldMetadata: metadata, @@ -1086,27 +1086,27 @@ func (wc *WalletClient) AdminUpdateContact(ctx context.Context, id, fullName str } // AdminDeleteContact executes an HTTP DELETE request to remove a contact using their ID. -func (wc *WalletClient) AdminDeleteContact(ctx context.Context, id string) *spverrors.SPVError { +func (wc *WalletClient) AdminDeleteContact(ctx context.Context, id string) *models.SPVError { err := wc.doHTTPRequest(ctx, http.MethodDelete, fmt.Sprintf("/admin/contact/%s", id), nil, wc.adminXPriv, true, nil) return WrapError(err) } // AdminAcceptContact executes an HTTP PATCH request to mark a contact as accepted using their ID. -func (wc *WalletClient) AdminAcceptContact(ctx context.Context, id string) (*models.Contact, *spverrors.SPVError) { +func (wc *WalletClient) AdminAcceptContact(ctx context.Context, id string) (*models.Contact, *models.SPVError) { var contact models.Contact err := wc.doHTTPRequest(ctx, http.MethodPatch, fmt.Sprintf("/admin/contact/accepted/%s", id), nil, wc.adminXPriv, true, &contact) return &contact, WrapError(err) } // AdminRejectContact executes an HTTP PATCH request to mark a contact as rejected using their ID. -func (wc *WalletClient) AdminRejectContact(ctx context.Context, id string) (*models.Contact, *spverrors.SPVError) { +func (wc *WalletClient) AdminRejectContact(ctx context.Context, id string) (*models.Contact, *models.SPVError) { var contact models.Contact err := wc.doHTTPRequest(ctx, http.MethodPatch, fmt.Sprintf("/admin/contact/rejected/%s", id), nil, wc.adminXPriv, true, &contact) return &contact, WrapError(err) } // FinalizeTransaction will finalize the transaction -func (wc *WalletClient) FinalizeTransaction(draft *models.DraftTransaction) (string, *spverrors.SPVError) { +func (wc *WalletClient) FinalizeTransaction(draft *models.DraftTransaction) (string, *models.SPVError) { res, err := GetSignedHex(draft, wc.xPriv) if err != nil { return "", WrapError(err) @@ -1116,7 +1116,7 @@ func (wc *WalletClient) FinalizeTransaction(draft *models.DraftTransaction) (str } // SendToRecipients send to recipients -func (wc *WalletClient) SendToRecipients(ctx context.Context, recipients []*Recipients, metadata map[string]any) (*models.Transaction, *spverrors.SPVError) { +func (wc *WalletClient) SendToRecipients(ctx context.Context, recipients []*Recipients, metadata map[string]any) (*models.Transaction, *models.SPVError) { draft, err := wc.DraftToRecipients(ctx, recipients, metadata) if err != nil { return nil, err diff --git a/search.go b/search.go index f8702b5..585c8df 100644 --- a/search.go +++ b/search.go @@ -3,14 +3,14 @@ package walletclient import ( "context" "encoding/json" - "github.com/bitcoin-sv/spv-wallet/spverrors" + "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoin-sv/spv-wallet/models/filter" "github.com/libsv/go-bk/bip32" ) // SearchRequester is a function that sends a request to the server and returns the response. -type SearchRequester func(ctx context.Context, method string, path string, rawJSON []byte, xPriv *bip32.ExtendedKey, sign bool, responseJSON interface{}) *spverrors.SPVError +type SearchRequester func(ctx context.Context, method string, path string, rawJSON []byte, xPriv *bip32.ExtendedKey, sign bool, responseJSON interface{}) *models.SPVError // Search prepares and sends a search request to the server. func Search[TFilter any, TResp any]( @@ -22,7 +22,7 @@ func Search[TFilter any, TResp any]( metadata map[string]any, queryParams *filter.QueryParams, requester SearchRequester, -) (TResp, *spverrors.SPVError) { +) (TResp, *models.SPVError) { jsonStr, err := json.Marshal(filter.SearchModel[TFilter]{ ConditionsModel: filter.ConditionsModel[TFilter]{ Conditions: f, @@ -51,7 +51,7 @@ func Count[TFilter any]( f *TFilter, metadata map[string]any, requester SearchRequester, -) (int64, *spverrors.SPVError) { +) (int64, *models.SPVError) { jsonStr, err := json.Marshal(filter.ConditionsModel[TFilter]{ Conditions: f, Metadata: metadata,