Skip to content
This repository has been archived by the owner on Oct 21, 2024. It is now read-only.

feat(BUX-236): transaction submitting in RawTx format for backwards compatibility #41

Merged
merged 8 commits into from
Oct 5, 2023
92 changes: 62 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ Having your client created, you can use the method `SubmitTx` to submit a single
```go
// ...
tx := broadcast.Transaction{
RawTx: "xyz",
Hex: "xyz",
}

result, err := client.SubmitTransaction(context.Background(), tx)
Expand All @@ -167,14 +167,46 @@ Having your client created, you can use the method `SubmitTx` to submit a single

You need to pass the [transaction](#transaction) as a parameter to the method `SubmitTransaction`.

Setting tx.MerkleProof to true will add the header `X-MerkleProof` to the request.
MerkleProof while broadcasting will handle the merkle proof capability of the node.
You may add options to this method:

##### WithCallback

Setting tx.CallBackURL and tx.CallBackToken will add the headers `X-CallbackUrl` and `X-CallbackToken` to the request.
```go
result, err := client.SubmitTransaction(context.Background(), tx, broadcast.WithCallback(callBackUrl, callbackToken))
```
Setting `CallbackURL` and `CallBackToken` will add the headers `X-CallbackUrl` and `X-CallbackToken` to the request.
It will allow you to get the callback from the node when the transaction is mined, and receive the transaction details and status.

Setting tx.WaitForStatus will add the header `X-WaitForStatus` to the request.
It will allow you to wait for the transaction to be mined and return the result only when the transaction reaches the status you set.
##### WithMerkleProof
arkadiuszos4chain marked this conversation as resolved.
Show resolved Hide resolved

```go
result, err := client.SubmitTransaction(context.Background(), tx, broadcast.WithMerkleProof())
```
Setting `MerkleProof` to true will add the header `X-MerkleProof` to the request.
MerkleProof while broadcasting will handle the merkle proof capability of the node.

##### WithWaitForstatus

```go
result, err := client.SubmitTransaction(context.Background(), tx, broadcast.WithWaitForstatus(broadcast.AnnouncedToNetwork))
```
Setting `WaitForStatus` will add the header `X-WaitForStatus` to the request.
It will allow you to return the result only when the transaction reaches the status you set.

##### WithBeefFormat

```go
result, err := client.SubmitTransaction(context.Background(), tx, broadcast.WithBeefFormat())
```
Setting `BeefFormat` will accept your transaction in BEEF format and decode it for a proper format acceptable by Arc.

##### WithRawFormat (**DEPRECATED!**)

```go
result, err := client.SubmitTransaction(context.Background(), tx, broadcast.WithRawFormat())
```
Setting `RawFormat` will accept your transaction in RawTx format and encode it for a proper format acceptable by Arc.
This option will become deprecated soon.

### SubmitBatchTx Method

Expand All @@ -183,11 +215,11 @@ Having your client created, you can use the method `SubmitBatchTx` to submit a b
```go
// ...
txs := []*broadcast.Transaction{
{RawTx: "xyz1"},
{RawTx: "xyz2"},
{RawTx: "xyz3"},
{RawTx: "xyz4"},
{RawTx: "xyz5"},
{Hex: "xyz1"},
{Hex: "xyz2"},
{Hex: "xyz3"},
{Hex: "xyz4"},
{Hex: "xyz5"},
}

result, err := client.SubmitBatchTransaction(context.Background(), txs)
Expand All @@ -204,11 +236,11 @@ The method works the same as the `SubmitTx` method, but it is sending a batch of

```go
type QueryTxResponse struct {
BlockHash string `json:"blockHash,omitempty"`
BlockHeight int64 `json:"blockHeight,omitempty"`
Timestamp string `json:"timestamp,omitempty"`
TxID string `json:"txid,omitempty"`
TxStatus TxStatus `json:"txStatus,omitempty"`
BlockHash string `json:"blockHash,omitempty"`
BlockHeight int64 `json:"blockHeight,omitempty"`
Timestamp string `json:"timestamp,omitempty"`
TxID string `json:"txid,omitempty"`
TxStatus TxStatus `json:"txStatus,omitempty"`
}
```

