From 22cd7e02302f009aacbb1b53e8eebb1f777a2cb0 Mon Sep 17 00:00:00 2001 From: Kuba <127198012+kuba-4chain@users.noreply.github.com> Date: Fri, 13 Oct 2023 14:27:17 +0200 Subject: [PATCH] feat(BUX-236): transaction submitting in RawTx format for backwards compatibility (#48) * feat(BUX-236): transaction submitting in RawTx format for backwards compatibility This reverts commit 7939ff84b43430b119334ae93d2f1692af91c80b. * feat(BUX-236): supporting RawTx transaction submitting without junglebus * feat(BUX-236): extracing conversion methods --- README.md | 92 ++++++--- .../broadcast-client-mock/mock_client_test.go | 12 +- .../acceptance_tests/arc_submit_tx_test.go | 24 +-- broadcast/internal/arc/arc_submit_tx.go | 134 ++++++++++++- broadcast/internal/arc/arc_submit_tx_test.go | 184 ++++++++++++++++-- broadcast/transaction.go | 46 ++++- .../submit_batch_txs.go | 12 +- examples/submit_transaction/submit_tx.go | 4 +- go.mod | 10 +- go.sum | 16 +- 10 files changed, 445 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index 006c721..7af0811 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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 + +```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 @@ -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) @@ -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"` } ``` @@ -262,12 +294,12 @@ 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"` } ``` @@ -275,14 +307,14 @@ type SubmitTxResponse struct { ```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:"hex"` + WaitForStatus TxStatus `json:"waitForStatus,omitempty"` } ``` diff --git a/broadcast/broadcast-client-mock/mock_client_test.go b/broadcast/broadcast-client-mock/mock_client_test.go index 565b845..628b711 100644 --- a/broadcast/broadcast-client-mock/mock_client_test.go +++ b/broadcast/broadcast-client-mock/mock_client_test.go @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) diff --git a/broadcast/internal/acceptance_tests/arc_submit_tx_test.go b/broadcast/internal/acceptance_tests/arc_submit_tx_test.go index b07a8b7..c9bd8d8 100644 --- a/broadcast/internal/acceptance_tests/arc_submit_tx_test.go +++ b/broadcast/internal/acceptance_tests/arc_submit_tx_test.go @@ -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) @@ -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) @@ -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) @@ -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) @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/broadcast/internal/arc/arc_submit_tx.go b/broadcast/internal/arc/arc_submit_tx.go index c5aa4d9..6bbee45 100644 --- a/broadcast/internal/arc/arc_submit_tx.go +++ b/broadcast/internal/arc/arc_submit_tx.go @@ -2,10 +2,14 @@ package arc import ( "context" + "encoding/hex" "encoding/json" "errors" + "fmt" "strconv" + "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" @@ -38,7 +42,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 @@ -77,7 +81,7 @@ func (a *ArcClient) SubmitBatchTransactions(ctx context.Context, txs []*broadcas func submitTransaction(ctx context.Context, arc *ArcClient, tx *broadcast.Transaction, opts *broadcast.TransactionOpts) (*broadcast.SubmittedTx, error) { url := arc.apiURL + arcSubmitTxRoute - data, err := createSubmitTxBody(tx) + data, err := createSubmitTxBody(arc, tx, opts.TransactionFormat) if err != nil { return nil, err } @@ -109,7 +113,7 @@ func submitTransaction(ctx context.Context, arc *ArcClient, tx *broadcast.Transa func submitBatchTransactions(ctx context.Context, arc *ArcClient, txs []*broadcast.Transaction, opts *broadcast.TransactionOpts) ([]*broadcast.SubmittedTx, error) { url := arc.apiURL + arcSubmitBatchTxsRoute - data, err := createSubmitBatchTxsBody(txs) + data, err := createSubmitBatchTxsBody(arc, txs, opts.TransactionFormat) if err != nil { return nil, err } @@ -139,10 +143,13 @@ func submitBatchTransactions(ctx context.Context, arc *ArcClient, txs []*broadca return model, nil } -func createSubmitTxBody(tx *broadcast.Transaction) ([]byte, error) { - body := &SubmitTxRequest{tx.RawTx} - data, err := json.Marshal(body) +func createSubmitTxBody(arc *ArcClient, tx *broadcast.Transaction, txFormat broadcast.TransactionFormat) ([]byte, error) { + body, err := formatTxRequest(arc, tx, txFormat) + if err != nil { + return nil, err + } + data, err := json.Marshal(body) if err != nil { return nil, ErrSubmitTxMarshal } @@ -150,10 +157,14 @@ func createSubmitTxBody(tx *broadcast.Transaction) ([]byte, error) { return data, nil } -func createSubmitBatchTxsBody(txs []*broadcast.Transaction) ([]byte, error) { +func createSubmitBatchTxsBody(arc *ArcClient, txs []*broadcast.Transaction, txFormat broadcast.TransactionFormat) ([]byte, error) { rawTxs := make([]*SubmitTxRequest, 0, len(txs)) for _, tx := range txs { - rawTxs = append(rawTxs, &SubmitTxRequest{RawTx: tx.RawTx}) + requestTx, err := formatTxRequest(arc, tx, txFormat) + if err != nil { + return nil, err + } + rawTxs = append(rawTxs, requestTx) } data, err := json.Marshal(rawTxs) @@ -203,3 +214,110 @@ func validateSubmitTxResponse(model *broadcast.SubmittedTx) error { return nil } + +func formatTxRequest(arc *ArcClient, tx *broadcast.Transaction, txFormat broadcast.TransactionFormat) (*SubmitTxRequest, error) { + var ( + body *SubmitTxRequest + err error + ) + + switch txFormat { + case broadcast.RawTxFormat: + body, err = rawTxRequest(arc, tx.Hex) + case broadcast.EfFormat: + body, err = efTxRequest(tx.Hex) + case broadcast.BeefFormat: + body, err = beefTxRequest(tx.Hex) + default: + err = fmt.Errorf("unknown transaction format") + } + + if err != nil { + return nil, err + } + + return body, nil +} + +func efTxRequest(rawTx string) (*SubmitTxRequest, error) { + request := &SubmitTxRequest{RawTx: rawTx} + + return request, nil +} + +func beefTxRequest(rawTx string) (*SubmitTxRequest, error) { + return nil, fmt.Errorf("Submitting transactions in BEEF format is unimplemented yet...") +} + +func rawTxRequest(arc *ArcClient, rawTx string) (*SubmitTxRequest, error) { + transaction, err := bt.NewTxFromString(rawTx) + if err != nil { + return nil, err + } + + for _, input := range transaction.Inputs { + if err = updateUtxoWithMissingData(arc, input); err != nil { + return nil, err + } + } + + request := &SubmitTxRequest{ + RawTx: hex.EncodeToString(transaction.ExtendedBytes()), + } + return request, nil +} + +func updateUtxoWithMissingData(arc *ArcClient, input *bt.Input) error { + txid := input.PreviousTxIDStr() + + pld := httpclient.NewPayload( + httpclient.GET, + fmt.Sprintf("https://junglebus.gorillapool.io/v1/transaction/get/%s", txid), + "", + nil, + ) + resp, err := arc.HTTPClient.DoRequest(context.Background(), pld) + if err != nil { + return err + } + + var tx *junglebusTransaction + err = arc_utils.DecodeResponseBody(resp.Body, &tx) + if err != nil { + return err + } + + actualTx, err := bt.NewTxFromBytes(tx.Transaction) + if err != nil { + return err + } + + o := actualTx.Outputs[input.PreviousTxOutIndex] + input.PreviousTxScript = o.LockingScript + input.PreviousTxSatoshis = o.Satoshis + return nil +} + +type junglebusTransaction struct { + ID string `json:"id"` + Transaction []byte `json:"transaction"` + BlockHash string `json:"block_hash"` + BlockHeight uint32 `json:"block_height"` + BlockTime uint32 `json:"block_time"` + BlockIndex uint64 `json:"block_index"` + + // index data + // input/output types are + // p2pkh, p2sh, token-stas, opreturn, tokenized, metanet, bitcom, run, map, bap, non-standard etc. + Addresses []string `json:"addresses"` + Inputs []string `json:"inputs"` + Outputs []string `json:"outputs"` + InputTypes []string `json:"input_types"` + OutputTypes []string `json:"output_types"` + Contexts []string `json:"contexts"` // optional contexts of output types, only for known protocols + SubContexts []string `json:"sub_contexts"` // optional sub-contexts of output types, only for known protocols + Data []string `json:"data"` // optional data of output types, only for known protocols + + // the merkle proof in binary + MerkleProof []byte `json:"merkle_proof"` +} diff --git a/broadcast/internal/arc/arc_submit_tx_test.go b/broadcast/internal/arc/arc_submit_tx_test.go index 7fb54e0..1b3e194 100644 --- a/broadcast/internal/arc/arc_submit_tx_test.go +++ b/broadcast/internal/arc/arc_submit_tx_test.go @@ -26,7 +26,7 @@ func TestSubmitTransaction(t *testing.T) { { name: "successful request", transaction: &broadcast.Transaction{ - RawTx: "abc123", + Hex: "abc123", }, httpResponse: &http.Response{ StatusCode: http.StatusOK, @@ -44,7 +44,7 @@ func TestSubmitTransaction(t *testing.T) { { name: "error in HTTP request", transaction: &broadcast.Transaction{ - RawTx: "abc123", + Hex: "abc123", }, httpError: errors.New("some error"), expectedError: errors.New("some error"), @@ -52,7 +52,7 @@ func TestSubmitTransaction(t *testing.T) { { name: "missing txStatus in response", transaction: &broadcast.Transaction{ - RawTx: "abc123", + Hex: "abc123", }, httpResponse: &http.Response{ StatusCode: http.StatusOK, @@ -70,7 +70,13 @@ func TestSubmitTransaction(t *testing.T) { // given mockHttpClient := new(MockHttpClient) - body, _ := createSubmitTxBody(tc.transaction) + client := &ArcClient{ + HTTPClient: mockHttpClient, + apiURL: "http://example.com", + token: "someToken", + } + + body, _ := createSubmitTxBody(client, tc.transaction, broadcast.EfFormat) expectedPayload := httpclient.NewPayload( httpclient.POST, "http://example.com"+arcSubmitTxRoute, @@ -79,6 +85,59 @@ func TestSubmitTransaction(t *testing.T) { ) appendSubmitTxHeaders(&expectedPayload, nil) + mockHttpClient.On("DoRequest", context.Background(), expectedPayload). + Return(tc.httpResponse, tc.httpError).Once() + + // when + result, err := client.SubmitTransaction(context.Background(), tc.transaction) + + // then + assert.Equal(t, tc.expectedResult, result) + assert.Equal(t, tc.expectedError, err) + + // assert Expectations on the mock + mockHttpClient.AssertExpectations(t) + }) + } +} + +func TestConvertTransaction(t *testing.T) { + testCases := []struct { + name string + transaction *broadcast.Transaction + httpResponse *http.Response + httpError error + expectedResult *SubmitTxRequest + }{ + { + name: "successful conversion from RawTx to EF", + transaction: &broadcast.Transaction{ + Hex: "0100000001d6d1607b208b30c0a3fe21d563569c4d2a0f913604b4c5054fe267da6be324ab220000006b4830450221009a965dcd5d42983090a63cfd761038ff8adcea621c46a68a205f326292a95383022061b8d858f366c69f3ebd30a60ccafe36faca4e242ac3d2edd3bf63b669bcf23b4121034e871e147aa4a3e2f1665eaf76cf9264d089b6a91702af92bd6ce33bac84a765ffffffff0123020000000000001976a914d8819a7197d3e221e15f4348203fdecfd29fa2b888ac00000000", + }, + httpResponse: &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(bytes.NewBufferString(` + { + "transaction": "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" + } + `)), + }, + expectedResult: &SubmitTxRequest{ + RawTx: "010000000000000000ef01d6d1607b208b30c0a3fe21d563569c4d2a0f913604b4c5054fe267da6be324ab220000006b4830450221009a965dcd5d42983090a63cfd761038ff8adcea621c46a68a205f326292a95383022061b8d858f366c69f3ebd30a60ccafe36faca4e242ac3d2edd3bf63b669bcf23b4121034e871e147aa4a3e2f1665eaf76cf9264d089b6a91702af92bd6ce33bac84a765ffffffffbc020000000000001976a914a5127a45f084089fc1ff33012c3da3dfbc0fc7db88ac0123020000000000001976a914d8819a7197d3e221e15f4348203fdecfd29fa2b888ac00000000", + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // given + mockHttpClient := new(MockHttpClient) + expectedPayload := httpclient.NewPayload( + httpclient.GET, + "https://junglebus.gorillapool.io/v1/transaction/get/ab24e36bda67e24f05c5b40436910f2a4d9c5663d521fea3c0308b207b60d1d6", + "", + nil, + ) + mockHttpClient.On("DoRequest", context.Background(), expectedPayload). Return(tc.httpResponse, tc.httpError).Once() @@ -89,14 +148,97 @@ func TestSubmitTransaction(t *testing.T) { } // when - result, err := client.SubmitTransaction(context.Background(), tc.transaction) + result, err := formatTxRequest(client, tc.transaction, broadcast.RawTxFormat) // then + assert.NoError(t, err) assert.Equal(t, tc.expectedResult, result) - assert.Equal(t, tc.expectedError, err) + }) + } +} - // assert Expectations on the mock - mockHttpClient.AssertExpectations(t) +func TestConvertBatchTransactions(t *testing.T) { + testCases := []struct { + name string + transactions []*broadcast.Transaction + httpResponses []*http.Response + httpError error + expectedResults []*SubmitTxRequest + }{ + { + name: "successful batch conversion from RawTx to EF", + transactions: []*broadcast.Transaction{ + { + Hex: "0100000001d6d1607b208b30c0a3fe21d563569c4d2a0f913604b4c5054fe267da6be324ab220000006b4830450221009a965dcd5d42983090a63cfd761038ff8adcea621c46a68a205f326292a95383022061b8d858f366c69f3ebd30a60ccafe36faca4e242ac3d2edd3bf63b669bcf23b4121034e871e147aa4a3e2f1665eaf76cf9264d089b6a91702af92bd6ce33bac84a765ffffffff0123020000000000001976a914d8819a7197d3e221e15f4348203fdecfd29fa2b888ac00000000", + }, + { + Hex: "0100000001d6d1607b208b30c0a3fe21d563569c4d2a0f913604b4c5054fe267da6be324ab220000006b4830450221009a965dcd5d42983090a63cfd761038ff8adcea621c46a68a205f326292a95383022061b8d858f366c69f3ebd30a60ccafe36faca4e242ac3d2edd3bf63b669bcf23b4121034e871e147aa4a3e2f1665eaf76cf9264d089b6a91702af92bd6ce33bac84a765ffffffff0123020000000000001976a914d8819a7197d3e221e15f4348203fdecfd29fa2b888ac00000000", + }, + }, + httpResponses: []*http.Response{ + { + StatusCode: http.StatusOK, + Body: io.NopCloser(bytes.NewBufferString(` + { + "transaction": "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" + } + `)), + }, + { + StatusCode: http.StatusOK, + Body: io.NopCloser(bytes.NewBufferString(` + { + "transaction": "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" + } + `)), + }, + }, + expectedResults: []*SubmitTxRequest{ + { + RawTx: "010000000000000000ef01d6d1607b208b30c0a3fe21d563569c4d2a0f913604b4c5054fe267da6be324ab220000006b4830450221009a965dcd5d42983090a63cfd761038ff8adcea621c46a68a205f326292a95383022061b8d858f366c69f3ebd30a60ccafe36faca4e242ac3d2edd3bf63b669bcf23b4121034e871e147aa4a3e2f1665eaf76cf9264d089b6a91702af92bd6ce33bac84a765ffffffffbc020000000000001976a914a5127a45f084089fc1ff33012c3da3dfbc0fc7db88ac0123020000000000001976a914d8819a7197d3e221e15f4348203fdecfd29fa2b888ac00000000", + }, + { + RawTx: "010000000000000000ef01d6d1607b208b30c0a3fe21d563569c4d2a0f913604b4c5054fe267da6be324ab220000006b4830450221009a965dcd5d42983090a63cfd761038ff8adcea621c46a68a205f326292a95383022061b8d858f366c69f3ebd30a60ccafe36faca4e242ac3d2edd3bf63b669bcf23b4121034e871e147aa4a3e2f1665eaf76cf9264d089b6a91702af92bd6ce33bac84a765ffffffffbc020000000000001976a914a5127a45f084089fc1ff33012c3da3dfbc0fc7db88ac0123020000000000001976a914d8819a7197d3e221e15f4348203fdecfd29fa2b888ac00000000", + }, + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // given + mockHttpClient := new(MockHttpClient) + expectedPayload := httpclient.NewPayload( + httpclient.GET, + "https://junglebus.gorillapool.io/v1/transaction/get/ab24e36bda67e24f05c5b40436910f2a4d9c5663d521fea3c0308b207b60d1d6", + "", + nil, + ) + + for _, resp := range tc.httpResponses { + mockHttpClient.On("DoRequest", context.Background(), expectedPayload). + Return(resp, tc.httpError).Once() + } + + client := &ArcClient{ + HTTPClient: mockHttpClient, + apiURL: "http://example.com", + token: "someToken", + } + + // when + results := make([]*SubmitTxRequest, 0, len(tc.transactions)) + errors := make([]error, 0) + for _, tx := range tc.transactions { + requestTx, err := formatTxRequest(client, tx, broadcast.RawTxFormat) + if err != nil { + errors = append(errors, err) + } + results = append(results, requestTx) + } + + // then + assert.Empty(t, errors) + assert.Equal(t, tc.expectedResults, results) }) } } @@ -113,8 +255,8 @@ func TestSubmitBatchTransactions(t *testing.T) { { name: "successful request", transactions: []*broadcast.Transaction{ - {RawTx: "abc123"}, - {RawTx: "cba321"}, + {Hex: "abc123"}, + {Hex: "cba321"}, }, httpResponse: &http.Response{ StatusCode: http.StatusOK, @@ -139,8 +281,8 @@ func TestSubmitBatchTransactions(t *testing.T) { { name: "error in HTTP request", transactions: []*broadcast.Transaction{ - {RawTx: "abc123"}, - {RawTx: "cba321"}, + {Hex: "abc123"}, + {Hex: "cba321"}, }, httpError: errors.New("some error"), expectedError: errors.New("some error"), @@ -148,8 +290,8 @@ func TestSubmitBatchTransactions(t *testing.T) { { name: "missing txStatus in response", transactions: []*broadcast.Transaction{ - {RawTx: "abc123"}, - {RawTx: "cba321"}, + {Hex: "abc123"}, + {Hex: "cba321"}, }, httpResponse: &http.Response{ StatusCode: http.StatusOK, @@ -171,7 +313,13 @@ func TestSubmitBatchTransactions(t *testing.T) { // given mockHttpClient := new(MockHttpClient) - body, _ := createSubmitBatchTxsBody(tc.transactions) + client := &ArcClient{ + HTTPClient: mockHttpClient, + apiURL: "http://example.com", + token: "someToken", + } + + body, _ := createSubmitBatchTxsBody(client, tc.transactions, broadcast.EfFormat) expectedPayload := httpclient.NewPayload( httpclient.POST, "http://example.com"+arcSubmitBatchTxsRoute, @@ -183,12 +331,6 @@ func TestSubmitBatchTransactions(t *testing.T) { mockHttpClient.On("DoRequest", context.Background(), expectedPayload). Return(tc.httpResponse, tc.httpError).Once() - client := &ArcClient{ - HTTPClient: mockHttpClient, - apiURL: "http://example.com", - token: "someToken", - } - // when result, err := client.SubmitBatchTransactions(context.Background(), tc.transactions) diff --git a/broadcast/transaction.go b/broadcast/transaction.go index f74804b..d47c8e0 100644 --- a/broadcast/transaction.go +++ b/broadcast/transaction.go @@ -2,13 +2,22 @@ package broadcast // Transaction is the body contents in the "submit transaction" request type Transaction struct { - // RawTx is the raw transaction hex string. - RawTx string `json:"rawtx"` + // Hex is the transaction hex string. + Hex string `json:"hex"` } // TransactionOptFunc defines an optional arguments that can be passed to the SubmitTransaction method. type TransactionOptFunc func(o *TransactionOpts) +// TransactionFormat is the format of transaction being submitted. +type TransactionFormat int + +const ( + EfFormat TransactionFormat = iota + BeefFormat + RawTxFormat +) + // TransactionOpts is a struct that holds optional arguments that can be passed to the SubmitTransaction method. type TransactionOpts struct { // CallbackURL is the URL that will be called when the transaction status changes. @@ -19,8 +28,13 @@ type TransactionOpts struct { MerkleProof bool // WaitForStatus is the status that the callback request will wait for. WaitForStatus TxStatus + // TransactionFormat is the format of transaction being submitted. Acceptable + // formats are: RawFormat (deprecated soon), BeefFormat and EfFormat (default). + TransactionFormat TransactionFormat } +// WithCallback allow you to get the callback from the node when the transaction is mined, +// and receive the transaction details and status. func WithCallback(callbackURL string, callbackToken ...string) TransactionOptFunc { return func(o *TransactionOpts) { o.CallbackToken = callbackURL @@ -30,14 +44,42 @@ func WithCallback(callbackURL string, callbackToken ...string) TransactionOptFun } } +// WithMerkleProof will return merkle proof from Arc. func WithMerkleProof() TransactionOptFunc { return func(o *TransactionOpts) { o.MerkleProof = true } } +// WithWaitForStatus will allow you to return the result only +// when the transaction reaches the status you set. func WithWaitForStatus(status TxStatus) TransactionOptFunc { return func(o *TransactionOpts) { o.WaitForStatus = status } } + +// WithBeefFormat will accept your transaction in BEEF format +// and decode it for a proper format acceptable by Arc. +func WithBeefFormat() TransactionOptFunc { + return func(o *TransactionOpts) { + o.TransactionFormat = BeefFormat + } +} + +// WithEfFormat will submit your transaction in EF format. +func WithEfFormat() TransactionOptFunc { + return func(o *TransactionOpts) { + o.TransactionFormat = EfFormat + } +} + +// WithRawFormat will accept your transaction in RawTx format +// and encode it for a proper format acceptable by Arc. +// Deprecated: This function will be depreacted soon. +// Only EF and BEEF format will be acceptable. +func WithRawFormat() TransactionOptFunc { + return func(o *TransactionOpts) { + o.TransactionFormat = RawTxFormat + } +} diff --git a/examples/submit_batch_transactions/submit_batch_txs.go b/examples/submit_batch_transactions/submit_batch_txs.go index 1c75ca1..13afae4 100644 --- a/examples/submit_batch_transactions/submit_batch_txs.go +++ b/examples/submit_batch_transactions/submit_batch_txs.go @@ -13,11 +13,11 @@ func main() { token := "{token}" apiURL := "https://tapi.taal.com/arc" txs := []*broadcast.Transaction{ - {RawTx: "0100000001d6d1607b208b30c0a3fe21d563569c4d2a0f913604b4c5054fe267da6be324ab220000006b4830450221009a965dcd5d42983090a63cfd761038ff8adcea621c46a68a205f326292a95383022061b8d858f366c69f3ebd30a60ccafe36faca4e242ac3d2edd3bf63b669bcf23b4121034e871e147aa4a3e2f1665eaf76cf9264d089b6a91702af92bd6ce33bac84a765ffffffff0123020000000000001976a914d8819a7197d3e221e15f4348203fdecfd29fa2b888ac00000000"}, - {RawTx: "0100000001bbf4d68a935126df1eea3e14714c5a6438bbda3d18b7e754bc90895cf2190196060000006a47304402206a0c3923a9ae253ac5ea22d36e667f067c51f88a8bfb2861d9124bc5402fecd00220745731fa951076c63df2de6528616e0ef97ef8ade5b17d8e1d51494cf153a64b412103af3ead8a3ab792225bf22262f0b81a72e5070788d363ee717c5868421b75a62dffffffff01000000000000000040006a0a6d793263656e74732c201c4759384d656c46634d326449674d75557356644d53776f6a66467431152c20302e323135353133313536343634343531313600000000"}, - {RawTx: "0100000001bbf4d68a935126df1eea3e14714c5a6438bbda3d18b7e754bc90895cf21901962b0000006a47304402202eb4a5a437da4f2d17f0301e8f7fe1364a3655069797284501b5295d707238e802202ff312652a492ff9bd63c700283abe321b637b34b41794e47b37d589503132c1412103af3ead8a3ab792225bf22262f0b81a72e5070788d363ee717c5868421b75a62dffffffff01000000000000000042006a0a6d793263656e74732c201c625a753465447130333856726a75736e6f4651746573455355344c32172c20302e3030313834343339383431333331333235393800000000"}, - {RawTx: "010000000141dcf2f3e9146db096eaab29576546567c81ec43b181f61012606d0bd6e2ad5c010000006b483045022100bda2f44e5c27f1c10ca908996125d4debbc8bfdd9e23c8140d3006390d71ffd50220486c0725a03e798295270b20b1163f525f211a4be4cd9855225e4a3865ab3ae841210262142850483b6728b8ecd299e4d0c8cf30ea0636f66205166814e52d73b64b4bffffffff0200000000000000000a006a075354554b2e434f8ea2e60c000000001976a91454cba8da8701174e34aac2bb31d42a88e2c302d088ac00000000"}, - {RawTx: "01000000018e5cbbfdf1c4e1c168037bbb305be7018de2d19f6de919c75678daad07c375af010000006b483045022100a0b73eca88a56b30e7f2ea7388aac64506cbdccdc04452b9150344054fc969ae022071dc9e99e42825495ab4f274066a519f0ecaf671d84c156b37458fcd888159ba41210262142850483b6728b8ecd299e4d0c8cf30ea0636f66205166814e52d73b64b4bffffffff0200000000000000000a006a075354554b2e434f90a2e60c000000001976a91454cba8da8701174e34aac2bb31d42a88e2c302d088ac00000000"}, + {Hex: "0100000001d6d1607b208b30c0a3fe21d563569c4d2a0f913604b4c5054fe267da6be324ab220000006b4830450221009a965dcd5d42983090a63cfd761038ff8adcea621c46a68a205f326292a95383022061b8d858f366c69f3ebd30a60ccafe36faca4e242ac3d2edd3bf63b669bcf23b4121034e871e147aa4a3e2f1665eaf76cf9264d089b6a91702af92bd6ce33bac84a765ffffffff0123020000000000001976a914d8819a7197d3e221e15f4348203fdecfd29fa2b888ac00000000"}, + {Hex: "0100000001bbf4d68a935126df1eea3e14714c5a6438bbda3d18b7e754bc90895cf2190196060000006a47304402206a0c3923a9ae253ac5ea22d36e667f067c51f88a8bfb2861d9124bc5402fecd00220745731fa951076c63df2de6528616e0ef97ef8ade5b17d8e1d51494cf153a64b412103af3ead8a3ab792225bf22262f0b81a72e5070788d363ee717c5868421b75a62dffffffff01000000000000000040006a0a6d793263656e74732c201c4759384d656c46634d326449674d75557356644d53776f6a66467431152c20302e323135353133313536343634343531313600000000"}, + {Hex: "0100000001bbf4d68a935126df1eea3e14714c5a6438bbda3d18b7e754bc90895cf21901962b0000006a47304402202eb4a5a437da4f2d17f0301e8f7fe1364a3655069797284501b5295d707238e802202ff312652a492ff9bd63c700283abe321b637b34b41794e47b37d589503132c1412103af3ead8a3ab792225bf22262f0b81a72e5070788d363ee717c5868421b75a62dffffffff01000000000000000042006a0a6d793263656e74732c201c625a753465447130333856726a75736e6f4651746573455355344c32172c20302e3030313834343339383431333331333235393800000000"}, + {Hex: "010000000141dcf2f3e9146db096eaab29576546567c81ec43b181f61012606d0bd6e2ad5c010000006b483045022100bda2f44e5c27f1c10ca908996125d4debbc8bfdd9e23c8140d3006390d71ffd50220486c0725a03e798295270b20b1163f525f211a4be4cd9855225e4a3865ab3ae841210262142850483b6728b8ecd299e4d0c8cf30ea0636f66205166814e52d73b64b4bffffffff0200000000000000000a006a075354554b2e434f8ea2e60c000000001976a91454cba8da8701174e34aac2bb31d42a88e2c302d088ac00000000"}, + {Hex: "01000000018e5cbbfdf1c4e1c168037bbb305be7018de2d19f6de919c75678daad07c375af010000006b483045022100a0b73eca88a56b30e7f2ea7388aac64506cbdccdc04452b9150344054fc969ae022071dc9e99e42825495ab4f274066a519f0ecaf671d84c156b37458fcd888159ba41210262142850483b6728b8ecd299e4d0c8cf30ea0636f66205166814e52d73b64b4bffffffff0200000000000000000a006a075354554b2e434f90a2e60c000000001976a91454cba8da8701174e34aac2bb31d42a88e2c302d088ac00000000"}, } cfg := broadcast_client.ArcClientConfig{ @@ -29,7 +29,7 @@ func main() { WithArc(cfg). Build() - result, err := client.SubmitBatchTransactions(context.Background(), txs) + result, err := client.SubmitBatchTransactions(context.Background(), txs, broadcast.WithRawFormat()) if err != nil { log.Fatalf("error: %s", err.Error()) } diff --git a/examples/submit_transaction/submit_tx.go b/examples/submit_transaction/submit_tx.go index 0fbb6d4..ff4125e 100644 --- a/examples/submit_transaction/submit_tx.go +++ b/examples/submit_transaction/submit_tx.go @@ -12,7 +12,7 @@ import ( func main() { token := "" apiURL := "https://tapi.taal.com/arc" - tx := broadcast.Transaction{RawTx: "0100000001d6d1607b208b30c0a3fe21d563569c4d2a0f913604b4c5054fe267da6be324ab220000006b4830450221009a965dcd5d42983090a63cfd761038ff8adcea621c46a68a205f326292a95383022061b8d858f366c69f3ebd30a60ccafe36faca4e242ac3d2edd3bf63b669bcf23b4121034e871e147aa4a3e2f1665eaf76cf9264d089b6a91702af92bd6ce33bac84a765ffffffff0123020000000000001976a914d8819a7197d3e221e15f4348203fdecfd29fa2b888ac00000000"} + tx := broadcast.Transaction{Hex: "0100000001d6d1607b208b30c0a3fe21d563569c4d2a0f913604b4c5054fe267da6be324ab220000006b4830450221009a965dcd5d42983090a63cfd761038ff8adcea621c46a68a205f326292a95383022061b8d858f366c69f3ebd30a60ccafe36faca4e242ac3d2edd3bf63b669bcf23b4121034e871e147aa4a3e2f1665eaf76cf9264d089b6a91702af92bd6ce33bac84a765ffffffff0123020000000000001976a914d8819a7197d3e221e15f4348203fdecfd29fa2b888ac00000000"} cfg := broadcast_client.ArcClientConfig{ Token: token, @@ -23,7 +23,7 @@ func main() { WithArc(cfg). Build() - result, err := client.SubmitTransaction(context.Background(), &tx) + result, err := client.SubmitTransaction(context.Background(), &tx, broadcast.WithRawFormat()) if err != nil { log.Fatalf("error: %s", err.Error()) } diff --git a/go.mod b/go.mod index a98ccb4..74686dc 100644 --- a/go.mod +++ b/go.mod @@ -2,11 +2,19 @@ module github.com/bitcoin-sv/go-broadcast-client go 1.20 -require github.com/stretchr/testify v1.8.4 +require ( + github.com/libsv/go-bt/v2 v2.2.5 + github.com/stretchr/testify v1.8.4 +) require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/libsv/go-bk v0.1.6 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/stretchr/objx v0.5.0 // indirect + golang.org/x/crypto v0.13.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 673e918..3841fde 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,20 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/libsv/go-bk v0.1.6 h1:c9CiT5+64HRDbzxPl1v/oiFmbvWZTuUYqywCf+MBs/c= +github.com/libsv/go-bk v0.1.6/go.mod h1:khJboDoH18FPUaZlzRFKzlVN84d4YfdmlDtdX4LAjQA= +github.com/libsv/go-bt/v2 v2.2.5 h1:VoggBLMRW9NYoFujqe5bSYKqnw5y+fYfufgERSoubog= +github.com/libsv/go-bt/v2 v2.2.5/go.mod h1:cV45+jDlPOLfhJLfpLmpQoWzrIvVth9Ao2ZO1f6CcqU= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= @@ -11,8 +23,10 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=