diff --git a/actions/transactions/internal/mapping/outline/outline_request_to_engine.go b/actions/transactions/internal/mapping/outline/outline_request_to_engine.go index a7f64112..e543c27b 100644 --- a/actions/transactions/internal/mapping/outline/outline_request_to_engine.go +++ b/actions/transactions/internal/mapping/outline/outline_request_to_engine.go @@ -5,20 +5,20 @@ import ( "reflect" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft/outputs" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines/outputs" "github.com/bitcoin-sv/spv-wallet/models/request" "github.com/bitcoin-sv/spv-wallet/models/request/opreturn" paymailreq "github.com/bitcoin-sv/spv-wallet/models/request/paymail" "github.com/mitchellh/mapstructure" ) -// Request is a draft transaction request model. -type Request request.DraftTransaction +// Request is a transaction outline request model. +type Request request.TransactionSpecification -// ToEngine converts a draft transaction request model to the engine model. -func (tx Request) ToEngine(xPubID string) (*draft.TransactionSpec, error) { - spec := &draft.TransactionSpec{ +// ToEngine converts a transaction outline request model to the engine model. +func (tx Request) ToEngine(xPubID string) (*outlines.TransactionSpec, error) { + spec := &outlines.TransactionSpec{ XPubID: xPubID, } config := mapstructure.DecoderConfig{ diff --git a/actions/transactions/internal/mapping/outline/outline_to_response.go b/actions/transactions/internal/mapping/outline/outline_to_response.go index 0969ac9f..e79827b7 100644 --- a/actions/transactions/internal/mapping/outline/outline_to_response.go +++ b/actions/transactions/internal/mapping/outline/outline_to_response.go @@ -2,13 +2,13 @@ package outline import ( "github.com/bitcoin-sv/spv-wallet/engine/spverrors" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines" model "github.com/bitcoin-sv/spv-wallet/models/transaction" "github.com/mitchellh/mapstructure" ) -// ToResponse converts a draft transaction to a response model. -func ToResponse(tx *draft.Transaction) (*model.AnnotatedTransaction, error) { +// ToResponse converts a transaction outline to a response model. +func ToResponse(tx *outlines.Transaction) (*model.AnnotatedTransaction, error) { res := &model.AnnotatedTransaction{} err := mapstructure.Decode(tx, res) if err != nil { diff --git a/actions/transactions/outlines.go b/actions/transactions/outlines.go index 24ffa1fd..a64c81d7 100644 --- a/actions/transactions/outlines.go +++ b/actions/transactions/outlines.go @@ -12,7 +12,7 @@ import ( func transactionOutlines(c *gin.Context, userCtx *reqctx.UserContext) { logger := reqctx.Logger(c) - var requestBody request.DraftTransaction + var requestBody request.TransactionSpecification err := c.ShouldBindWith(&requestBody, binding.JSON) if err != nil { spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest.Wrap(err), logger) @@ -25,7 +25,7 @@ func transactionOutlines(c *gin.Context, userCtx *reqctx.UserContext) { return } - txOutline, err := reqctx.Engine(c).TransactionDraftService().Create(c, spec) + txOutline, err := reqctx.Engine(c).TransactionOutlinesService().Create(c, spec) if err != nil { spverrors.ErrorResponse(c, err, logger) return diff --git a/actions/transactions/outlines_endpoint_test.go b/actions/transactions/outlines_endpoint_test.go index 178e0d66..4ee55851 100644 --- a/actions/transactions/outlines_endpoint_test.go +++ b/actions/transactions/outlines_endpoint_test.go @@ -217,7 +217,7 @@ func TestPOSTTransactionOutlines(t *testing.T) { Post(transactionsOutlinesURL) // then: - then.Response(res).IsBadRequest().WithJSONf(apierror.ExpectedJSON("error-draft-paymail-address-no-default", "cannot choose paymail address of the sender")) + then.Response(res).IsBadRequest().WithJSONf(apierror.ExpectedJSON("error-tx-spec-paymail-address-no-default", "cannot choose paymail address of the sender")) }) t.Run("Bad Request: no body", func(t *testing.T) { @@ -244,13 +244,13 @@ func TestPOSTTransactionOutlines(t *testing.T) { }{ "Bad Request: Empty request": { json: `{}`, - expectedErr: apierror.ExpectedJSON("draft-output-required", "draft requires at least one output"), + expectedErr: apierror.ExpectedJSON("tx-spec-output-required", "transaction outline requires at least one output"), }, "Bad Request: Empty outputs": { json: `{ "outputs": [] }`, - expectedErr: apierror.ExpectedJSON("draft-output-required", "draft requires at least one output"), + expectedErr: apierror.ExpectedJSON("tx-spec-output-required", "transaction outline requires at least one output"), }, "Bad Request: Unsupported output type": { json: `{ @@ -281,7 +281,7 @@ func TestPOSTTransactionOutlines(t *testing.T) { } ] }`, - expectedErr: apierror.ExpectedJSON("draft-op-return-data-required", "data is required for OP_RETURN output"), + expectedErr: apierror.ExpectedJSON("tx-spec-op-return-data-required", "data is required for OP_RETURN output"), }, "Bad Request: OP_RETURN output with unknown data type": { json: `{ @@ -305,7 +305,7 @@ func TestPOSTTransactionOutlines(t *testing.T) { } ] }`, - expectedErr: apierror.ExpectedJSON("draft-op-return-data-required", "data is required for OP_RETURN output"), + expectedErr: apierror.ExpectedJSON("tx-spec-op-return-data-required", "data is required for OP_RETURN output"), }, "Bad Request: OP_RETURN strings output with string instead of array as data": { json: `{ @@ -329,7 +329,7 @@ func TestPOSTTransactionOutlines(t *testing.T) { } ] }`, - expectedErr: apierror.ExpectedJSON("draft-op-return-data-required", "data is required for OP_RETURN output"), + expectedErr: apierror.ExpectedJSON("tx-spec-op-return-data-required", "data is required for OP_RETURN output"), }, "Bad Request: OP_RETURN hexes output with invalid hex": { json: `{ diff --git a/engine/client.go b/engine/client.go index 9f526772..2b73ea91 100644 --- a/engine/client.go +++ b/engine/client.go @@ -18,7 +18,7 @@ import ( "github.com/bitcoin-sv/spv-wallet/engine/paymailaddress" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/taskmanager" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines" "github.com/go-resty/resty/v2" "github.com/mrz1836/go-cachestore" "github.com/rs/zerolog" @@ -33,27 +33,27 @@ type ( // clientOptions holds all the configuration for the client clientOptions struct { - cacheStore *cacheStoreOptions // Configuration options for Cachestore (ristretto, redis, etc.) - cluster *clusterOptions // Configuration options for the cluster coordinator - chainstate *chainstateOptions // Configuration options for Chainstate (broadcast, sync, etc.) - dataStore *dataStoreOptions // Configuration options for the DataStore (PostgreSQL, etc.) - debug bool // If the client is in debug mode - encryptionKey string // Encryption key for encrypting sensitive information (IE: paymail xPub) (hex encoded key) - httpClient *resty.Client // HTTP client to use for http calls - iuc bool // (Input UTXO Check) True will check input utxos when saving transactions - logger *zerolog.Logger // Internal logging - metrics *metrics.Metrics // Metrics with a collector interface - models *modelOptions // Configuration options for the loaded models - notifications *notificationsOptions // Configuration options for Notifications - paymail *paymailOptions // Paymail options & client - transactionDraftService draft.Service // Service for transaction drafts - paymailAddressService paymailaddress.Service // Service for paymail addresses - taskManager *taskManagerOptions // Configuration options for the TaskManager (TaskQ, etc.) - userAgent string // User agent for all outgoing requests - chainService chain.Service // Chain service - arcConfig chainmodels.ARCConfig // Configuration for ARC - bhsConfig chainmodels.BHSConfig // Configuration for BHS - txCallbackConfig *txCallbackConfig // Configuration for TX callback received from ARC; disabled if nil + cacheStore *cacheStoreOptions // Configuration options for Cachestore (ristretto, redis, etc.) + cluster *clusterOptions // Configuration options for the cluster coordinator + chainstate *chainstateOptions // Configuration options for Chainstate (broadcast, sync, etc.) + dataStore *dataStoreOptions // Configuration options for the DataStore (PostgreSQL, etc.) + debug bool // If the client is in debug mode + encryptionKey string // Encryption key for encrypting sensitive information (IE: paymail xPub) (hex encoded key) + httpClient *resty.Client // HTTP client to use for http calls + iuc bool // (Input UTXO Check) True will check input utxos when saving transactions + logger *zerolog.Logger // Internal logging + metrics *metrics.Metrics // Metrics with a collector interface + models *modelOptions // Configuration options for the loaded models + notifications *notificationsOptions // Configuration options for Notifications + paymail *paymailOptions // Paymail options & client + transactionOutlinesService outlines.Service // Service for transaction drafts + paymailAddressService paymailaddress.Service // Service for paymail addresses + taskManager *taskManagerOptions // Configuration options for the TaskManager (TaskQ, etc.) + userAgent string // User agent for all outgoing requests + chainService chain.Service // Chain service + arcConfig chainmodels.ARCConfig // Configuration for ARC + bhsConfig chainmodels.BHSConfig // Configuration for BHS + txCallbackConfig *txCallbackConfig // Configuration for TX callback received from ARC; disabled if nil } txCallbackConfig struct { @@ -182,7 +182,7 @@ func NewClient(ctx context.Context, opts ...ClientOps) (ClientInterface, error) return nil, err } - if err = client.loadTransactionDraftService(); err != nil { + if err = client.loadTransactionOutlinesService(); err != nil { return nil, err } diff --git a/engine/client_internal.go b/engine/client_internal.go index e5882a3d..93d396c4 100644 --- a/engine/client_internal.go +++ b/engine/client_internal.go @@ -14,7 +14,7 @@ import ( "github.com/bitcoin-sv/spv-wallet/engine/paymailaddress" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/taskmanager" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines" "github.com/mrz1836/go-cachestore" ) @@ -196,10 +196,10 @@ func (c *Client) loadPaymailAddressService() error { return nil } -func (c *Client) loadTransactionDraftService() error { - if c.options.transactionDraftService == nil { - logger := c.Logger().With().Str("subservice", "transactionDraft").Logger() - c.options.transactionDraftService = draft.NewDraftService(c.PaymailService(), c.options.paymailAddressService, logger) +func (c *Client) loadTransactionOutlinesService() error { + if c.options.transactionOutlinesService == nil { + logger := c.Logger().With().Str("subservice", "transactionOutlines").Logger() + c.options.transactionOutlinesService = outlines.NewService(c.PaymailService(), c.options.paymailAddressService, logger) } return nil } diff --git a/engine/client_options.go b/engine/client_options.go index 621e9e8f..017951df 100644 --- a/engine/client_options.go +++ b/engine/client_options.go @@ -88,8 +88,8 @@ func defaultClientOptions() *clientOptions { }, }, - // Blank transaction draft config - transactionDraftService: nil, + // Blank transaction outline + transactionOutlinesService: nil, // Blank TaskManager config taskManager: &taskManagerOptions{ diff --git a/engine/client_transaction.go b/engine/client_transaction.go index d9fe4fe4..9ac523cc 100644 --- a/engine/client_transaction.go +++ b/engine/client_transaction.go @@ -1,13 +1,13 @@ package engine import ( - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines" ) -// TransactionDraftService will return the draft.Service if it exists -func (c *Client) TransactionDraftService() draft.Service { - if c.options.transactionDraftService != nil { - return c.options.transactionDraftService +// TransactionOutlinesService will return the outlines.Service if it exists +func (c *Client) TransactionOutlinesService() outlines.Service { + if c.options.transactionOutlinesService != nil { + return c.options.transactionOutlinesService } return nil } diff --git a/engine/interface.go b/engine/interface.go index af8fdd8c..121f5afe 100644 --- a/engine/interface.go +++ b/engine/interface.go @@ -14,7 +14,7 @@ import ( "github.com/bitcoin-sv/spv-wallet/engine/notifications" paymailclient "github.com/bitcoin-sv/spv-wallet/engine/paymail" "github.com/bitcoin-sv/spv-wallet/engine/taskmanager" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines" "github.com/mrz1836/go-cachestore" "github.com/rs/zerolog" ) @@ -57,7 +57,7 @@ type ClientService interface { Notifications() *notifications.Notifications PaymailClient() paymail.ClientInterface PaymailService() paymailclient.ServiceClient - TransactionDraftService() draft.Service + TransactionOutlinesService() outlines.Service Taskmanager() taskmanager.TaskEngine } diff --git a/engine/transaction/draft/create_draft_test.go b/engine/transaction/draft/create_draft_test.go deleted file mode 100644 index 5b52db8c..00000000 --- a/engine/transaction/draft/create_draft_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package draft_test - -import ( - "context" - "testing" - - "github.com/bitcoin-sv/spv-wallet/engine/tester/fixtures" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft/outputs" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft/testabilities" - txerrors "github.com/bitcoin-sv/spv-wallet/engine/transaction/errors" - "github.com/bitcoin-sv/spv-wallet/models" -) - -func TestCreateTransactionDraftError(t *testing.T) { - errorTests := map[string]struct { - spec *draft.TransactionSpec - expectedError models.SPVError - }{ - "return error for nil as transaction spec": { - spec: nil, - expectedError: txerrors.ErrDraftSpecificationRequired, - }, - "return error for transaction spec without xPub Id": { - spec: &draft.TransactionSpec{}, - expectedError: txerrors.ErrDraftSpecificationXPubIDRequired, - }, - "return error for no outputs in transaction spec": { - spec: &draft.TransactionSpec{XPubID: fixtures.Sender.XPubID()}, - expectedError: txerrors.ErrDraftRequiresAtLeastOneOutput, - }, - "return error for empty output list in transaction spec": { - spec: &draft.TransactionSpec{ - XPubID: fixtures.Sender.XPubID(), - Outputs: outputs.NewSpecifications(), - }, - expectedError: txerrors.ErrDraftRequiresAtLeastOneOutput, - }, - } - for name, test := range errorTests { - t.Run(name, func(t *testing.T) { - given, then := testabilities.New(t) - - // given: - draftService := given.NewDraftTransactionService() - - // when: - tx, err := draftService.Create(context.Background(), test.spec) - - // then: - then.Created(tx).WithError(err).ThatIs(test.expectedError) - }) - } -} diff --git a/engine/transaction/draft/testabilities/ability_draft_transaction.go b/engine/transaction/draft/testabilities/ability_draft_transaction.go deleted file mode 100644 index 71cc64c3..00000000 --- a/engine/transaction/draft/testabilities/ability_draft_transaction.go +++ /dev/null @@ -1,7 +0,0 @@ -package testabilities - -import "testing" - -func New(t testing.TB) (given DraftTransactionFixture, then DraftTransactionAssertion) { - return Given(t), Then(t) -} diff --git a/engine/transaction/draft/testabilities/assert_draft_transaction.go b/engine/transaction/draft/testabilities/assert_draft_transaction.go deleted file mode 100644 index cd3953b2..00000000 --- a/engine/transaction/draft/testabilities/assert_draft_transaction.go +++ /dev/null @@ -1,177 +0,0 @@ -package testabilities - -import ( - "testing" - - sdk "github.com/bitcoin-sv/go-sdk/transaction" - "github.com/bitcoin-sv/spv-wallet/engine/transaction" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft" - "github.com/bitcoin-sv/spv-wallet/models/bsv" - "github.com/bitcoin-sv/spv-wallet/models/transaction/bucket" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -type DraftTransactionAssertion interface { - Created(transaction *draft.Transaction) CreatedDraftTransactionAssertion -} - -type CreatedDraftTransactionAssertion interface { - WithNoError(err error) SuccessfullyCreatedDraftTransactionAssertion - WithError(err error) ErrorCreationDraftTransactionAssertion -} - -type ErrorCreationDraftTransactionAssertion interface { - ThatIs(expectedError error) -} - -type SuccessfullyCreatedDraftTransactionAssertion interface { - WithParseableBEEFHex() WithParseableBEEFDraftTransactionAssertion -} - -type WithParseableBEEFDraftTransactionAssertion interface { - HasOutputs(count int) WithParseableBEEFDraftTransactionAssertion - HasOutput(index int, assert func(OutputAssertion)) WithParseableBEEFDraftTransactionAssertion - Output(index int) OutputAssertion -} - -type OutputAssertion interface { - HasBucket(bucket bucket.Name) OutputAssertion - HasSatoshis(satoshis bsv.Satoshis) OutputAssertion - HasLockingScript(lockingScript string) OutputAssertion - IsDataOnly() OutputAssertion - IsPaymail() DraftTransactionPaymailOutputAssertion -} - -type DraftTransactionPaymailOutputAssertion interface { - HasReceiver(receiver string) DraftTransactionPaymailOutputAssertion - HasSender(sender string) DraftTransactionPaymailOutputAssertion - HasReference(reference string) DraftTransactionPaymailOutputAssertion -} - -func Then(t testing.TB) DraftTransactionAssertion { - return &createdDraftAssertion{t: t, require: require.New(t), assert: assert.New(t)} -} - -type createdDraftAssertion struct { - t testing.TB - require *require.Assertions - assert *assert.Assertions - draft *draft.Transaction - tx *sdk.Transaction - err error -} - -func (a *createdDraftAssertion) Created(transaction *draft.Transaction) CreatedDraftTransactionAssertion { - a.draft = transaction - return a -} - -func (a *createdDraftAssertion) WithError(err error) ErrorCreationDraftTransactionAssertion { - a.assert.Nil(a.draft) - a.assert.Error(err) - a.err = err - return a -} - -func (a *createdDraftAssertion) ThatIs(expectedError error) { - a.assert.ErrorIs(a.err, expectedError) -} - -// WithNoError checks if there was no error and result is not nil. It also checks if BEEF hex is parseable. -func (a *createdDraftAssertion) WithNoError(err error) SuccessfullyCreatedDraftTransactionAssertion { - a.require.NoError(err, "Creation of draft has finished with error") - a.require.NotNil(a.draft, "Draft should be created if there is no error") - return a -} - -func (a *createdDraftAssertion) WithParseableBEEFHex() WithParseableBEEFDraftTransactionAssertion { - a.t.Helper() - a.t.Logf("BEEF: %s", a.draft.BEEF) - - var err error - a.tx, err = sdk.NewTransactionFromBEEFHex(a.draft.BEEF) - a.require.NoErrorf(err, "Draft has invalid BEEF hex: %s", a.draft.BEEF) - return a -} - -func (a *createdDraftAssertion) HasOutputs(count int) WithParseableBEEFDraftTransactionAssertion { - a.require.Lenf(a.tx.Outputs, count, "BEEF of draft transaction has invalid number of outputs") - a.require.Lenf(a.draft.Annotations.Outputs, count, "Annotations of draft transaction has invalid number of outputs") - return a -} - -type draftTransactionOutputAssertion struct { - parent *createdDraftAssertion - assert *assert.Assertions - require *require.Assertions - txout *sdk.TransactionOutput - annotation *transaction.OutputAnnotation - index int -} - -func (a *createdDraftAssertion) HasOutput(index int, assert func(OutputAssertion)) WithParseableBEEFDraftTransactionAssertion { - assert(a.Output(index)) - return a -} - -func (a *createdDraftAssertion) Output(index int) OutputAssertion { - a.require.Greater(len(a.tx.Outputs), index, "Draft transaction outputs has no element at index %d", index) - a.require.Greater(len(a.draft.Annotations.Outputs), index, "Draft transaction annotation outputs has no element at index %d", index) - - return &draftTransactionOutputAssertion{ - parent: a, - assert: a.assert, - require: a.require, - txout: a.tx.Outputs[index], - annotation: a.draft.Annotations.Outputs[index], - index: index, - } -} - -func (a *draftTransactionOutputAssertion) HasBucket(bucket bucket.Name) OutputAssertion { - a.assert.Equal(bucket, a.annotation.Bucket, "Output %d has invalid bucket annotation", a.index) - return a -} - -func (a *draftTransactionOutputAssertion) HasSatoshis(satoshis bsv.Satoshis) OutputAssertion { - a.assert.EqualValues(satoshis, a.txout.Satoshis, "Output %d has invalid satoshis value", a.index) - return a -} - -func (a *draftTransactionOutputAssertion) HasLockingScript(lockingScript string) OutputAssertion { - a.assert.Equal(lockingScript, a.txout.LockingScriptHex(), "Output %d has invalid locking script", a.index) - return a -} - -func (a *draftTransactionOutputAssertion) IsDataOnly() OutputAssertion { - a.assert.Zerof(a.txout.Satoshis, "Output %d has value in satoshis which is not allowed for data only outputs", a.index) - a.assert.True(a.txout.LockingScript.IsData(), "Output %d has locking script which is not data script", a.index) - return a -} - -func (a *draftTransactionOutputAssertion) IsPaymail() DraftTransactionPaymailOutputAssertion { - a.assert.NotNil(a.annotation.Paymail, "Output %d is not a paymail output", a.index) - return a -} - -func (a *draftTransactionOutputAssertion) HasReceiver(receiver string) DraftTransactionPaymailOutputAssertion { - if a.annotation.Paymail != nil { - a.assert.Equal(receiver, a.annotation.Paymail.Receiver, "Output %d has invalid paymail receiver", a.index) - } - return a -} - -func (a *draftTransactionOutputAssertion) HasSender(sender string) DraftTransactionPaymailOutputAssertion { - if a.annotation.Paymail != nil { - a.assert.Equal(sender, a.annotation.Paymail.Sender, "Output %d has invalid paymail sender", a.index) - } - return a -} - -func (a *draftTransactionOutputAssertion) HasReference(reference string) DraftTransactionPaymailOutputAssertion { - if a.annotation.Paymail != nil { - a.assert.Equal(reference, a.annotation.Paymail.Reference, "Output %d has invalid paymail reference", a.index) - } - return a -} diff --git a/engine/transaction/draft/testabilities/fixture_draft_transaction.go b/engine/transaction/draft/testabilities/fixture_draft_transaction.go deleted file mode 100644 index 1015413f..00000000 --- a/engine/transaction/draft/testabilities/fixture_draft_transaction.go +++ /dev/null @@ -1,42 +0,0 @@ -package testabilities - -import ( - "testing" - - tpaymail "github.com/bitcoin-sv/spv-wallet/engine/paymail/testabilities" - tpaymailaddress "github.com/bitcoin-sv/spv-wallet/engine/paymailaddress/testabilities" - "github.com/bitcoin-sv/spv-wallet/engine/tester" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft" -) - -// DraftTransactionFixture is a test fixture - used for establishing environment for test. -type DraftTransactionFixture interface { - NewDraftTransactionService() draft.Service - ExternalRecipientHost() tpaymail.PaymailHostFixture -} - -type draftTransactionAbility struct { - t testing.TB - paymailClientAbility tpaymail.PaymailClientFixture - paymailAddressAbility tpaymailaddress.PaymailAddressServiceFixture -} - -// Given creates a new test fixture. -func Given(t testing.TB) (given DraftTransactionFixture) { - ability := &draftTransactionAbility{ - t: t, - paymailClientAbility: tpaymail.Given(t), - paymailAddressAbility: tpaymailaddress.Given(t), - } - return ability -} - -// ExternalRecipientHost returns test fixture for setting up mocked paymail host. -func (a *draftTransactionAbility) ExternalRecipientHost() tpaymail.PaymailHostFixture { - return a.paymailClientAbility.ExternalPaymailHost() -} - -// NewDraftTransactionService creates a new draft transaction service to use in tests. -func (a *draftTransactionAbility) NewDraftTransactionService() draft.Service { - return draft.NewDraftService(a.paymailClientAbility.NewPaymailClientService(), a.paymailAddressAbility.NewPaymailAddressService(), tester.Logger(a.t)) -} diff --git a/engine/transaction/errors/errors.go b/engine/transaction/errors/errors.go index d731eb2a..4aaea360 100644 --- a/engine/transaction/errors/errors.go +++ b/engine/transaction/errors/errors.go @@ -3,36 +3,36 @@ package txerrors import "github.com/bitcoin-sv/spv-wallet/models" var ( - // ErrDraftSpecificationRequired is returned when a draft is created with no specification. - ErrDraftSpecificationRequired = models.SPVError{Code: "draft-spec-required", Message: "draft requires a specification", StatusCode: 400} + // ErrTxOutlineSpecificationRequired is returned when a transaction outline is created with no specification. + ErrTxOutlineSpecificationRequired = models.SPVError{Code: "tx-spec-spec-required", Message: "transaction outline requires a specification", StatusCode: 400} - // ErrDraftSpecificationXPubIDRequired is returned when a draft is created without xPubID. - ErrDraftSpecificationXPubIDRequired = models.SPVError{Code: "draft-spec-xpub-id-required", Message: "cannot create draft without knowledge about xPubID", StatusCode: 500} + // ErrTxOutlineSpecificationXPubIDRequired is returned when a transaction outline is created without xPubID. + ErrTxOutlineSpecificationXPubIDRequired = models.SPVError{Code: "tx-spec-spec-xpub-id-required", Message: "cannot create transaction outline without knowledge about xPubID", StatusCode: 500} - // ErrDraftRequiresAtLeastOneOutput is returned when a draft is created with no outputs. - ErrDraftRequiresAtLeastOneOutput = models.SPVError{Code: "draft-output-required", Message: "draft requires at least one output", StatusCode: 400} + // ErrTxOutlineRequiresAtLeastOneOutput is returned when a transaction outline is created with no outputs. + ErrTxOutlineRequiresAtLeastOneOutput = models.SPVError{Code: "tx-spec-output-required", Message: "transaction outline requires at least one output", StatusCode: 400} - // ErrDraftOpReturnDataRequired is returned when an OP_RETURN output is created with no data. - ErrDraftOpReturnDataRequired = models.SPVError{Code: "draft-op-return-data-required", Message: "data is required for OP_RETURN output", StatusCode: 400} + // ErrTxOutlineOpReturnDataRequired is returned when an OP_RETURN output is created with no data. + ErrTxOutlineOpReturnDataRequired = models.SPVError{Code: "tx-spec-op-return-data-required", Message: "data is required for OP_RETURN output", StatusCode: 400} - // ErrDraftOpReturnDataTooLarge is returned when OP_RETURN data part is too big to add to transaction. - ErrDraftOpReturnDataTooLarge = models.SPVError{Code: "draft-op-return-data-too-large", Message: "OP_RETURN data is too large", StatusCode: 400} + // ErrTxOutlineOpReturnDataTooLarge is returned when OP_RETURN data part is too big to add to transaction. + ErrTxOutlineOpReturnDataTooLarge = models.SPVError{Code: "tx-spec-op-return-data-too-large", Message: "OP_RETURN data is too large", StatusCode: 400} - // ErrDraftOpReturnUnsupportedDataType is returned when the data type for an OP_RETURN output is unsupported. - ErrDraftOpReturnUnsupportedDataType = models.SPVError{Code: "draft-op-return-data-type-unsupported", Message: "unsupported data type for OP_RETURN output", StatusCode: 400} + // ErrTxOutlineOpReturnUnsupportedDataType is returned when the data type for an OP_RETURN output is unsupported. + ErrTxOutlineOpReturnUnsupportedDataType = models.SPVError{Code: "tx-spec-op-return-data-type-unsupported", Message: "unsupported data type for OP_RETURN output", StatusCode: 400} - // ErrDraftSenderPaymailAddressNoDefault is when it is not possible to determine the default address for the sender. - ErrDraftSenderPaymailAddressNoDefault = models.SPVError{Message: "cannot choose paymail address of the sender", StatusCode: 400, Code: "error-draft-paymail-address-no-default"} + // ErrTxOutlineSenderPaymailAddressNoDefault is when it is not possible to determine the default address for the sender. + ErrTxOutlineSenderPaymailAddressNoDefault = models.SPVError{Code: "error-tx-spec-paymail-address-no-default", Message: "cannot choose paymail address of the sender", StatusCode: 400} // ErrFailedToDecodeHex is returned when hex decoding fails. ErrFailedToDecodeHex = models.SPVError{Code: "failed-to-decode-hex", Message: "failed to decode hex", StatusCode: 400} // ErrReceiverPaymailAddressIsInvalid is when the receiver paymail address is NOT alias@domain.com - ErrReceiverPaymailAddressIsInvalid = models.SPVError{Message: "receiver paymail address is invalid", StatusCode: 400, Code: "error-paymail-address-invalid-receiver"} + ErrReceiverPaymailAddressIsInvalid = models.SPVError{Code: "error-paymail-address-invalid-receiver", Message: "receiver paymail address is invalid", StatusCode: 400} // ErrSenderPaymailAddressIsInvalid is when the sender paymail address is NOT alias@domain.com - ErrSenderPaymailAddressIsInvalid = models.SPVError{Message: "sender paymail address is invalid", StatusCode: 400, Code: "error-paymail-address-invalid-sender"} + ErrSenderPaymailAddressIsInvalid = models.SPVError{Code: "error-paymail-address-invalid-sender", Message: "sender paymail address is invalid", StatusCode: 400} // ErrOutputValueTooLow is when the satoshis output is too low for a given type of output. - ErrOutputValueTooLow = models.SPVError{Message: "output value is too low", StatusCode: 400, Code: "error-transaction-output-value-too-low"} + ErrOutputValueTooLow = models.SPVError{Code: "error-transaction-output-value-too-low", Message: "output value is too low", StatusCode: 400} ) diff --git a/engine/transaction/outlines/create_draft_test.go b/engine/transaction/outlines/create_draft_test.go new file mode 100644 index 00000000..c9a8f327 --- /dev/null +++ b/engine/transaction/outlines/create_draft_test.go @@ -0,0 +1,54 @@ +package outlines_test + +import ( + "context" + "testing" + + "github.com/bitcoin-sv/spv-wallet/engine/tester/fixtures" + txerrors "github.com/bitcoin-sv/spv-wallet/engine/transaction/errors" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines/outputs" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines/testabilities" + "github.com/bitcoin-sv/spv-wallet/models" +) + +func TestCreateTransactionOutlineError(t *testing.T) { + errorTests := map[string]struct { + spec *outlines.TransactionSpec + expectedError models.SPVError + }{ + "return error for nil as transaction spec": { + spec: nil, + expectedError: txerrors.ErrTxOutlineSpecificationRequired, + }, + "return error for transaction spec without xPub Id": { + spec: &outlines.TransactionSpec{}, + expectedError: txerrors.ErrTxOutlineSpecificationXPubIDRequired, + }, + "return error for no outputs in transaction spec": { + spec: &outlines.TransactionSpec{XPubID: fixtures.Sender.XPubID()}, + expectedError: txerrors.ErrTxOutlineRequiresAtLeastOneOutput, + }, + "return error for empty output list in transaction spec": { + spec: &outlines.TransactionSpec{ + XPubID: fixtures.Sender.XPubID(), + Outputs: outputs.NewSpecifications(), + }, + expectedError: txerrors.ErrTxOutlineRequiresAtLeastOneOutput, + }, + } + for name, test := range errorTests { + t.Run(name, func(t *testing.T) { + given, then := testabilities.New(t) + + // given: + service := given.NewTransactionOutlinesService() + + // when: + tx, err := service.Create(context.Background(), test.spec) + + // then: + then.Created(tx).WithError(err).ThatIs(test.expectedError) + }) + } +} diff --git a/engine/transaction/draft/create_op_return_draft_test.go b/engine/transaction/outlines/create_op_return_draft_test.go similarity index 73% rename from engine/transaction/draft/create_op_return_draft_test.go rename to engine/transaction/outlines/create_op_return_draft_test.go index 43de3369..f1f56d84 100644 --- a/engine/transaction/draft/create_op_return_draft_test.go +++ b/engine/transaction/outlines/create_op_return_draft_test.go @@ -1,4 +1,4 @@ -package draft_test +package outlines_test import ( "context" @@ -7,16 +7,16 @@ import ( "testing" "github.com/bitcoin-sv/spv-wallet/engine/tester/fixtures" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft/outputs" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft/testabilities" txerrors "github.com/bitcoin-sv/spv-wallet/engine/transaction/errors" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines/outputs" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines/testabilities" "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoin-sv/spv-wallet/models/request/opreturn" "github.com/bitcoin-sv/spv-wallet/models/transaction/bucket" ) -func TestCreateOpReturnDraft(t *testing.T) { +func TestCreateOpReturnTransactionOutline(t *testing.T) { // maxOpPushDataSize is the maximum size of the data chunk that can be pushed to transaction with OP_PUSH operation. const maxOpPushDataSize = 0xFFFFFFFF @@ -24,28 +24,28 @@ func TestCreateOpReturnDraft(t *testing.T) { opReturn *outputs.OpReturn lockingScript string }{ - "return draft for single string": { + "return transaction outline for single string": { opReturn: &outputs.OpReturn{ DataType: opreturn.DataTypeStrings, Data: []string{"Example data"}, }, lockingScript: "006a0c4578616d706c652064617461", }, - "return draft for multiple strings": { + "return transaction outline for multiple strings": { opReturn: &outputs.OpReturn{ DataType: opreturn.DataTypeStrings, Data: []string{"Example", " ", "data"}, }, lockingScript: "006a074578616d706c6501200464617461", }, - "return draft for single hex": { + "return transaction outline for single hex": { opReturn: &outputs.OpReturn{ DataType: opreturn.DataTypeHexes, Data: []string{toHex("Example data")}, }, lockingScript: "006a0c4578616d706c652064617461", }, - "return draft for multiple hexes": { + "return transaction outline for multiple hexes": { opReturn: &outputs.OpReturn{ DataType: opreturn.DataTypeHexes, Data: []string{toHex("Example"), toHex(" "), toHex("data")}, @@ -58,19 +58,19 @@ func TestCreateOpReturnDraft(t *testing.T) { given, then := testabilities.New(t) // given: - draftService := given.NewDraftTransactionService() + service := given.NewTransactionOutlinesService() // and: - spec := &draft.TransactionSpec{ + spec := &outlines.TransactionSpec{ XPubID: fixtures.Sender.XPubID(), Outputs: outputs.NewSpecifications(test.opReturn), } // when: - draftTx, err := draftService.Create(context.Background(), spec) + tx, err := service.Create(context.Background(), spec) // then: - thenTx := then.Created(draftTx).WithNoError(err).WithParseableBEEFHex() + thenTx := then.Created(tx).WithNoError(err).WithParseableBEEFHex() thenTx.HasOutputs(1) @@ -87,13 +87,13 @@ func TestCreateOpReturnDraft(t *testing.T) { }{ "return error for no data in default type": { spec: &outputs.OpReturn{}, - expectedError: txerrors.ErrDraftOpReturnDataRequired, + expectedError: txerrors.ErrTxOutlineOpReturnDataRequired, }, "return error for no data string type": { spec: &outputs.OpReturn{ DataType: opreturn.DataTypeStrings, }, - expectedError: txerrors.ErrDraftOpReturnDataRequired, + expectedError: txerrors.ErrTxOutlineOpReturnDataRequired, }, "return error for invalid hex": { spec: &outputs.OpReturn{ @@ -107,14 +107,14 @@ func TestCreateOpReturnDraft(t *testing.T) { DataType: 123, Data: []string{"Example", " ", "data"}, }, - expectedError: txerrors.ErrDraftOpReturnUnsupportedDataType, + expectedError: txerrors.ErrTxOutlineOpReturnUnsupportedDataType, }, "return error for to big string": { spec: &outputs.OpReturn{ DataType: opreturn.DataTypeStrings, Data: []string{strings.Repeat("1", maxOpPushDataSize+1)}, }, - expectedError: txerrors.ErrDraftOpReturnDataTooLarge, + expectedError: txerrors.ErrTxOutlineOpReturnDataTooLarge, }, } for name, test := range errorTests { @@ -122,16 +122,16 @@ func TestCreateOpReturnDraft(t *testing.T) { given, then := testabilities.New(t) // given: - draftService := given.NewDraftTransactionService() + service := given.NewTransactionOutlinesService() // and: - spec := &draft.TransactionSpec{ + spec := &outlines.TransactionSpec{ XPubID: fixtures.Sender.XPubID(), Outputs: outputs.NewSpecifications(test.spec), } // when: - tx, err := draftService.Create(context.Background(), spec) + tx, err := service.Create(context.Background(), spec) // then: then.Created(tx).WithError(err).ThatIs(test.expectedError) diff --git a/engine/transaction/draft/create_paymail_draft_test.go b/engine/transaction/outlines/create_paymail_draft_test.go similarity index 85% rename from engine/transaction/draft/create_paymail_draft_test.go rename to engine/transaction/outlines/create_paymail_draft_test.go index e07093c5..e9e68a6c 100644 --- a/engine/transaction/draft/create_paymail_draft_test.go +++ b/engine/transaction/outlines/create_paymail_draft_test.go @@ -1,4 +1,4 @@ -package draft_test +package outlines_test import ( "context" @@ -7,32 +7,32 @@ import ( pmerrors "github.com/bitcoin-sv/spv-wallet/engine/paymail/errors" tpaymail "github.com/bitcoin-sv/spv-wallet/engine/paymail/testabilities" "github.com/bitcoin-sv/spv-wallet/engine/tester/fixtures" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft/outputs" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft/testabilities" txerrors "github.com/bitcoin-sv/spv-wallet/engine/transaction/errors" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines/outputs" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines/testabilities" "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoin-sv/spv-wallet/models/bsv" "github.com/bitcoin-sv/spv-wallet/models/optional" "github.com/bitcoin-sv/spv-wallet/models/transaction/bucket" ) -func TestCreatePaymailDraft(t *testing.T) { +func TestCreatePaymailTransactionOutline(t *testing.T) { const transactionSatoshiValue = bsv.Satoshis(1) var recipient = fixtures.RecipientExternal.DefaultPaymail() var sender = fixtures.Sender.DefaultPaymail() - t.Run("return draft with payment to valid paymail address", func(t *testing.T) { + t.Run("return transaction outline with payment to valid paymail address", func(t *testing.T) { given, then := testabilities.New(t) // given: paymailHostResponse := given.ExternalRecipientHost().WillRespondWithP2PDestinationsWithSats(transactionSatoshiValue) // and: - draftService := given.NewDraftTransactionService() + service := given.NewTransactionOutlinesService() // and: - spec := &draft.TransactionSpec{ + spec := &outlines.TransactionSpec{ XPubID: fixtures.Sender.XPubID(), Outputs: outputs.NewSpecifications(&outputs.Paymail{ To: recipient, @@ -42,10 +42,10 @@ func TestCreatePaymailDraft(t *testing.T) { } // when: - draftTx, err := draftService.Create(context.Background(), spec) + tx, err := service.Create(context.Background(), spec) // then: - thenTx := then.Created(draftTx).WithNoError(err).WithParseableBEEFHex() + thenTx := then.Created(tx).WithNoError(err).WithParseableBEEFHex() thenTx.HasOutputs(1) @@ -59,7 +59,7 @@ func TestCreatePaymailDraft(t *testing.T) { HasReference(paymailHostResponse.Reference) }) - t.Run("return draft with payment with multiple outputs from valid paymail address", func(t *testing.T) { + t.Run("return transaction outline with payment with multiple outputs from valid paymail address", func(t *testing.T) { given, then := testabilities.New(t) // given: @@ -71,10 +71,10 @@ func TestCreatePaymailDraft(t *testing.T) { paymailHostResponse := given.ExternalRecipientHost().WillRespondWithP2PDestinationsWithSats(firstOutputSatoshiValue, secondOutputSatoshiValue) // and: - draftService := given.NewDraftTransactionService() + service := given.NewTransactionOutlinesService() // and: - spec := &draft.TransactionSpec{ + spec := &outlines.TransactionSpec{ XPubID: fixtures.Sender.XPubID(), Outputs: outputs.NewSpecifications(&outputs.Paymail{ To: recipient, @@ -84,10 +84,10 @@ func TestCreatePaymailDraft(t *testing.T) { } // when: - draftTx, err := draftService.Create(context.Background(), spec) + tx, err := service.Create(context.Background(), spec) // then: - thenTx := then.Created(draftTx).WithNoError(err).WithParseableBEEFHex() + thenTx := then.Created(tx).WithNoError(err).WithParseableBEEFHex() thenTx.HasOutputs(2) @@ -110,17 +110,17 @@ func TestCreatePaymailDraft(t *testing.T) { HasReference(paymailHostResponse.Reference) }) - t.Run("return draft with default paymail in sender annotation", func(t *testing.T) { + t.Run("return transaction outline with default paymail in sender annotation", func(t *testing.T) { given, then := testabilities.New(t) // given: given.ExternalRecipientHost().WillRespondWithP2PDestinationsWithSats(transactionSatoshiValue) // and: - draftService := given.NewDraftTransactionService() + service := given.NewTransactionOutlinesService() // and: - spec := &draft.TransactionSpec{ + spec := &outlines.TransactionSpec{ XPubID: fixtures.UserWithMorePaymails.XPubID(), Outputs: outputs.NewSpecifications(&outputs.Paymail{ To: recipient, @@ -129,10 +129,10 @@ func TestCreatePaymailDraft(t *testing.T) { } // when: - draftTx, err := draftService.Create(context.Background(), spec) + tx, err := service.Create(context.Background(), spec) // then: - then.Created(draftTx).WithNoError(err).WithParseableBEEFHex(). + then.Created(tx).WithNoError(err).WithParseableBEEFHex(). Output(0). IsPaymail(). HasSender(fixtures.UserWithMorePaymails.DefaultPaymail()) @@ -257,7 +257,7 @@ func TestCreatePaymailDraft(t *testing.T) { To: recipient, Satoshis: transactionSatoshiValue, }, - expectedError: txerrors.ErrDraftSenderPaymailAddressNoDefault, + expectedError: txerrors.ErrTxOutlineSenderPaymailAddressNoDefault, }, } for name, test := range errorTests { @@ -268,16 +268,16 @@ func TestCreatePaymailDraft(t *testing.T) { given.ExternalRecipientHost().WillRespondWithP2PDestinationsWithSats(transactionSatoshiValue) // and: - draftService := given.NewDraftTransactionService() + service := given.NewTransactionOutlinesService() // and: - spec := &draft.TransactionSpec{ + spec := &outlines.TransactionSpec{ XPubID: test.user.XPubID(), Outputs: outputs.NewSpecifications(test.spec), } // when: - tx, err := draftService.Create(context.Background(), spec) + tx, err := service.Create(context.Background(), spec) // then: then.Created(tx).WithError(err).ThatIs(test.expectedError) @@ -333,10 +333,10 @@ func TestCreatePaymailDraft(t *testing.T) { test.paymailHostScenario(given.ExternalRecipientHost()) // given: - draftService := given.NewDraftTransactionService() + service := given.NewTransactionOutlinesService() // and: - spec := &draft.TransactionSpec{ + spec := &outlines.TransactionSpec{ XPubID: fixtures.Sender.XPubID(), Outputs: outputs.NewSpecifications(&outputs.Paymail{ To: recipient, @@ -345,7 +345,7 @@ func TestCreatePaymailDraft(t *testing.T) { } // when: - tx, err := draftService.Create(context.Background(), spec) + tx, err := service.Create(context.Background(), spec) // then: then.Created(tx).WithError(err).ThatIs(test.expectedError) diff --git a/engine/transaction/draft/inteface.go b/engine/transaction/outlines/inteface.go similarity index 61% rename from engine/transaction/draft/inteface.go rename to engine/transaction/outlines/inteface.go index 759c33b7..cb747849 100644 --- a/engine/transaction/draft/inteface.go +++ b/engine/transaction/outlines/inteface.go @@ -1,8 +1,8 @@ -package draft +package outlines import "context" -// Service is a service for creating draft transactions. +// Service is a service for creating transaction outlines. type Service interface { Create(ctx context.Context, spec *TransactionSpec) (*Transaction, error) } diff --git a/engine/transaction/draft/evaluation/context.go b/engine/transaction/outlines/internal/evaluation/context.go similarity index 100% rename from engine/transaction/draft/evaluation/context.go rename to engine/transaction/outlines/internal/evaluation/context.go diff --git a/engine/transaction/draft/evaluation/interface.go b/engine/transaction/outlines/internal/evaluation/interface.go similarity index 80% rename from engine/transaction/draft/evaluation/interface.go rename to engine/transaction/outlines/internal/evaluation/interface.go index 36885e44..c6b406c2 100644 --- a/engine/transaction/draft/evaluation/interface.go +++ b/engine/transaction/outlines/internal/evaluation/interface.go @@ -8,7 +8,7 @@ import ( "github.com/rs/zerolog" ) -// Context is a context for the evaluation of a transaction draft specification. +// Context is a context for the evaluation of a transaction outline specification. type Context interface { context.Context XPubID() string diff --git a/engine/transaction/draft/outputs/op_return.go b/engine/transaction/outlines/outputs/op_return.go similarity index 85% rename from engine/transaction/draft/outputs/op_return.go rename to engine/transaction/outlines/outputs/op_return.go index cc39cb9f..89cd22f4 100644 --- a/engine/transaction/draft/outputs/op_return.go +++ b/engine/transaction/outlines/outputs/op_return.go @@ -8,8 +8,8 @@ import ( sdk "github.com/bitcoin-sv/go-sdk/transaction" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/transaction" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft/evaluation" txerrors "github.com/bitcoin-sv/spv-wallet/engine/transaction/errors" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines/internal/evaluation" "github.com/bitcoin-sv/spv-wallet/models/request/opreturn" ) @@ -18,7 +18,7 @@ type OpReturn opreturn.Output func (o *OpReturn) evaluate(evaluation.Context) (annotatedOutputs, error) { if len(o.Data) == 0 { - return nil, txerrors.ErrDraftOpReturnDataRequired + return nil, txerrors.ErrTxOutlineOpReturnDataRequired } data, err := o.getData() @@ -29,7 +29,7 @@ func (o *OpReturn) evaluate(evaluation.Context) (annotatedOutputs, error) { output, err := sdk.CreateOpReturnOutput(data) if err != nil { if errors.Is(err, script.ErrPartTooBig) { - return nil, txerrors.ErrDraftOpReturnDataTooLarge + return nil, txerrors.ErrTxOutlineOpReturnDataTooLarge } return nil, spverrors.Wrapf(err, "failed to create OP_RETURN output") } @@ -61,6 +61,6 @@ func toBytes(data string, dataType opreturn.DataType) ([]byte, error) { } return dataHex, nil default: - return nil, txerrors.ErrDraftOpReturnUnsupportedDataType + return nil, txerrors.ErrTxOutlineOpReturnUnsupportedDataType } } diff --git a/engine/transaction/draft/outputs/paymail.go b/engine/transaction/outlines/outputs/paymail.go similarity index 95% rename from engine/transaction/draft/outputs/paymail.go rename to engine/transaction/outlines/outputs/paymail.go index f0b118ac..1dbd093c 100644 --- a/engine/transaction/draft/outputs/paymail.go +++ b/engine/transaction/outlines/outputs/paymail.go @@ -9,8 +9,8 @@ import ( pmerrors "github.com/bitcoin-sv/spv-wallet/engine/paymail/errors" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/transaction" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft/evaluation" txerrors "github.com/bitcoin-sv/spv-wallet/engine/transaction/errors" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines/internal/evaluation" paymailreq "github.com/bitcoin-sv/spv-wallet/models/request/paymail" "github.com/bitcoin-sv/spv-wallet/models/transaction/bucket" ) @@ -110,7 +110,7 @@ func (p *Paymail) validateProvidedSenderPaymail(ctx evaluation.Context) error { func (p *Paymail) defaultSenderAddress(ctx evaluation.Context) (string, error) { sender, err := ctx.PaymailAddressService().GetDefaultPaymailAddress(ctx, ctx.XPubID()) if err != nil { - return "", txerrors.ErrDraftSenderPaymailAddressNoDefault.Wrap(err) + return "", txerrors.ErrTxOutlineSenderPaymailAddressNoDefault.Wrap(err) } return sender, nil } diff --git a/engine/transaction/draft/outputs/spec.go b/engine/transaction/outlines/outputs/spec.go similarity index 94% rename from engine/transaction/draft/outputs/spec.go rename to engine/transaction/outlines/outputs/spec.go index b43c5732..afc73cf8 100644 --- a/engine/transaction/draft/outputs/spec.go +++ b/engine/transaction/outlines/outputs/spec.go @@ -4,8 +4,8 @@ import ( sdk "github.com/bitcoin-sv/go-sdk/transaction" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/transaction" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft/evaluation" txerrors "github.com/bitcoin-sv/spv-wallet/engine/transaction/errors" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines/internal/evaluation" ) // Specifications are representing a client specification for outputs part of the transaction. @@ -33,7 +33,7 @@ func (s *Specifications) Add(output Spec) { // Evaluate the outputs specifications and return the transaction outputs and their annotations. func (s *Specifications) Evaluate(ctx evaluation.Context) ([]*sdk.TransactionOutput, transaction.OutputsAnnotations, error) { if s.Outputs == nil { - return nil, nil, txerrors.ErrDraftRequiresAtLeastOneOutput + return nil, nil, txerrors.ErrTxOutlineRequiresAtLeastOneOutput } outputs, err := s.evaluate(ctx) if err != nil { diff --git a/engine/transaction/outlines/testabilities/ability_draft_transaction.go b/engine/transaction/outlines/testabilities/ability_draft_transaction.go new file mode 100644 index 00000000..f436ab80 --- /dev/null +++ b/engine/transaction/outlines/testabilities/ability_draft_transaction.go @@ -0,0 +1,7 @@ +package testabilities + +import "testing" + +func New(t testing.TB) (given TransactionOutlineFixture, then TransactionOutlineAssertion) { + return Given(t), Then(t) +} diff --git a/engine/transaction/outlines/testabilities/assert_draft_transaction.go b/engine/transaction/outlines/testabilities/assert_draft_transaction.go new file mode 100644 index 00000000..92ba6055 --- /dev/null +++ b/engine/transaction/outlines/testabilities/assert_draft_transaction.go @@ -0,0 +1,177 @@ +package testabilities + +import ( + "testing" + + sdk "github.com/bitcoin-sv/go-sdk/transaction" + "github.com/bitcoin-sv/spv-wallet/engine/transaction" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines" + "github.com/bitcoin-sv/spv-wallet/models/bsv" + "github.com/bitcoin-sv/spv-wallet/models/transaction/bucket" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type TransactionOutlineAssertion interface { + Created(transaction *outlines.Transaction) CreatedTransactionOutlineAssertion +} + +type CreatedTransactionOutlineAssertion interface { + WithNoError(err error) SuccessfullyCreatedTransactionOutlineAssertion + WithError(err error) ErrorCreationTransactionOutlineAssertion +} + +type ErrorCreationTransactionOutlineAssertion interface { + ThatIs(expectedError error) +} + +type SuccessfullyCreatedTransactionOutlineAssertion interface { + WithParseableBEEFHex() WithParseableBEEFTransactionOutlineAssertion +} + +type WithParseableBEEFTransactionOutlineAssertion interface { + HasOutputs(count int) WithParseableBEEFTransactionOutlineAssertion + HasOutput(index int, assert func(OutputAssertion)) WithParseableBEEFTransactionOutlineAssertion + Output(index int) OutputAssertion +} + +type OutputAssertion interface { + HasBucket(bucket bucket.Name) OutputAssertion + HasSatoshis(satoshis bsv.Satoshis) OutputAssertion + HasLockingScript(lockingScript string) OutputAssertion + IsDataOnly() OutputAssertion + IsPaymail() TransactionOutlinePaymailOutputAssertion +} + +type TransactionOutlinePaymailOutputAssertion interface { + HasReceiver(receiver string) TransactionOutlinePaymailOutputAssertion + HasSender(sender string) TransactionOutlinePaymailOutputAssertion + HasReference(reference string) TransactionOutlinePaymailOutputAssertion +} + +func Then(t testing.TB) TransactionOutlineAssertion { + return &assertion{t: t, require: require.New(t), assert: assert.New(t)} +} + +type assertion struct { + t testing.TB + require *require.Assertions + assert *assert.Assertions + txOutline *outlines.Transaction + tx *sdk.Transaction + err error +} + +func (a *assertion) Created(transaction *outlines.Transaction) CreatedTransactionOutlineAssertion { + a.txOutline = transaction + return a +} + +func (a *assertion) WithError(err error) ErrorCreationTransactionOutlineAssertion { + a.assert.Nil(a.txOutline) + a.assert.Error(err) + a.err = err + return a +} + +func (a *assertion) ThatIs(expectedError error) { + a.assert.ErrorIs(a.err, expectedError) +} + +// WithNoError checks if there was no error and result is not nil. It also checks if BEEF hex is parseable. +func (a *assertion) WithNoError(err error) SuccessfullyCreatedTransactionOutlineAssertion { + a.require.NoError(err, "Creation of transaction outline has finished with error") + a.require.NotNil(a.txOutline, "Transaction outline should be created if there is no error") + return a +} + +func (a *assertion) WithParseableBEEFHex() WithParseableBEEFTransactionOutlineAssertion { + a.t.Helper() + a.t.Logf("BEEF: %s", a.txOutline.BEEF) + + var err error + a.tx, err = sdk.NewTransactionFromBEEFHex(a.txOutline.BEEF) + a.require.NoErrorf(err, "Transaction outline has invalid BEEF hex: %s", a.txOutline.BEEF) + return a +} + +func (a *assertion) HasOutputs(count int) WithParseableBEEFTransactionOutlineAssertion { + a.require.Lenf(a.tx.Outputs, count, "BEEF of transaction outline has invalid number of outputs") + a.require.Lenf(a.txOutline.Annotations.Outputs, count, "Annotations of transaction outline has invalid number of outputs") + return a +} + +type txOutputAssertion struct { + parent *assertion + assert *assert.Assertions + require *require.Assertions + txout *sdk.TransactionOutput + annotation *transaction.OutputAnnotation + index int +} + +func (a *assertion) HasOutput(index int, assert func(OutputAssertion)) WithParseableBEEFTransactionOutlineAssertion { + assert(a.Output(index)) + return a +} + +func (a *assertion) Output(index int) OutputAssertion { + a.require.Greater(len(a.tx.Outputs), index, "Transaction outline outputs has no element at index %d", index) + a.require.Greater(len(a.txOutline.Annotations.Outputs), index, "Transaction outline annotation outputs has no element at index %d", index) + + return &txOutputAssertion{ + parent: a, + assert: a.assert, + require: a.require, + txout: a.tx.Outputs[index], + annotation: a.txOutline.Annotations.Outputs[index], + index: index, + } +} + +func (a *txOutputAssertion) HasBucket(bucket bucket.Name) OutputAssertion { + a.assert.Equal(bucket, a.annotation.Bucket, "Output %d has invalid bucket annotation", a.index) + return a +} + +func (a *txOutputAssertion) HasSatoshis(satoshis bsv.Satoshis) OutputAssertion { + a.assert.EqualValues(satoshis, a.txout.Satoshis, "Output %d has invalid satoshis value", a.index) + return a +} + +func (a *txOutputAssertion) HasLockingScript(lockingScript string) OutputAssertion { + a.assert.Equal(lockingScript, a.txout.LockingScriptHex(), "Output %d has invalid locking script", a.index) + return a +} + +func (a *txOutputAssertion) IsDataOnly() OutputAssertion { + a.assert.Zerof(a.txout.Satoshis, "Output %d has value in satoshis which is not allowed for data only outputs", a.index) + a.assert.True(a.txout.LockingScript.IsData(), "Output %d has locking script which is not data script", a.index) + return a +} + +func (a *txOutputAssertion) IsPaymail() TransactionOutlinePaymailOutputAssertion { + a.assert.NotNil(a.annotation.Paymail, "Output %d is not a paymail output", a.index) + return a +} + +func (a *txOutputAssertion) HasReceiver(receiver string) TransactionOutlinePaymailOutputAssertion { + if a.annotation.Paymail != nil { + a.assert.Equal(receiver, a.annotation.Paymail.Receiver, "Output %d has invalid paymail receiver", a.index) + } + return a +} + +func (a *txOutputAssertion) HasSender(sender string) TransactionOutlinePaymailOutputAssertion { + if a.annotation.Paymail != nil { + a.assert.Equal(sender, a.annotation.Paymail.Sender, "Output %d has invalid paymail sender", a.index) + } + return a +} + +func (a *txOutputAssertion) HasReference(reference string) TransactionOutlinePaymailOutputAssertion { + if a.annotation.Paymail != nil { + a.assert.Equal(reference, a.annotation.Paymail.Reference, "Output %d has invalid paymail reference", a.index) + } + return a +} diff --git a/engine/transaction/outlines/testabilities/fixture_draft_transaction.go b/engine/transaction/outlines/testabilities/fixture_draft_transaction.go new file mode 100644 index 00000000..d21c83c4 --- /dev/null +++ b/engine/transaction/outlines/testabilities/fixture_draft_transaction.go @@ -0,0 +1,42 @@ +package testabilities + +import ( + "testing" + + tpaymail "github.com/bitcoin-sv/spv-wallet/engine/paymail/testabilities" + tpaymailaddress "github.com/bitcoin-sv/spv-wallet/engine/paymailaddress/testabilities" + "github.com/bitcoin-sv/spv-wallet/engine/tester" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines" +) + +// TransactionOutlineFixture is a test fixture - used for establishing environment for test. +type TransactionOutlineFixture interface { + NewTransactionOutlinesService() outlines.Service + ExternalRecipientHost() tpaymail.PaymailHostFixture +} + +type transactionOutlineAbility struct { + t testing.TB + paymailClientAbility tpaymail.PaymailClientFixture + paymailAddressAbility tpaymailaddress.PaymailAddressServiceFixture +} + +// Given creates a new test fixture. +func Given(t testing.TB) (given TransactionOutlineFixture) { + ability := &transactionOutlineAbility{ + t: t, + paymailClientAbility: tpaymail.Given(t), + paymailAddressAbility: tpaymailaddress.Given(t), + } + return ability +} + +// ExternalRecipientHost returns test fixture for setting up mocked paymail host. +func (a *transactionOutlineAbility) ExternalRecipientHost() tpaymail.PaymailHostFixture { + return a.paymailClientAbility.ExternalPaymailHost() +} + +// NewTransactionOutlinesService creates a new transaction outline service to use in tests. +func (a *transactionOutlineAbility) NewTransactionOutlinesService() outlines.Service { + return outlines.NewService(a.paymailClientAbility.NewPaymailClientService(), a.paymailAddressAbility.NewPaymailAddressService(), tester.Logger(a.t)) +} diff --git a/engine/transaction/draft/transaction.go b/engine/transaction/outlines/transaction.go similarity index 69% rename from engine/transaction/draft/transaction.go rename to engine/transaction/outlines/transaction.go index 0a13a914..a18cb459 100644 --- a/engine/transaction/draft/transaction.go +++ b/engine/transaction/outlines/transaction.go @@ -1,8 +1,8 @@ -package draft +package outlines import "github.com/bitcoin-sv/spv-wallet/engine/transaction" -// Transaction represents a transaction draft. +// Transaction represents a transaction outline. type Transaction struct { BEEF string Annotations *transaction.Annotations diff --git a/engine/transaction/draft/draft_service.go b/engine/transaction/outlines/transaction_outlines_service.go similarity index 64% rename from engine/transaction/draft/draft_service.go rename to engine/transaction/outlines/transaction_outlines_service.go index d84fc4df..30751a5c 100644 --- a/engine/transaction/draft/draft_service.go +++ b/engine/transaction/outlines/transaction_outlines_service.go @@ -1,4 +1,4 @@ -package draft +package outlines import ( "context" @@ -8,8 +8,8 @@ import ( "github.com/bitcoin-sv/spv-wallet/engine/paymailaddress" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/transaction" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft/evaluation" txerrors "github.com/bitcoin-sv/spv-wallet/engine/transaction/errors" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines/internal/evaluation" "github.com/rs/zerolog" ) @@ -19,14 +19,14 @@ type service struct { paymailAddressService paymailaddress.Service } -// NewDraftService creates a new draft service. -func NewDraftService(paymailService paymail.ServiceClient, paymailAddressService paymailaddress.Service, logger zerolog.Logger) Service { +// NewService creates a new transaction outlines service. +func NewService(paymailService paymail.ServiceClient, paymailAddressService paymailaddress.Service, logger zerolog.Logger) Service { if paymailService == nil { - panic("paymail.ServiceClient is required to create draft transaction service") + panic("paymail.ServiceClient is required to create transaction outlines service") } if paymailAddressService == nil { - panic("paymailaddress.Service is required to create draft transaction service") + panic("paymailaddress.Service is required to create transaction outlines service") } return &service{ @@ -36,14 +36,14 @@ func NewDraftService(paymailService paymail.ServiceClient, paymailAddressService } } -// Create creates a new draft transaction based on specification. +// Create creates a new transaction outline based on specification. func (s *service) Create(ctx context.Context, spec *TransactionSpec) (*Transaction, error) { if spec == nil { - return nil, txerrors.ErrDraftSpecificationRequired + return nil, txerrors.ErrTxOutlineSpecificationRequired } if spec.XPubID == "" { - return nil, txerrors.ErrDraftSpecificationXPubIDRequired + return nil, txerrors.ErrTxOutlineSpecificationXPubIDRequired } c := evaluation.NewContext( @@ -65,7 +65,7 @@ func (s *service) Create(ctx context.Context, spec *TransactionSpec) (*Transacti beef, err := tx.BEEFHex() if err != nil { - return nil, spverrors.Wrapf(err, "failed to create draft transaction") + return nil, spverrors.Wrapf(err, "failed to create transaction outline") } return &Transaction{ diff --git a/engine/transaction/draft/transaction_spec.go b/engine/transaction/outlines/transaction_spec.go similarity index 73% rename from engine/transaction/draft/transaction_spec.go rename to engine/transaction/outlines/transaction_spec.go index 4a3838b6..2620780e 100644 --- a/engine/transaction/draft/transaction_spec.go +++ b/engine/transaction/outlines/transaction_spec.go @@ -1,15 +1,15 @@ -package draft +package outlines import ( sdk "github.com/bitcoin-sv/go-sdk/transaction" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/transaction" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft/evaluation" - "github.com/bitcoin-sv/spv-wallet/engine/transaction/draft/outputs" txerrors "github.com/bitcoin-sv/spv-wallet/engine/transaction/errors" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines/internal/evaluation" + "github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines/outputs" ) -// TransactionSpec represents client provided specification for a transaction draft. +// TransactionSpec represents client provided specification for a transaction outline. type TransactionSpec struct { Outputs *outputs.Specifications XPubID string @@ -17,7 +17,7 @@ type TransactionSpec struct { func (t *TransactionSpec) outputs(ctx evaluation.Context) ([]*sdk.TransactionOutput, transaction.OutputsAnnotations, error) { if t.Outputs == nil { - return nil, nil, txerrors.ErrDraftRequiresAtLeastOneOutput + return nil, nil, txerrors.ErrTxOutlineRequiresAtLeastOneOutput } outs, annotations, err := t.Outputs.Evaluate(ctx) diff --git a/models/request/draft_transaction.go b/models/request/transaction_specification.go similarity index 67% rename from models/request/draft_transaction.go rename to models/request/transaction_specification.go index b9d152c5..17fa090d 100644 --- a/models/request/draft_transaction.go +++ b/models/request/transaction_specification.go @@ -4,18 +4,18 @@ import ( "encoding/json" ) -// DraftTransaction represents a request with specification for making a draft transaction. -type DraftTransaction struct { +// TransactionSpecification represents a request with specification for making a transaction outline. +type TransactionSpecification struct { Outputs []Output `json:"-"` } -// Output represents an output in a draft transaction request. +// Output represents an output in a transaction outline request. type Output interface { GetType() string } -// UnmarshalJSON custom unmarshall logic for DraftTransaction -func (dt *DraftTransaction) UnmarshalJSON(data []byte) error { +// UnmarshalJSON custom unmarshall logic for TransactionSpecification +func (dt *TransactionSpecification) UnmarshalJSON(data []byte) error { rawOutputs, err := dt.unmarshalPartials(data) if err != nil { return err @@ -31,12 +31,12 @@ func (dt *DraftTransaction) UnmarshalJSON(data []byte) error { return nil } -// unmarshalPartials unmarshalls the data into the DraftTransaction +// unmarshalPartials unmarshalls the data into the TransactionSpecification // and returns also raw parts that couldn't be unmarshalled out of the box. -func (dt *DraftTransaction) unmarshalPartials(data []byte) (rawOutputs []json.RawMessage, err error) { +func (dt *TransactionSpecification) unmarshalPartials(data []byte) (rawOutputs []json.RawMessage, err error) { // Define a temporary struct to unmarshal the struct without unmarshalling outputs. // We're defining it here, to not publish Alias type. - type Alias DraftTransaction + type Alias TransactionSpecification temp := &struct { Outputs []json.RawMessage `json:"outputs"` *Alias @@ -70,9 +70,9 @@ func unmarshalOutputs(outputs []json.RawMessage) ([]Output, error) { return result, nil } -// MarshalJSON custom marshaller for DraftTransaction -func (dt *DraftTransaction) MarshalJSON() ([]byte, error) { - type Alias DraftTransaction +// MarshalJSON custom marshaller for TransactionSpecification +func (dt *TransactionSpecification) MarshalJSON() ([]byte, error) { + type Alias TransactionSpecification temp := &struct { Outputs []any `json:"outputs"` *Alias diff --git a/models/request/draft_transaction_json.go b/models/request/transaction_specification_json.go similarity index 86% rename from models/request/draft_transaction_json.go rename to models/request/transaction_specification_json.go index 45d4fe87..9c071859 100644 --- a/models/request/draft_transaction_json.go +++ b/models/request/transaction_specification_json.go @@ -8,7 +8,7 @@ import ( paymailreq "github.com/bitcoin-sv/spv-wallet/models/request/paymail" ) -// unmarshalOutput used by DraftTransaction unmarshalling to get Output object by type +// unmarshalOutput used by TransactionSpecification unmarshalling to get Output object by type // IMPORTANT: Every time a new output type is added, it must be handled here also. func unmarshalOutput(rawOutput json.RawMessage, outputType string) (Output, error) { switch outputType { @@ -29,7 +29,7 @@ func unmarshalOutput(rawOutput json.RawMessage, outputType string) (Output, erro } } -// expandOutputForMarshaling used by DraftTransaction marshalling to expand Output object before marshalling. +// expandOutputForMarshaling used by TransactionSpecification marshalling to expand Output object before marshalling. // IMPORTANT: Every time a new output type is added, it must be handled here also. func expandOutputForMarshaling(output Output) (any, error) { switch o := output.(type) { diff --git a/models/request/draft_transaction_test.go b/models/request/transaction_specification_test.go similarity index 82% rename from models/request/draft_transaction_test.go rename to models/request/transaction_specification_test.go index ba03d5be..503c0f11 100644 --- a/models/request/draft_transaction_test.go +++ b/models/request/transaction_specification_test.go @@ -14,10 +14,10 @@ import ( "github.com/stretchr/testify/require" ) -func TestDraft_TransactionJSON(t *testing.T) { +func TestTransactionSpecification_TransactionJSON(t *testing.T) { tests := map[string]struct { - json string - draft *request.DraftTransaction + json string + spec *request.TransactionSpecification }{ "OP_RETURN output with single string": { json: `{ @@ -29,7 +29,7 @@ func TestDraft_TransactionJSON(t *testing.T) { } ] }`, - draft: &request.DraftTransaction{ + spec: &request.TransactionSpecification{ Outputs: []request.Output{ opreturn.Output{ DataType: opreturn.DataTypeStrings, @@ -48,7 +48,7 @@ func TestDraft_TransactionJSON(t *testing.T) { } ] }`, - draft: &request.DraftTransaction{ + spec: &request.TransactionSpecification{ Outputs: []request.Output{ opreturn.Output{ DataType: opreturn.DataTypeStrings, @@ -66,7 +66,7 @@ func TestDraft_TransactionJSON(t *testing.T) { } ] }`, - draft: &request.DraftTransaction{ + spec: &request.TransactionSpecification{ Outputs: []request.Output{ opreturn.Output{ DataType: opreturn.DataTypeDefault, @@ -85,7 +85,7 @@ func TestDraft_TransactionJSON(t *testing.T) { } ] }`, - draft: &request.DraftTransaction{ + spec: &request.TransactionSpecification{ Outputs: []request.Output{ opreturn.Output{ DataType: opreturn.DataTypeHexes, @@ -104,7 +104,7 @@ func TestDraft_TransactionJSON(t *testing.T) { } ] }`, - draft: &request.DraftTransaction{ + spec: &request.TransactionSpecification{ Outputs: []request.Output{ opreturn.Output{ DataType: opreturn.DataTypeHexes, @@ -123,7 +123,7 @@ func TestDraft_TransactionJSON(t *testing.T) { } ] }`, - draft: &request.DraftTransaction{ + spec: &request.TransactionSpecification{ Outputs: []request.Output{ paymailreq.Output{ To: "receiver@example.com", @@ -143,7 +143,7 @@ func TestDraft_TransactionJSON(t *testing.T) { } ] }`, - draft: &request.DraftTransaction{ + spec: &request.TransactionSpecification{ Outputs: []request.Output{ paymailreq.Output{ To: "receiver@example.com", @@ -155,14 +155,14 @@ func TestDraft_TransactionJSON(t *testing.T) { }, } for name, test := range tests { - t.Run("draft from JSON: "+name, func(t *testing.T) { - var draft *request.DraftTransaction - err := json.Unmarshal([]byte(test.json), &draft) + t.Run("spec from JSON: "+name, func(t *testing.T) { + var spec *request.TransactionSpecification + err := json.Unmarshal([]byte(test.json), &spec) require.NoError(t, err) - require.Equal(t, test.draft, draft) + require.Equal(t, test.spec, spec) }) - t.Run("draft to JSON: "+name, func(t *testing.T) { - data, err := json.Marshal(test.draft) + t.Run("spec to JSON: "+name, func(t *testing.T) { + data, err := json.Marshal(test.spec) require.NoError(t, err) jsonValue := string(data) require.JSONEq(t, test.json, jsonValue) @@ -170,7 +170,7 @@ func TestDraft_TransactionJSON(t *testing.T) { } } -func TestDraft_TransactionJSONParsingErrors(t *testing.T) { +func TestTransactionSpecification_JSONParsingErrors(t *testing.T) { tests := map[string]struct { json string expectedErr string @@ -236,8 +236,8 @@ func TestDraft_TransactionJSONParsingErrors(t *testing.T) { } for name, test := range tests { t.Run(name, func(t *testing.T) { - var draft *request.DraftTransaction - err := json.Unmarshal([]byte(test.json), &draft) + var spec *request.TransactionSpecification + err := json.Unmarshal([]byte(test.json), &spec) require.ErrorContains(t, err, test.expectedErr) }) } @@ -248,13 +248,13 @@ func getTooLargeSatsValueToParse() string { return maxSats + "0" } -func TestDraft_TransactionJSONEncodingErrors(t *testing.T) { +func TestTransactionSpecification_JSONEncodingErrors(t *testing.T) { tests := map[string]struct { - draft *request.DraftTransaction + spec *request.TransactionSpecification expectedErr string }{ "Unsupported output type": { - draft: &request.DraftTransaction{ + spec: &request.TransactionSpecification{ Outputs: []request.Output{&unsupportedOutput{}}, }, expectedErr: "unsupported output type", @@ -262,7 +262,7 @@ func TestDraft_TransactionJSONEncodingErrors(t *testing.T) { } for name, test := range tests { t.Run(name, func(t *testing.T) { - _, err := json.Marshal(test.draft) + _, err := json.Marshal(test.spec) require.ErrorContains(t, err, test.expectedErr) }) }