diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..2745b8e --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,3 @@ +run: + skip-dirs: + - internal/ diff --git a/README.md b/README.md index 5d7fcc7..59c6d6f 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,53 @@ -# WORK IN PROGRESS - # go-broadcast-client + > Interact with Bitcoin SV Overlay Nodes exposing the interface [interface.go](https://github.com/bitcoin-sv/go-broadcast-client/blob/main/broadcast/interface.go) +## Features -# Features - Arc API Support [details](https://github.com/bitcoin-sv/arc): - [x] [Query Transaction Status](https://bitcoin-sv.github.io/arc/api.html#/Arc/GET%20transaction%20status) - [x] [Submit Transaction](https://bitcoin-sv.github.io/arc/api.html#/Arc/POST%20transaction) + - [x] [Submit Batch Transactions](https://bitcoin-sv.github.io/arc/api.html#/Arc/POST%20transactions) - [ ] Quote Services -> WORK IN PROGRESS - - [ ] Submit Batch Transactions -> WORK IN PROGRESS -# What is library doing? +## What is library doing? + It is a wrapper around the [Bitcoin SV Overlay API](https://bitcoin-sv.github.io/arc/api.html) that allows you to interact with the API in a more convenient way by providing a set of custom features to work with multiple nodes and retry logic. -# Custom features - - [x] Possibility to work with multiple nodes [builder pattern](https://github.com/bitcoin-sv/go-broadcast-client/blob/main/broadcast/broadcast-client/client_builder.go) - - [x] Define strategy how to work with multiple nodes [strategy](https://github.com/bitcoin-sv/go-broadcast-client/blob/main/broadcast/internal/composite/strategy.go) - - [x] Gives possibility to handle different client exposing the same interface as Arc [WithArc](https://github.com/bitcoin-sv/go-broadcast-client/blob/main/broadcast/broadcast-client/client_builder.go) - - [x] Possibility to set url and access token for each node independently [Config](https://github.com/bitcoin-sv/go-broadcast-client/blob/main/broadcast/broadcast-client/arc_config.go) - - [x] Possibility to use custom http client [WithHTTPClient](https://github.com/bitcoin-sv/go-broadcast-client/blob/main/broadcast/broadcast-client/client_builder.go#L19) +## Custom features + +- [x] Possibility to work with multiple nodes [builder pattern](https://github.com/bitcoin-sv/go-broadcast-client/blob/main/broadcast/broadcast-client/client_builder.go) + +- [x] Define strategy how to work with multiple nodes [strategy](https://github.com/bitcoin-sv/go-broadcast-client/blob/main/broadcast/internal/composite/strategy.go) + +- [x] Gives possibility to handle different client exposing the same interface as Arc [WithArc](https://github.com/bitcoin-sv/go-broadcast-client/blob/main/broadcast/broadcast-client/client_builder.go) + +- [x] Possibility to set url and access token for each node independently [Config](https://github.com/bitcoin-sv/go-broadcast-client/blob/main/broadcast/broadcast-client/arc_config.go) + +- [x] Possibility to use custom http client [WithHTTPClient](https://github.com/bitcoin-sv/go-broadcast-client/blob/main/broadcast/broadcast-client/client_builder.go#L19) + +## How to use it? -# How to use it? +### Create client -## Create client ```go // Set the access token and url for the node - token := "" - apiURL := "https://tapi.taal.com/arc" + token := "" + apiURL := "https://tapi.taal.com/arc" - cfg := broadcast_client.ArcClientConfig{ - Token: token, - APIUrl: apiURL, - } + cfg := broadcast_client.ArcClientConfig{ + Token: token, + APIUrl: apiURL, + } - client := broadcast_client.Builder(). - WithArc(cfg). - Build() + client := broadcast_client.Builder(). + WithArc(cfg). + Build() ``` -## Use the method exposed by the interface +### Use the method exposed by the interface + ```go // ... hex := "9c5f5244ee45e8c3213521c1d1d5df265d6c74fb108961a876917073d65fef14" @@ -51,7 +58,169 @@ custom features to work with multiple nodes and retry logic. Examples of usage can be found in the [examples](https://github.com/bitcoin-sv/go-broadcast-client/tree/main/examples) -# Transaction Statuses returned by the library +## ClientBuilder Methods + +Client allows you to create your own client with setting it with multiple nodes and custom http client. + +### WithArc Method + +```go +// Set the access token and url for the node + token := "" + apiURL := "https://tapi.taal.com/arc" + + cfg := broadcast_client.ArcClientConfig{ + Token: token, + APIUrl: apiURL, + } + + client := broadcast_client.Builder(). + WithArc(cfg). + Build() +``` + +We can also call multiple times the method `WithArc` to set multiple nodes. + +```go +// Set the access token and url for the node + client := broadcast_client.Builder(). + WithArc(cfg1). + WithArc(cfg2). + WithArc(cfg3). + Build() +``` + +What is the call order if we have multiple nodes configured? + +We use the **strategy** to define the order of the calls. The default strategy is `OneByOne` in RoundRobin algorith that will call the nodes in the order they were set. + **Only if all of them fail, we will return the error to the user.** + +### WithHTTPClient Method + +```go +// (...) + clent := broadcast_client.Builder(). + WithArc(cfg). + WithHTTPClient(&http.Client{}). + Build() +``` + +We can use the method `WithHTTPClient` to set the custom http client. +It needs to implement the interface `HTTPInterface` that is defined in the [httpclient.go](/broadcast/internal/httpclient/http_client.go#L35) + +```go +type HTTPInterface interface { + DoRequest(ctx context.Context, pld HTTPRequest) (*http.Response, error) +} +``` + + +## ARC Client Methods + +### QueryTx Method + +When you created your own client, you can use the method `QueryTx` to query the status of a transaction. + +```go + // ... + hex := "9c5f5244ee45e8c3213521c1d1d5df265d6c74fb108961a876917073d65fef14" + + result, err := client.QueryTransaction(context.Background(), hex) + // ... +``` + +### SubmitTx Method + +Having your client created, you can use the method `SubmitTx` to submit a single transaction to the node. + +```go + // ... + tx := broadcast.Transaction{ + RawTx: "xyz", + } + + result, err := client.SubmitTransaction(context.Background(), tx) + // ... +``` + +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. + +Setting tx.CallBackURL and tx.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. + +### SubmitBatchTx Method + +Having your client created, you can use the method `SubmitBatchTx` to submit a batch of transactions to the node. + +```go + // ... + txs := []*broadcast.Transaction{ + {RawTx: "xyz1"}, + {RawTx: "xyz2"}, + {RawTx: "xyz3"}, + {RawTx: "xyz4"}, + {RawTx: "xyz5"}, + } + + result, err := client.SubmitBatchTransaction(context.Background(), txs) + // ... +``` + +The method works the same as the `SubmitTx` method, but it is sending a batch of transactions instead of a single one. It is also receiving a batch of responses for each of the transactions. + +## Models and constants + +### QueryTx + +#### QueryTxResponse + +```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"` +} +``` + +### SubmitTx + +#### SubmitTxResponse + +```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"` +} +``` + +#### 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"` +} + ``` + +### Transaction Statuses returned by the library | Code | Status | Description | |-----|------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| @@ -66,6 +235,6 @@ Examples of usage can be found in the [examples](https://github.com/bitcoin-sv/g | 8 | `SEEN_ON_NETWORK` | The transaction has been seen on the Bitcoin network and propagated to other nodes. This status is set when metamorph receives an INV message for the transaction from another node than it was sent to. | | 9 | `MINED` | The transaction has been mined into a block by a mining node. | | 108 | `CONFIRMED` | The transaction is marked as confirmed when it is in a block with 100 blocks built on top of that block. | -| 109 | `REJECTED` | The transaction has been rejected by the Bitcoin network. +| 109 | `REJECTED` | The transaction has been rejected by the Bitcoin network. *Source* [Arc API](https://github.com/bitcoin-sv/arc/blob/main/README.md) diff --git a/broadcast/broadcast-client/arc_config.go b/broadcast/broadcast-client/arc_config.go index 795462b..9483bc7 100644 --- a/broadcast/broadcast-client/arc_config.go +++ b/broadcast/broadcast-client/arc_config.go @@ -1,14 +1,19 @@ +// Package broadcast_client contains the client for the broadcast service. package broadcast_client +// ArcClientConfig is used by [WithArc] to set up the connection between the broadcast client and Arc. +// The provided token will be used as the Authorization header. type ArcClientConfig struct { APIUrl string Token string } +// GetApiUrl returns the API url. func (c *ArcClientConfig) GetApiUrl() string { return c.APIUrl } +// GetToken returns the token. func (c *ArcClientConfig) GetToken() string { return c.Token } diff --git a/broadcast/broadcast-client/client_builder.go b/broadcast/broadcast-client/client_builder.go index 48f81b2..18b3af0 100644 --- a/broadcast/broadcast-client/client_builder.go +++ b/broadcast/broadcast-client/client_builder.go @@ -12,15 +12,19 @@ type builder struct { client httpclient.HTTPInterface } +// Builder is used to prepare the broadcast client. It is recommended to use that builder for creating the broadcast client. func Builder() *builder { return &builder{} } +// WithHttpClient sets the http client to be used by the broadcast client. It requires a httpclient.HTTPInterface to be passed. func (cb *builder) WithHttpClient(client httpclient.HTTPInterface) *builder { cb.client = client return cb } +// WithArc sets up the connection of the broadcast client to the Arc service using the provided ArcClientConfig. +// This method can be called multiple times with different ArcClientConfigurations to establish connections to multiple Arc instances. func (cb *builder) WithArc(config ArcClientConfig) *builder { cb.factories = append(cb.factories, func() broadcast_api.Client { return arc.NewArcClient(&config, cb.client) @@ -28,6 +32,7 @@ func (cb *builder) WithArc(config ArcClientConfig) *builder { return cb } +// Build builds the broadcast client based on the provided configuration. func (cb *builder) Build() broadcast_api.Client { if len(cb.factories) == 1 { return cb.factories[0]() diff --git a/broadcast/errors.go b/broadcast/errors.go index babac21..0938b76 100644 --- a/broadcast/errors.go +++ b/broadcast/errors.go @@ -1,3 +1,4 @@ +// Package broadcast provides a set of functions to broadcast or query transactions to the Bitcoin SV network using the client provided. package broadcast import ( @@ -6,22 +7,43 @@ import ( "strings" ) +// ErrClientUndefined is returned when the client is undefined. +// Example: +// +// func (a *ArcClient) QueryTransaction(ctx context.Context, txID string) (*broadcast.QueryTxResponse, error) { +// if a == nil { +// return nil, broadcast.ErrClientUndefined +// } +// +// It should be returned for all defined clients in the future. var ErrClientUndefined = errors.New("client is undefined") +// ErrAllBroadcastersFailed is returned when all configured broadcasters failed to broadcast the transaction. var ErrAllBroadcastersFailed = errors.New("all broadcasters failed") +// ErrURLEmpty is returned when the API URL is empty. var ErrURLEmpty = errors.New("url is empty") +// ErrBroadcastFailed is returned when the broadcast failed. var ErrBroadcasterFailed = errors.New("broadcaster failed") +// ErrUnableToDecodeResponse is returned when the http response cannot be decoded. var ErrUnableToDecodeResponse = errors.New("unable to decode response") +// ErrMissingStatus is returned when the tx status is missing. var ErrMissingStatus = errors.New("missing tx status") +// ErrStrategyUnkown is returned when the strategy provided is unknown. +// Example: +// +// func NewBroadcaster(strategy Strategy, factories ...BroadcastFactory) broadcast.Client +// Calling NewBroadcaster we need to provide a strategy, if the strategy is unknown (we don't have an implementation for that) we return ErrStrategyUnkown. var ErrStrategyUnkown = errors.New("unknown strategy") +// ErrNoMinerResponse is returned when no response is received from any miner. var ErrNoMinerResponse = errors.New("failed to get reponse from any miner") +// ArcError is general type for the error returned by the ArcClient. type ArcError struct { Type string `json:"type"` Title string `json:"title"` @@ -32,6 +54,7 @@ type ArcError struct { ExtraInfo string `json:"extraInfo,omitempty"` } +// Error returns the error string it's the implementation of the error interface. func (err ArcError) Error() string { sb := strings.Builder{} diff --git a/broadcast/interface.go b/broadcast/interface.go index 2bc3020..a74a439 100644 --- a/broadcast/interface.go +++ b/broadcast/interface.go @@ -21,18 +21,29 @@ type PolicyQuoter interface { GetPolicyQuote(ctx context.Context) (*PolicyQuoteResponse, error) } +// TransactionQuerier is the interface that wraps the QueryTransaction method. +// It takes a transaction ID and returns the transaction details, like it's status, hash, height etc. +// Everything is wrapped in the QueryTxResponse struct. type TransactionQuerier interface { QueryTransaction(ctx context.Context, txID string) (*QueryTxResponse, error) } +// TransactionSubmitter is the interface that wraps the SubmitTransaction method. +// It takes a transaction and tries to broadcast it to the P2P network. +// Transaction object needs RawTx to be set. All other fields are optional and used to append headers related to status callbacks. +// As a result it returns a SubmitTxResponse object. type TransactionSubmitter interface { SubmitTransaction(ctx context.Context, tx *Transaction) (*SubmitTxResponse, error) } +// TransactionsSubmitter is the interface that wraps the SubmitBatchTransactions method. +// It is the same as TransactionSubmitter but it takes a slice of transactions and tries to broadcast them to the P2P network. +// As a result it returns a slice of SubmitTxResponse objects. type TransactionsSubmitter interface { SubmitBatchTransactions(ctx context.Context, tx []*Transaction) ([]*SubmitTxResponse, error) } +// Client is a grouping interface that represents the entire exposed functionality of the broadcast client. type Client interface { BestQuoter FastestQuoter diff --git a/broadcast/internal/arc/arc_client.go b/broadcast/internal/arc/arc_client.go index 85eae5f..7ff9b19 100644 --- a/broadcast/internal/arc/arc_client.go +++ b/broadcast/internal/arc/arc_client.go @@ -27,6 +27,7 @@ func NewArcClient(config Config, client httpclient.HTTPInterface) broadcast_api. if client == nil { client = httpclient.NewHttpClient() } + return &ArcClient{ apiURL: config.GetApiUrl(), token: config.GetToken(), diff --git a/broadcast/internal/arc/arc_query_tx.go b/broadcast/internal/arc/arc_query_tx.go index ebab67a..c2ffb06 100644 --- a/broadcast/internal/arc/arc_query_tx.go +++ b/broadcast/internal/arc/arc_query_tx.go @@ -5,7 +5,7 @@ import ( "errors" "github.com/bitcoin-sv/go-broadcast-client/broadcast" - arc_utils "github.com/bitcoin-sv/go-broadcast-client/broadcast/internal/arc/utils" + arcutils "github.com/bitcoin-sv/go-broadcast-client/broadcast/internal/arc/utils" "github.com/bitcoin-sv/go-broadcast-client/broadcast/internal/httpclient" ) @@ -29,7 +29,6 @@ func (a *ArcClient) QueryTransaction(ctx context.Context, txID string) (*broadca return result, nil } -// queryTransaction will fire the HTTP request to retrieve the tx status and details func queryTransaction(ctx context.Context, arc *ArcClient, txHash string) (*broadcast.QueryTxResponse, error) { url := arc.apiURL + arcQueryTxRoute + txHash pld := httpclient.NewPayload( @@ -48,7 +47,7 @@ func queryTransaction(ctx context.Context, arc *ArcClient, txHash string) (*broa } model := broadcast.QueryTxResponse{} - err = arc_utils.DecodeResponseBody(resp.Body, &model) + err = arcutils.DecodeResponseBody(resp.Body, &model) if err != nil { return nil, err } diff --git a/broadcast/internal/arc/arc_submit_tx.go b/broadcast/internal/arc/arc_submit_tx.go index da24346..e9e4977 100644 --- a/broadcast/internal/arc/arc_submit_tx.go +++ b/broadcast/internal/arc/arc_submit_tx.go @@ -39,7 +39,7 @@ func (a *ArcClient) SubmitBatchTransactions(ctx context.Context, txs []*broadcas return nil, broadcast.ErrClientUndefined } - if txs == nil || len(txs) == 0 { + if len(txs) == 0 { return nil, errors.New("invalid request, no transactions to submit") } diff --git a/broadcast/internal/arc/utils/arc_utils.go b/broadcast/internal/arc/utils/arc_utils.go index c78ea6f..8dc77e4 100644 --- a/broadcast/internal/arc/utils/arc_utils.go +++ b/broadcast/internal/arc/utils/arc_utils.go @@ -1,14 +1,17 @@ -package arc_utils +package arcutils import ( "bytes" "encoding/json" + "errors" "io" "github.com/bitcoin-sv/go-broadcast-client/broadcast" "github.com/bitcoin-sv/go-broadcast-client/broadcast/internal/httpclient" ) +var ErrUnableToDecodeArcError = errors.New("unable to decode arc error") + func HandleHttpError(httpClientError error) error { noSuccessResponseErr, ok := httpClientError.(httpclient.HttpClientError) @@ -51,7 +54,11 @@ func decodeArcError(httpErr httpclient.HttpClientError) error { bodyReader := io.TeeReader(response.Body, &buffer) resultError := broadcast.ArcError{} - json.NewDecoder(bodyReader).Decode(&resultError) // ignore decoding error + err := json.NewDecoder(bodyReader).Decode(&resultError) + + if err != nil { + return ErrUnableToDecodeArcError + } if resultError.Title != "" { return resultError diff --git a/broadcast/internal/composite/broadcaster.go b/broadcast/internal/composite/broadcaster.go index 36b2874..09823e1 100644 --- a/broadcast/internal/composite/broadcaster.go +++ b/broadcast/internal/composite/broadcaster.go @@ -75,6 +75,7 @@ func (c *compositeBroadcaster) GetFeeQuote(ctx context.Context) (*broadcast.FeeQ return feeQuote, nil } +// GetBestQuote: ... func (c *compositeBroadcaster) GetBestQuote(ctx context.Context) (*broadcast.FeeQuote, error) { fees := make(chan *broadcast.FeeQuote, len(c.broadcasters)) var wg sync.WaitGroup @@ -114,6 +115,7 @@ func (c *compositeBroadcaster) GetBestQuote(ctx context.Context) (*broadcast.Fee return bestQuote, nil } +// GetFastestQuote: ... func (c *compositeBroadcaster) GetFastestQuote(ctx context.Context, timeout time.Duration) (*broadcast.FeeQuote, error) { if timeout.Seconds() == 0 { timeout = broadcast.DefaultFastestQuoteTimeout diff --git a/broadcast/internal/composite/broadcaster_test.go b/broadcast/internal/composite/broadcaster_test.go index 6537653..61cc11b 100644 --- a/broadcast/internal/composite/broadcaster_test.go +++ b/broadcast/internal/composite/broadcaster_test.go @@ -7,40 +7,37 @@ import ( "github.com/stretchr/testify/assert" ) -// MockClient to mock broadcast client type MockClient struct { broadcast.Client } -// MockBroadcastFactory to mock broadcast factory type MockBroadcastFactory struct{} -// Create mock client func (m *MockBroadcastFactory) Create() broadcast.Client { return &MockClient{} } func TestNewBroadcasterWithDefaultStrategy(t *testing.T) { - // Given + // given mockFactory := &MockBroadcastFactory{} - // When + // when broadcaster := NewBroadcasterWithDefaultStrategy(mockFactory.Create) - // Then + // then assert.NotNil(t, broadcaster) _, ok := broadcaster.(*compositeBroadcaster) assert.True(t, ok, "Expected broadcaster to be of type *compositeBroadcaster") } func TestNewBroadcaster(t *testing.T) { - // Given + // given mockFactory := &MockBroadcastFactory{} - // When + // when broadcaster := NewBroadcaster(*OneByOne, mockFactory.Create) - // Then + // then assert.NotNil(t, broadcaster) _, ok := broadcaster.(*compositeBroadcaster) assert.True(t, ok, "Expected broadcaster to be of type *compositeBroadcaster") diff --git a/broadcast/internal/composite/strategy.go b/broadcast/internal/composite/strategy.go index aadea2f..6099a9a 100644 --- a/broadcast/internal/composite/strategy.go +++ b/broadcast/internal/composite/strategy.go @@ -9,14 +9,20 @@ import ( type StrategyName string const ( + // OneByOneStrategy is a strategy that executes the execution funcs one by one until one of them succeeds. + // If all execution funcs fail, then the strategy returns an error. + // The error is: ErrAllBroadcastersFailed. OneByOneStrategy StrategyName = "OneByOneStrategy" ) type Result interface{} type executionFunc func(context.Context) (Result, error) + type StrategyExecutionFunc func(context.Context, []executionFunc) (Result, error) +// Strategy is a component designed to offer flexibility in selecting a communication approach +// for interacting with multiple broadcasting services, such as multiple Arc services. type Strategy struct { name StrategyName executionFunc StrategyExecutionFunc diff --git a/broadcast/internal/composite/strategy_test.go b/broadcast/internal/composite/strategy_test.go index 82865ce..310e61e 100644 --- a/broadcast/internal/composite/strategy_test.go +++ b/broadcast/internal/composite/strategy_test.go @@ -70,7 +70,6 @@ func TestStrategy_Execute(t *testing.T) { } func TestNew(t *testing.T) { - // Test case: Default OneByOne strategy t.Run("should return correct strategy for OneByOneStrategy", func(t *testing.T) { // given expectedStrategyName := OneByOneStrategy @@ -83,7 +82,6 @@ func TestNew(t *testing.T) { assert.Equal(t, expectedStrategyName, actualStrategy.name) }) - // Test case: Unknown strategy t.Run("should return error for unknown strategy name", func(t *testing.T) { // given unknownStrategyName := StrategyName("Unknown") diff --git a/broadcast/query_tx.go b/broadcast/query_tx.go index 4b42d77..5e1ad8b 100644 --- a/broadcast/query_tx.go +++ b/broadcast/query_tx.go @@ -1,9 +1,15 @@ package broadcast +// QueryTxResponse is the response returned by the QueryTransaction method. 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 is the hash of the block where the transaction was included. + BlockHash string `json:"blockHash,omitempty"` + // BlockHeight is the height of the block where the transaction was included. + BlockHeight int64 `json:"blockHeight,omitempty"` + // Timestamp is the timestamp of the block where the transaction was included. + Timestamp string `json:"timestamp,omitempty"` + // TxID is the transaction id. + TxID string `json:"txid,omitempty"` + // TxStatus is the status of the transaction. + TxStatus TxStatus `json:"txStatus,omitempty"` } diff --git a/broadcast/submit_tx.go b/broadcast/submit_tx.go index 80454f9..f616a67 100644 --- a/broadcast/submit_tx.go +++ b/broadcast/submit_tx.go @@ -1,10 +1,17 @@ package broadcast +// SubmitTxResponse is the response returned by the SubmitTransaction method. 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 is the hash of the block where the transaction was included. + BlockHash string `json:"blockHash,omitempty"` + // BlockHeight is the height of the block where the transaction was included. + BlockHeight int64 `json:"blockHeight,omitempty"` + // ExtraInfo is the extra info returned by the broadcaster. + ExtraInfo string `json:"extraInfo,omitempty"` + // Status is the status of the response. + Status int `json:"status,omitempty"` + // Title is the title of the response. + Title string `json:"title,omitempty"` + // TxStatus is the status of the transaction. + TxStatus TxStatus `json:"txStatus,omitempty"` } diff --git a/broadcast/transaction.go b/broadcast/transaction.go index 76b5ce9..c69df69 100644 --- a/broadcast/transaction.go +++ b/broadcast/transaction.go @@ -2,12 +2,20 @@ package broadcast // Transaction is the body contents in the "submit transaction" request 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 is the encryption method used for the callback (to set in the callback header). + CallBackEncryption string `json:"callBackEncryption,omitempty"` + // CallbackToken is the token used for the callback (to set in the callback header). + CallBackToken string `json:"callBackToken,omitempty"` + // CallbackURL is the URL used for the callback (to set in the callback header). + CallBackURL string `json:"callBackUrl,omitempty"` + // DsCheck is the double spend check flag. + DsCheck bool `json:"dsCheck,omitempty"` + // MerkleFormat is the requested merkle format (to set in the merkle format header). + MerkleFormat string `json:"merkleFormat,omitempty"` + // MerkleProof is the merkle proof flag - if merkle proof should be included in the response (to set in the merkle proof header). + MerkleProof bool `json:"merkleProof,omitempty"` + // RawTx is the raw transaction string -> required. + RawTx string `json:"rawtx"` + // WaitForStatus is the status to wait for with the callback (to set in the wait for status header). + WaitForStatus TxStatus `json:"waitForStatus,omitempty"` } diff --git a/broadcast/tx_status.go b/broadcast/tx_status.go index 6e43051..3e98e72 100644 --- a/broadcast/tx_status.go +++ b/broadcast/tx_status.go @@ -5,29 +5,29 @@ type TxStatus string // List of statuses available here: https://github.com/bitcoin-sv/arc const ( - // The transaction has been sent to metamorph, but no processing has taken place. This should never be the case, unless something goes wrong. + // Unknown status means that transaction has been sent to metamorph, but no processing has taken place. This should never be the case, unless something goes wrong. Unknown TxStatus = "UNKNOWN" // 0 - // The transaction has been queued for processing. + // Queued status means that transaction has been queued for processing. Queued TxStatus = "QUEUED" // 1 - // The transaction has been properly received by the metamorph processor. + // Received status means that transaction has been properly received by the metamorph processor. Received TxStatus = "RECEIVED" // 2 - // The transaction has been stored in the metamorph store. This should ensure the transaction will be processed and retried if not picked up immediately by a mining node. + // Stored status means that transaction has been stored in the metamorph store. This should ensure the transaction will be processed and retried if not picked up immediately by a mining node. Stored TxStatus = "STORED" // 3 - // The transaction has been announced (INV message) to the Bitcoin network. + // AnnouncedToNetwork status means that transaction has been announced (INV message) to the Bitcoin network. AnnouncedToNetwork TxStatus = "ANNOUNCED_TO_NETWORK" // 4 - // The transaction has been requested from metamorph by a Bitcoin node. + // RequestedByNetwork status means that transaction has been requested from metamorph by a Bitcoin node. RequestedByNetwork TxStatus = "REQUESTED_BY_NETWORK" // 5 - // The transaction has been sent to at least 1 Bitcoin node. + // SentToNetwork status means that transaction has been sent to at least 1 Bitcoin node. SentToNetwork TxStatus = "SENT_TO_NETWORK" // 6 - // The transaction has been accepted by a connected Bitcoin node on the ZMQ interface. If metamorph is not connected to ZQM, this status will never by set. + // AcceptedByNetwork status means that transaction has been accepted by a connected Bitcoin node on the ZMQ interface. If metamorph is not connected to ZQM, this status will never by set. AcceptedByNetwork TxStatus = "ACCEPTED_BY_NETWORK" // 7 - // The transaction has been seen on the Bitcoin network and propagated to other nodes. This status is set when metamorph receives an INV message for the transaction from another node than it was sent to. + // SeenOnNetwork status means that transaction has been seen on the Bitcoin network and propagated to other nodes. This status is set when metamorph receives an INV message for the transaction from another node than it was sent to. SeenOnNetwork TxStatus = "SEEN_ON_NETWORK" // 8 - // The transaction has been mined into a block by a mining node. + // Mined status means that transaction has been mined into a block by a mining node. Mined TxStatus = "MINED" // 9 - // The transaction is marked as confirmed when it is in a block with 100 blocks built on top of that block. + // Confirmed status means that transaction is marked as confirmed when it is in a block with 100 blocks built on top of that block. Confirmed TxStatus = "CONFIRMED" // 108 - // The transaction has been rejected by the Bitcoin network. + // Rejected status means that transaction has been rejected by the Bitcoin network. Rejected TxStatus = "REJECTED" // 109 ) diff --git a/examples/query_transaction/query_tx.go b/examples/query_transaction/query_tx.go index 43b1e17..5610196 100644 --- a/examples/query_transaction/query_tx.go +++ b/examples/query_transaction/query_tx.go @@ -7,6 +7,7 @@ import ( broadcast_client "github.com/bitcoin-sv/go-broadcast-client/broadcast/broadcast-client" ) +// This example shows how to query a transaction by its hex. func main() { token := "" apiURL := "https://tapi.taal.com/arc" diff --git a/examples/submit_batch_transactions/submit_batch_txs.go b/examples/submit_batch_transactions/submit_batch_txs.go index 95e3a92..80c7f8b 100644 --- a/examples/submit_batch_transactions/submit_batch_txs.go +++ b/examples/submit_batch_transactions/submit_batch_txs.go @@ -8,6 +8,7 @@ import ( broadcast_client "github.com/bitcoin-sv/go-broadcast-client/broadcast/broadcast-client" ) +// This example shows how to submit a batch of transactions. func main() { token := "{token}" apiURL := "https://tapi.taal.com/arc" diff --git a/examples/submit_transaction/submit_tx.go b/examples/submit_transaction/submit_tx.go index 3d574db..b33e9ec 100644 --- a/examples/submit_transaction/submit_tx.go +++ b/examples/submit_transaction/submit_tx.go @@ -8,6 +8,7 @@ import ( broadcast_client "github.com/bitcoin-sv/go-broadcast-client/broadcast/broadcast-client" ) +// This example shows how to submit a transaction. func main() { token := "" apiURL := "https://tapi.taal.com/arc"