Expand Down Expand Up @@ -262,27 +294,27 @@ type FeeQuote struct {

```go
type SubmitTxResponse struct {
BlockHash string `json:"blockHash,omitempty"`
BlockHeight int64 `json:"blockHeight,omitempty"`
ExtraInfo string `json:"extraInfo,omitempty"`
Status int `json:"status,omitempty"`
Title string `json:"title,omitempty"`
TxStatus TxStatus `json:"txStatus,omitempty"`
BlockHash string `json:"blockHash,omitempty"`
BlockHeight int64 `json:"blockHeight,omitempty"`
ExtraInfo string `json:"extraInfo,omitempty"`
Status int `json:"status,omitempty"`
Title string `json:"title,omitempty"`
TxStatus TxStatus `json:"txStatus,omitempty"`
}
```

#### Transaction

```go
type Transaction struct {
CallBackEncryption string `json:"callBackEncryption,omitempty"`
CallBackToken string `json:"callBackToken,omitempty"`
CallBackURL string `json:"callBackUrl,omitempty"`
DsCheck bool `json:"dsCheck,omitempty"`
MerkleFormat string `json:"merkleFormat,omitempty"`
MerkleProof bool `json:"merkleProof,omitempty"`
RawTx string `json:"rawtx"`
WaitForStatus TxStatus `json:"waitForStatus,omitempty"`
CallBackEncryption string `json:"callBackEncryption,omitempty"`
CallBackToken string `json:"callBackToken,omitempty"`
CallBackURL string `json:"callBackUrl,omitempty"`
DsCheck bool `json:"dsCheck,omitempty"`
MerkleFormat string `json:"merkleFormat,omitempty"`
MerkleProof bool `json:"merkleProof,omitempty"`
Hex string `json:"rawtx"`
wregulski marked this conversation as resolved.
Show resolved Hide resolved
WaitForStatus TxStatus `json:"waitForStatus,omitempty"`
}
```

Expand Down
12 changes: 6 additions & 6 deletions broadcast/broadcast-client-mock/mock_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func TestMockClientSuccess(t *testing.T) {
Build()

// when
result, err := broadcaster.SubmitTransaction(context.Background(), &broadcast.Transaction{RawTx: "test-rawtx"})
result, err := broadcaster.SubmitTransaction(context.Background(), &broadcast.Transaction{Hex: "test-rawtx"})

// then
assert.NoError(t, err)
Expand All @@ -98,7 +98,7 @@ func TestMockClientSuccess(t *testing.T) {
}

// when
result, err := broadcaster.SubmitBatchTransactions(context.Background(), []*broadcast.Transaction{{RawTx: "test-rawtx"}, {RawTx: "test2-rawtx"}})
result, err := broadcaster.SubmitBatchTransactions(context.Background(), []*broadcast.Transaction{{Hex: "test-rawtx"}, {Hex: "test2-rawtx"}})

// then
assert.NoError(t, err)
Expand Down Expand Up @@ -160,7 +160,7 @@ func TestMockClientFailure(t *testing.T) {
Build()

// when
result, err := broadcaster.SubmitTransaction(context.Background(), &broadcast.Transaction{RawTx: "test-rawtx"})
result, err := broadcaster.SubmitTransaction(context.Background(), &broadcast.Transaction{Hex: "test-rawtx"})

// then
assert.Error(t, err)
Expand All @@ -175,7 +175,7 @@ func TestMockClientFailure(t *testing.T) {
Build()

// when
result, err := broadcaster.SubmitBatchTransactions(context.Background(), []*broadcast.Transaction{{RawTx: "test-rawtx"}, {RawTx: "test2-rawtx"}})
result, err := broadcaster.SubmitBatchTransactions(context.Background(), []*broadcast.Transaction{{Hex: "test-rawtx"}, {Hex: "test2-rawtx"}})

// then
assert.Error(t, err)
Expand Down Expand Up @@ -260,7 +260,7 @@ func TestMockClientTimeout(t *testing.T) {
startTime := time.Now()

// when
result, err := broadcaster.SubmitTransaction(ctx, &broadcast.Transaction{RawTx: "test-rawtx"})
result, err := broadcaster.SubmitTransaction(ctx, &broadcast.Transaction{Hex: "test-rawtx"})

// then
assert.NoError(t, err)
Expand Down Expand Up @@ -291,7 +291,7 @@ func TestMockClientTimeout(t *testing.T) {
}

// when
result, err := broadcaster.SubmitBatchTransactions(ctx, []*broadcast.Transaction{{RawTx: "test-rawtx"}, {RawTx: "test2-rawtx"}})
result, err := broadcaster.SubmitBatchTransactions(ctx, []*broadcast.Transaction{{Hex: "test-rawtx"}, {Hex: "test2-rawtx"}})

// then
assert.NoError(t, err)
Expand Down
24 changes: 12 additions & 12 deletions broadcast/internal/acceptance_tests/arc_submit_tx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func TestSubmitTransaction(t *testing.T) {
httpClientMock.On("DoRequest", mock.Anything, mock.Anything).Return(httpResponse2, nil).Times(0)

// when
result, err := broadcaster.SubmitTransaction(context.Background(), &broadcast.Transaction{RawTx: "transaction-data"})
result, err := broadcaster.SubmitTransaction(context.Background(), &broadcast.Transaction{Hex: "transaction-data"})

// then
httpClientMock.AssertExpectations(t)
Expand All @@ -74,7 +74,7 @@ func TestSubmitTransaction(t *testing.T) {
httpClientMock.On("DoRequest", mock.Anything, mock.Anything).Return(httpResponse, errors.New("http error")).Once()

// when
result, err := broadcaster.SubmitTransaction(context.Background(), &broadcast.Transaction{RawTx: "transaction-data"})
result, err := broadcaster.SubmitTransaction(context.Background(), &broadcast.Transaction{Hex: "transaction-data"})

// then
httpClientMock.AssertExpectations(t)
Expand All @@ -98,7 +98,7 @@ func TestSubmitTransaction(t *testing.T) {
httpClientMock.On("DoRequest", mock.Anything, mock.Anything).Return(httpResponse, errors.New("http error")).Once()

// when
result, err := broadcaster.SubmitTransaction(context.Background(), &broadcast.Transaction{RawTx: "transaction-data"})
result, err := broadcaster.SubmitTransaction(context.Background(), &broadcast.Transaction{Hex: "transaction-data"})

// then
httpClientMock.AssertExpectations(t)
Expand All @@ -122,7 +122,7 @@ func TestSubmitTransaction(t *testing.T) {
httpClientMock.On("DoRequest", mock.Anything, mock.Anything).Return(httpResponse2, nil).Once()

// when
result, err := broadcaster.SubmitTransaction(context.Background(), &broadcast.Transaction{RawTx: "transaction-data"})
result, err := broadcaster.SubmitTransaction(context.Background(), &broadcast.Transaction{Hex: "transaction-data"})

// then
httpClientMock.AssertExpectations(t)
Expand All @@ -146,8 +146,8 @@ func TestSubmitBatchTransactions(t *testing.T) {
httpClientMock.On("DoRequest", mock.Anything, mock.Anything).Return(httpResponse2, nil).Times(0)

batch := []*broadcast.Transaction{
{RawTx: "transaction-0-data"},
{RawTx: "transaction-1-data"},
{Hex: "transaction-0-data"},
{Hex: "transaction-1-data"},
}

// when
Expand All @@ -174,8 +174,8 @@ func TestSubmitBatchTransactions(t *testing.T) {
httpClientMock.On("DoRequest", mock.Anything, mock.Anything).Return(httpResponse2, nil).Once()

batch := []*broadcast.Transaction{
{RawTx: "transaction-0-data"},
{RawTx: "transaction-1-data"},
{Hex: "transaction-0-data"},
{Hex: "transaction-1-data"},
}

// when
Expand All @@ -198,8 +198,8 @@ func TestSubmitBatchTransactions(t *testing.T) {
httpClientMock.On("DoRequest", mock.Anything, mock.Anything).Return(httpResponse, errors.New("http error")).Once()

batch := []*broadcast.Transaction{
{RawTx: "transaction-0-data"},
{RawTx: "transaction-1-data"},
{Hex: "transaction-0-data"},
{Hex: "transaction-1-data"},
}

// when
Expand All @@ -223,8 +223,8 @@ func TestSubmitBatchTransactions(t *testing.T) {
httpClientMock.On("DoRequest", mock.Anything, mock.Anything).Return(httpResponse, errors.New("http error")).Once()

batch := []*broadcast.Transaction{
{RawTx: "transaction-0-data"},
{RawTx: "transaction-1-data"},
{Hex: "transaction-0-data"},
{Hex: "transaction-1-data"},
}

// when
Expand Down
55 changes: 50 additions & 5 deletions broadcast/internal/arc/arc_submit_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@ package arc

import (
"context"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"strconv"

"github.com/GorillaPool/go-junglebus"
"github.com/GorillaPool/go-junglebus/models"
"github.com/libsv/go-bt/v2"

"github.com/bitcoin-sv/go-broadcast-client/broadcast"
arc_utils "github.com/bitcoin-sv/go-broadcast-client/broadcast/internal/arc/utils"
"github.com/bitcoin-sv/go-broadcast-client/httpclient"
)

type SubmitTxRequest struct {
RawTx string `json:"rawTx"`
TxHex string `json:"txHex"`
arkadiuszos4chain marked this conversation as resolved.
Show resolved Hide resolved
}

var ErrSubmitTxMarshal = errors.New("error while marshalling submit tx body")
Expand All @@ -27,6 +33,16 @@ func (a *ArcClient) SubmitTransaction(ctx context.Context, tx *broadcast.Transac
opt(options)
}

fmt.Println("BEFORE CONVERSION: ", tx.Hex)
if options.RawFormat {
if err := convertToEfTransaction(tx); err != nil {
arkadiuszos4chain marked this conversation as resolved.
Show resolved Hide resolved
return nil, fmt.Errorf("Conversion to EF format failed: %s", err.Error())
}
} else if options.BeefFormat {
// To be implemented
arkadiuszos4chain marked this conversation as resolved.
Show resolved Hide resolved
}
fmt.Println("AFTER CONVERSION: ", tx.Hex)

result, err := submitTransaction(ctx, a, tx, options)
if err != nil {
return nil, err
Expand All @@ -38,7 +54,7 @@ func (a *ArcClient) SubmitTransaction(ctx context.Context, tx *broadcast.Transac

response := &broadcast.SubmitTxResponse{
BaseResponse: broadcast.BaseResponse{Miner: a.apiURL},
SubmittedTx: result,
SubmittedTx: result,
}

return response, nil
Expand Down Expand Up @@ -140,9 +156,8 @@ func submitBatchTransactions(ctx context.Context, arc *ArcClient, txs []*broadca
}

func createSubmitTxBody(tx *broadcast.Transaction) ([]byte, error) {
body := &SubmitTxRequest{tx.RawTx}
body := &SubmitTxRequest{tx.Hex}
data, err := json.Marshal(body)

if err != nil {
return nil, ErrSubmitTxMarshal
}
Expand All @@ -153,7 +168,7 @@ func createSubmitTxBody(tx *broadcast.Transaction) ([]byte, error) {
func createSubmitBatchTxsBody(txs []*broadcast.Transaction) ([]byte, error) {
rawTxs := make([]*SubmitTxRequest, 0, len(txs))
for _, tx := range txs {
rawTxs = append(rawTxs, &SubmitTxRequest{RawTx: tx.RawTx})
rawTxs = append(rawTxs, &SubmitTxRequest{TxHex: tx.Hex})
}

data, err := json.Marshal(rawTxs)
Expand Down Expand Up @@ -203,3 +218,33 @@ func validateSubmitTxResponse(model *broadcast.SubmittedTx) error {

return nil
}

func convertToEfTransaction(tx *broadcast.Transaction) error {
junglebusClient, err := junglebus.New(
junglebus.WithHTTP("https://junglebus.gorillapool.io"),
)
if err != nil {
return err
}
transaction, err := bt.NewTxFromString(tx.Hex)
if err != nil {
return err
}
for _, input := range transaction.Inputs {
txid := input.PreviousTxIDStr()
wregulski marked this conversation as resolved.
Show resolved Hide resolved
var tx *models.Transaction
if tx, err = junglebusClient.GetTransaction(context.Background(), txid); err != nil {
return err
} else {
wregulski marked this conversation as resolved.
Show resolved Hide resolved
actualTx, err := bt.NewTxFromBytes(tx.Transaction)
if err != nil {
return err
}
o := actualTx.Outputs[input.PreviousTxOutIndex]
input.PreviousTxScript = o.LockingScript
input.PreviousTxSatoshis = o.Satoshis
}
wregulski marked this conversation as resolved.
Show resolved Hide resolved
}
tx.Hex = hex.EncodeToString(transaction.ExtendedBytes())
return nil
}
Loading