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

fix: return submit errors with context information #83

Merged
merged 7 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 27 additions & 30 deletions broadcast/internal/arc/arc_submit_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strconv"

"github.com/bitcoin-sv/go-broadcast-client/broadcast"
outter_errors "github.com/bitcoin-sv/go-broadcast-client/broadcast/internal"
arc_utils "github.com/bitcoin-sv/go-broadcast-client/broadcast/internal/arc/utils"
"github.com/bitcoin-sv/go-broadcast-client/httpclient"
"github.com/libsv/go-bt/v2"
Expand All @@ -23,7 +24,6 @@ var ErrSubmitTxMarshal = errors.New("error while marshalling submit tx body")

func (a *ArcClient) SubmitTransaction(ctx context.Context, tx *broadcast.Transaction, opts ...broadcast.TransactionOptFunc) (*broadcast.SubmitTxResponse, error) {
if a == nil {
a.Logger.Error().Msgf("Failed to submit tx: %s", broadcast.ErrClientUndefined.Error())
return nil, broadcast.ErrClientUndefined
}

Expand All @@ -34,13 +34,11 @@ func (a *ArcClient) SubmitTransaction(ctx context.Context, tx *broadcast.Transac

result, err := submitTransaction(ctx, a, tx, options)
if err != nil {
a.Logger.Error().Msgf("Failed to submit tx: %s", err.Error())
return nil, err
return nil, outter_errors.New("SubmitTransaction: submitting failed", err)
}

if err := validateSubmitTxResponse(result); err != nil {
a.Logger.Error().Msgf("Failed to validate submit tx response: %s", err.Error())
return nil, err
return nil, outter_errors.New("SubmitTransaction: validation of submit tx response failed", err)
}

response := &broadcast.SubmitTxResponse{
Expand All @@ -49,20 +47,17 @@ func (a *ArcClient) SubmitTransaction(ctx context.Context, tx *broadcast.Transac
}

a.Logger.Debug().Msgf("Got submit tx response from miner: %s", response.Miner)

return response, nil
}

func (a *ArcClient) SubmitBatchTransactions(ctx context.Context, txs []*broadcast.Transaction, opts ...broadcast.TransactionOptFunc) (*broadcast.SubmitBatchTxResponse, error) {
if a == nil {
a.Logger.Error().Msgf("Failed to submit batch txs: %s", broadcast.ErrClientUndefined.Error())
return nil, broadcast.ErrClientUndefined
}

if len(txs) == 0 {
err := errors.New("invalid request, no transactions to submit")
a.Logger.Error().Msgf("Failed to submit batch txs: %s", err.Error())
return nil, err
return nil, outter_errors.New("SubmitBatchTransactions: bad request", err)
}

options := &broadcast.TransactionOpts{}
Expand All @@ -72,13 +67,11 @@ func (a *ArcClient) SubmitBatchTransactions(ctx context.Context, txs []*broadcas

result, err := submitBatchTransactions(ctx, a, txs, options)
if err != nil {
a.Logger.Error().Msgf("Failed to submit batch txs: %s", err.Error())
return nil, err
return nil, outter_errors.New("SubmitBatchTransactions: submitting failed", err)
}

if err := validateBatchResponse(result); err != nil {
a.Logger.Error().Msgf("Failed to validate batch txs response: %s", err.Error())
return nil, err
return nil, outter_errors.New("SubmitBatchTransactions: validation of batch submit tx response failed", err)
}

response := &broadcast.SubmitBatchTxResponse{
Expand Down Expand Up @@ -272,18 +265,18 @@ func efTxRequest(rawTx string) (*SubmitTxRequest, error) {
}

func beefTxRequest(rawTx string) (*SubmitTxRequest, error) {
return nil, fmt.Errorf("submitting transactions in BEEF format is unimplemented yet...")
return nil, errors.New("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
return nil, outter_errors.New("rawTxRequest: bt.NewTxFromString failed", err)
}

for _, input := range transaction.Inputs {
if err = updateUtxoWithMissingData(arc, input); err != nil {
return nil, err
return nil, outter_errors.New("rawTxRequest: updateUtxoWithMissingData() failed", err)
}
}

Expand All @@ -293,19 +286,8 @@ func rawTxRequest(arc *ArcClient, rawTx string) (*SubmitTxRequest, error) {
return request, nil
}

func decodeJunblebusTransaction(resp *http.Response) (*junglebusTransaction, error) {
tx := &junglebusTransaction{}
err := arc_utils.DecodeResponseBody(resp.Body, &tx)
if err != nil {
return nil, err
}

return tx, 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),
Expand All @@ -318,16 +300,20 @@ func updateUtxoWithMissingData(arc *ArcClient, input *bt.Input) error {
arc.HTTPClient.DoRequest,
pld,
decodeJunblebusTransaction,
parseArcError,
nil,
)

if err != nil {
return err
return fmt.Errorf("junglebus request failed: %w", err)
}

if len(tx.Transaction) == 0 {
return errors.New("junglebus responded with empty tx.Transaction[]")
}

actualTx, err := bt.NewTxFromBytes(tx.Transaction)
if err != nil {
return err
return outter_errors.New("converting junglebusTransaction.Transaction to bt.Tx failed", err)
}

o := actualTx.Outputs[input.PreviousTxOutIndex]
Expand All @@ -336,6 +322,17 @@ func updateUtxoWithMissingData(arc *ArcClient, input *bt.Input) error {
return nil
}

func decodeJunblebusTransaction(resp *http.Response) (*junglebusTransaction, error) {
tx := &junglebusTransaction{}
err := arc_utils.DecodeResponseBody(resp.Body, &tx)
if err != nil {
return nil, fmt.Errorf("decodeJunblebusTransaction: %w", err)
}

fmt.Println(tx)
return tx, nil
}

type junglebusTransaction struct {
ID string `json:"id"`
Transaction []byte `json:"transaction"`
Expand Down
2 changes: 1 addition & 1 deletion broadcast/internal/arc/arc_submit_tx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func TestSubmitTransaction(t *testing.T) {

// then
assert.Equal(t, tc.expectedResult, result)
assert.Equal(t, tc.expectedError, err)
assert.Equal(t, tc.expectedError, errors.Unwrap(err))

// assert Expectations on the mock
mockHttpClient.AssertExpectations(t)
Expand Down
44 changes: 44 additions & 0 deletions broadcast/internal/outter_errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package outter_errors
chris-4chain marked this conversation as resolved.
Show resolved Hide resolved

import (
"fmt"
"strings"
)

func New(msg string, inner error) error {
return &outterErr{
msg: msg,
inner: inner,
}
}

type outterErr struct {
dorzepowski marked this conversation as resolved.
Show resolved Hide resolved
msg string
inner error
}

func (e *outterErr) Error() string {
return e.error(1)
chris-4chain marked this conversation as resolved.
Show resolved Hide resolved
arkadiuszos4chain marked this conversation as resolved.
Show resolved Hide resolved
}

func (e *outterErr) Unwrap() error {
return e.inner
}

func (e *outterErr) error(level uint8) string {
chris-4chain marked this conversation as resolved.
Show resolved Hide resolved

if e.inner != nil {
var innerMsg string
if oie, ok := e.inner.(*outterErr); ok {
innerMsg = oie.error(level + 1)
} else {
innerMsg = e.inner.Error()
}

indent := strings.Repeat("\t", int(level))
return fmt.Sprintf("%s\n%sinner error: %s", e.msg, indent, innerMsg)

} else {
return e.msg
}
}
Loading