generated from mrz1836/go-template
-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(SPV-1160): new record outline endpoint with actual repository fo…
…r op_return txs (#777)
- Loading branch information
1 parent
18e6389
commit a4547fb
Showing
27 changed files
with
540 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
actions/transactions/internal/mapping/annotatedtx/annotated_tx_request_to_engine.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package annotatedtx | ||
|
||
import ( | ||
"maps" | ||
|
||
"github.com/bitcoin-sv/spv-wallet/engine/transaction" | ||
"github.com/bitcoin-sv/spv-wallet/engine/transaction/outlines" | ||
model "github.com/bitcoin-sv/spv-wallet/models/transaction" | ||
) | ||
|
||
// Request represents a request model for recording a transaction outline. | ||
type Request model.AnnotatedTransaction | ||
|
||
// ToEngine converts a request model to the engine model. | ||
func (req Request) ToEngine() *outlines.Transaction { | ||
return &outlines.Transaction{ | ||
BEEF: req.BEEF, | ||
Annotations: transaction.Annotations{ | ||
Outputs: maps.Collect(func(yield func(int, *transaction.OutputAnnotation) bool) { | ||
if req.Annotations == nil || len(req.Annotations.Outputs) == 0 { | ||
return | ||
} | ||
for index, output := range req.Annotations.Outputs { | ||
var paymail *transaction.PaymailAnnotation | ||
if output.Paymail != nil { | ||
paymail = &transaction.PaymailAnnotation{ | ||
Receiver: output.Paymail.Receiver, | ||
Reference: output.Paymail.Reference, | ||
Sender: output.Paymail.Sender, | ||
} | ||
} | ||
yield(index, &transaction.OutputAnnotation{ | ||
Bucket: output.Bucket, | ||
Paymail: paymail, | ||
}) | ||
} | ||
}), | ||
}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package transactions | ||
|
||
import ( | ||
"github.com/bitcoin-sv/spv-wallet/actions/transactions/internal/mapping/annotatedtx" | ||
"github.com/bitcoin-sv/spv-wallet/engine/spverrors" | ||
"github.com/bitcoin-sv/spv-wallet/server/reqctx" | ||
"github.com/gin-gonic/gin" | ||
"github.com/gin-gonic/gin/binding" | ||
) | ||
|
||
func transactionRecordOutline(c *gin.Context, _ *reqctx.UserContext) { | ||
logger := reqctx.Logger(c) | ||
|
||
var requestBody annotatedtx.Request | ||
err := c.ShouldBindWith(&requestBody, binding.JSON) | ||
if err != nil { | ||
spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest.Wrap(err), logger) | ||
return | ||
} | ||
|
||
recordService := reqctx.Engine(c).TransactionRecordService() | ||
if err = recordService.RecordTransactionOutline(c, requestBody.ToEngine()); err != nil { | ||
spverrors.ErrorResponse(c, err, logger) | ||
return | ||
} | ||
|
||
c.JSON(200, nil) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
package transactions_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/bitcoin-sv/spv-wallet/actions/testabilities" | ||
"github.com/bitcoin-sv/spv-wallet/actions/testabilities/apierror" | ||
chainmodels "github.com/bitcoin-sv/spv-wallet/engine/chain/models" | ||
"github.com/bitcoin-sv/spv-wallet/engine/tester/fixtures" | ||
) | ||
|
||
const ( | ||
transactionsOutlinesRecordURL = "/api/v2/transactions" | ||
dataOfOpReturnTx = "hello world" | ||
) | ||
|
||
func givenTXWithOpReturn(t *testing.T) fixtures.GivenTXSpec { | ||
return fixtures.GivenTX(t). | ||
WithInput(1). | ||
WithOPReturn(dataOfOpReturnTx) | ||
} | ||
|
||
func TestOutlinesRecordOpReturn(t *testing.T) { | ||
t.Run("Record op_return data", func(t *testing.T) { | ||
// given: | ||
given, then := testabilities.New(t) | ||
cleanup := given.StartedSPVWallet() | ||
defer cleanup() | ||
|
||
// and: | ||
client := given.HttpClient().ForUser() | ||
|
||
// and: | ||
txSpec := givenTXWithOpReturn(t) | ||
request := `{ | ||
"beef": "` + txSpec.BEEF() + `", | ||
"annotations": { | ||
"outputs": { | ||
"0": { | ||
"bucket": "data" | ||
} | ||
} | ||
} | ||
}` | ||
|
||
// and: | ||
given.ARC().WillRespondForBroadcast(200, &chainmodels.TXInfo{ | ||
TxID: txSpec.ID(), | ||
TXStatus: chainmodels.SeenOnNetwork, | ||
}) | ||
|
||
// when: | ||
res, _ := client.R(). | ||
SetHeader("Content-Type", "application/json"). | ||
SetBody(request). | ||
Post(transactionsOutlinesRecordURL) | ||
|
||
// then: | ||
then.Response(res).IsOK() | ||
}) | ||
} | ||
|
||
func TestOutlinesRecordOpReturnErrorCases(t *testing.T) { | ||
givenUnsignedTX := fixtures.GivenTX(t). | ||
WithoutSigning(). | ||
WithInput(1). | ||
WithOPReturn(dataOfOpReturnTx) | ||
|
||
givenTxWithP2PKHOutput := fixtures.GivenTX(t). | ||
WithInput(2). | ||
WithP2PKHOutput(1) | ||
|
||
tests := map[string]struct { | ||
request string | ||
expectHttpCode int | ||
expectedErr string | ||
}{ | ||
"RecordTransactionOutline for not signed transaction": { | ||
request: `{ | ||
"beef": "` + givenUnsignedTX.BEEF() + `" | ||
}`, | ||
expectHttpCode: 400, | ||
expectedErr: apierror.ExpectedJSON("error-transaction-validation", "transaction validation failed"), | ||
}, | ||
"RecordTransactionOutline for not a BEEF hex": { | ||
request: `{ | ||
"beef": "0b3818c665bf28a46"" | ||
}`, | ||
expectHttpCode: 400, | ||
expectedErr: apierror.ExpectedJSON("error-bind-body-invalid", "cannot bind request body"), | ||
}, | ||
"Vout out index as invalid number": { | ||
request: `{ | ||
"beef": "` + givenTXWithOpReturn(t).BEEF() + `" | ||
"annotations": { | ||
"outputs": { | ||
"invalid-number": { | ||
"bucket": "data" | ||
} | ||
} | ||
} | ||
}`, | ||
expectHttpCode: 400, | ||
expectedErr: apierror.ExpectedJSON("error-bind-body-invalid", "cannot bind request body"), | ||
}, | ||
"no-op_return output annotated as data": { | ||
request: `{ | ||
"beef": "` + givenTxWithP2PKHOutput.BEEF() + `", | ||
"annotations": { | ||
"outputs": { | ||
"0": { | ||
"bucket": "data" | ||
} | ||
} | ||
} | ||
}`, | ||
expectHttpCode: 400, | ||
expectedErr: apierror.ExpectedJSON("error-annotation-mismatch", "annotation mismatch"), | ||
}, | ||
} | ||
for name, test := range tests { | ||
t.Run(name, func(t *testing.T) { | ||
// given: | ||
given, then := testabilities.New(t) | ||
cleanup := given.StartedSPVWallet() | ||
defer cleanup() | ||
|
||
// and: | ||
client := given.HttpClient().ForUser() | ||
|
||
// when: | ||
res, _ := client.R(). | ||
SetHeader("Content-Type", "application/json"). | ||
SetBody(test.request). | ||
Post(transactionsOutlinesRecordURL) | ||
|
||
// then: | ||
then.Response(res).HasStatus(test.expectHttpCode).WithJSONf(test.expectedErr) | ||
}) | ||
} | ||
} | ||
|
||
func TestOutlinesRecordOpReturnOnBroadcastError(t *testing.T) { | ||
// given: | ||
given, then := testabilities.New(t) | ||
cleanup := given.StartedSPVWallet() | ||
defer cleanup() | ||
|
||
// and: | ||
client := given.HttpClient().ForUser() | ||
|
||
// and: | ||
txSpec := givenTXWithOpReturn(t) | ||
request := `{ | ||
"beef": "` + txSpec.BEEF() + `", | ||
"annotations": { | ||
"outputs": { | ||
"0": { | ||
"bucket": "data" | ||
} | ||
} | ||
} | ||
}` | ||
|
||
// and: | ||
given.ARC().WillRespondForBroadcast(500, &chainmodels.TXInfo{}) | ||
|
||
// when: | ||
res, _ := client.R(). | ||
SetHeader("Content-Type", "application/json"). | ||
SetBody(request). | ||
Post(transactionsOutlinesRecordURL) | ||
|
||
// then: | ||
then.Response(res).HasStatus(500).WithJSONf(apierror.ExpectedJSON("error-tx-broadcast", "failed to broadcast transaction")) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.