minercraft

package module
v0.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 29, 2021 License: MIT Imports: 17 Imported by: 1

README

go-minercraft

Interact with Bitcoin Miner APIs (unofficial Go library of Minercraft)

Release Build Status Report codecov Go
Mergify Status Gitpod Ready-to-Code


Table of Contents


Installation

go-minercraft requires a supported release of Go.

go get -u github.com/tonicpow/go-minercraft

Documentation

View the generated documentation

GoDoc

This package interacts with BSV miners using the Merchant API specification.

View documentation on hosting your own mAPI server.

Features
  • Merchant API Support:
  • Custom Features:
    • Client is completely configurable
    • Using default heimdall http client with exponential backoff & more
    • Use your own HTTP client
    • Use your own miner configuration
    • Uses common type: bt.Fee for easy integration across projects
    • Current miner information located at response.Miner.name and defaults
    • Automatic Signature Validation response.Validated=true/false
    • AddMiner() for adding your own customer miner configuration
    • RemoveMiner() for removing any miner configuration
    • FastestQuote() asks all miners and returns the fastest quote response
    • BestQuote() gets all quotes from miners and return the best rate/quote
    • CalculateFee() returns the fee for a given transaction
Library Deployment

goreleaser for easy binary or library deployment to Github and can be installed via: brew install goreleaser.

The .goreleaser.yml file is used to configure goreleaser.

Use make release-snap to create a snapshot version of the release, and finally make release to ship to production.

Makefile Commands

View all makefile commands

make help

List of all current commands:

all                  Runs multiple commands
clean                Remove previous builds and any test cache data
clean-mods           Remove all the Go mod cache
coverage             Shows the test coverage
diff                 Show the git diff
generate             Runs the go generate command in the base of the repo
godocs               Sync the latest tag with GoDocs
help                 Show this help message
install              Install the application
install-go           Install the application (Using Native Go)
lint                 Run the golangci-lint application (install if not found)
release              Full production release (creates release in Github)
release              Runs common.release then runs godocs
release-snap         Test the full release (build binaries)
release-test         Full production test release (everything except deploy)
replace-version      Replaces the version in HTML/JS (pre-deploy)
tag                  Generate a new tag and push (tag version=0.0.0)
tag-remove           Remove a tag if found (tag-remove version=0.0.0)
tag-update           Update an existing tag to current commit (tag-update version=0.0.0)
test                 Runs lint and ALL tests
test-ci              Runs all tests via CI (exports coverage)
test-ci-no-race      Runs all tests via CI (no race) (exports coverage)
test-ci-short        Runs unit tests via CI (exports coverage)
test-no-lint         Runs just tests
test-short           Runs vet, lint and tests (excludes integration tests)
test-unit            Runs tests and outputs coverage
uninstall            Uninstall the application (and remove files)
update-linter        Update the golangci-lint package (macOS only)
vet                  Run the Go vet application

Examples & Tests

All unit tests and examples run via Github Actions and uses Go version 1.15.x. View the configuration file.

Run all tests (including integration tests)

make test

Run tests (excluding integration tests)

make test-short

Benchmarks

Run the Go benchmarks:

make bench

Code Standards

Read more about this Go project's code standards.


Usage

View the examples


Maintainers

MrZ
MrZ

Contributing

View the contributing guidelines and please follow the code of conduct.

How can I help?

All kinds of contributions are welcome 🙌! The most basic way to show your support is to star 🌟 the project, or to raise issues 💬. You can also support this project by becoming a sponsor on GitHub 👏 or by making a bitcoin donation to ensure this journey continues indefinitely! 🚀

Stars

Credits

Unwriter & Interplanaria for their original contribution: Minercraft which was the inspiration for this library.

nChain & team for developing the brfc-merchant-api specifications.


License

License

Documentation

Overview

Package minercraft is an unofficial Go version of Unwriter's Minercraft

If you have any suggestions or comments, please feel free to open an issue on this GitHub repository!

By TonicPow Inc (https://tonicpow.com)

Index

Examples

Constants

View Source
const (
	// MinerTaal is the name of the known miner for "Taal"
	MinerTaal = "Taal"

	// MinerMempool is the name of the known miner for "Mempool"
	MinerMempool = "Mempool"

	// MinerMatterpool is the name of the known miner for "Matterpool"
	MinerMatterpool = "Matterpool"
)
View Source
const (

	// FeeTypeData is the key corresponding to the data rate
	FeeTypeData = "data"

	// FeeTypeStandard is the key corresponding to the standard rate
	FeeTypeStandard = "standard"

	// FeeCategoryMining is the category corresponding to the mining rate
	FeeCategoryMining = "mining"

	// FeeCategoryRelay is the category corresponding to the relay rate
	FeeCategoryRelay = "relay"
)
View Source
const KnownMiners = `` /* 595-byte string literal not displayed */

KnownMiners is a pre-filled list of known miners Any pre-filled tokens are for free use only update your custom token with client.MinerUpdateToken("name", "token")

View Source
const (
	// MerkleFormatTSC can be set when calling SubmitTransaction to request a MerkleProof in TSC format.
	MerkleFormatTSC = "TSC"
)
View Source
const QueryTransactionFailure = "failure"

QueryTransactionFailure is on failure

View Source
const QueryTransactionInMempoolFailure = "Transaction in mempool but not yet in block"

QueryTransactionInMempoolFailure in mempool but not in a block yet

View Source
const QueryTransactionSuccess = "success"

QueryTransactionSuccess is on success

Variables

This section is empty.

Functions

This section is empty.

Types

type Callback added in v0.2.11

type Callback struct {
	CallbackPayload string `json:"callbackPayload"`
	APIVersion      string `json:"apiVersion"`
	Timestamp       string `json:"timestamp"`
	MinerID         string `json:"minerId"`
	BlockHash       string `json:"blockHash"`
	BlockHeight     uint64 `json:"blockHeight"`
	CallbackTxID    string `json:"callbackTxId"`
	CallbackReason  string `json:"callbackReason"`
}

Callback is the body contents posted to the provided callback url from Merchant API

type Client

type Client struct {
	Miners  []*Miner       // List of loaded miners
	Options *ClientOptions // Client options config
	// contains filtered or unexported fields
}

Client is the parent struct that contains the miner clients and list of miners to use

func NewClient

func NewClient(clientOptions *ClientOptions, customHTTPClient *http.Client,
	customMiners []*Miner) (client *Client, err error)

NewClient creates a new client for requests

clientOptions: inject custom client options on load customHTTPClient: use your own custom HTTP client customMiners: use your own custom list of miners

Example

ExampleNewClient example using NewClient()

client, err := NewClient(nil, nil, nil)
if err != nil {
	fmt.Printf("error occurred: %s", err.Error())
	return
}

fmt.Printf("created new client with %d default miners", len(client.Miners))
Output:

created new client with 3 default miners

func (*Client) AddMiner

func (c *Client) AddMiner(miner Miner) error

AddMiner will add a new miner to the list of miners

Example

ExampleClient_AddMiner example using AddMiner()

client, err := NewClient(nil, nil, nil)
if err != nil {
	fmt.Printf("error occurred: %s", err.Error())
	return
}

// Add a miner
if err = client.AddMiner(Miner{Name: testMinerName, URL: testMinerURL}); err != nil {
	fmt.Printf("error occurred: %s", err.Error())
	return
}

// Get miner by name
fmt.Printf("created new miner named: %s", client.MinerByName(testMinerName).Name)
Output:

created new miner named: TestMiner

func (*Client) BestQuote

func (c *Client) BestQuote(ctx context.Context, feeCategory, feeType string) (*FeeQuoteResponse, error)

BestQuote will check all known miners and compare rates, returning the best rate/quote

Note: this might return different results each time if miners have the same rates as it's a race condition on which results come back first

Example

ExampleClient_BestQuote example using BestQuote()

// Create a client (using a test client vs NewClient())
client := newTestClient(&mockHTTPValidBestQuote{})

// Create a req
_, err := client.BestQuote(context.Background(), FeeCategoryMining, FeeTypeData)
if err != nil {
	fmt.Printf("error occurred: %s", err.Error())
	return
}

// Note: cannot show response since the miner might be different each time
fmt.Printf("got best quote!")
Output:

got best quote!

func (*Client) FastestQuote added in v0.0.4

func (c *Client) FastestQuote(ctx context.Context, timeout time.Duration) (*FeeQuoteResponse, error)

FastestQuote will check all known miners and return the fastest quote response

Note: this might return different results each time if miners have the same rates as it's a race condition on which results come back first

Example

ExampleClient_FastestQuote example using FastestQuote()

// Create a client (using a test client vs NewClient())
client := newTestClient(&mockHTTPValidFastestQuote{})

// Create a req
_, err := client.FastestQuote(context.Background(), defaultFastQuoteTimeout)
if err != nil {
	fmt.Printf("error occurred: %s", err.Error())
	return
}

// Note: cannot show response since the miner might be different each time
fmt.Printf("got fastest quote!")
Output:

got fastest quote!

func (*Client) FeeQuote

func (c *Client) FeeQuote(ctx context.Context, miner *Miner) (*FeeQuoteResponse, error)

FeeQuote will fire a Merchant API request to retrieve the fees from a given miner

This endpoint is used to get the different fees quoted by a miner. It returns a JSONEnvelope with a payload that contains the fees charged by a specific BSV miner. The purpose of the envelope is to ensure strict consistency in the message content for the purpose of signing responses.

Specs: https://github.com/bitcoin-sv-specs/brfc-merchantapi/tree/v1.2-beta#get-fee-quote

Example

ExampleClient_FeeQuote example using FeeQuote()

// Create a client (using a test client vs NewClient())
client := newTestClient(&mockHTTPValidFeeQuote{})

// Create a req
response, err := client.FeeQuote(context.Background(), client.MinerByName(MinerTaal))
if err != nil {
	fmt.Printf("error occurred: %s", err.Error())
	return
}

fmt.Printf("got quote from: %s", response.Miner.Name)
Output:

got quote from: Taal

func (*Client) MinerByID

func (c *Client) MinerByID(minerID string) *Miner

MinerByID will return a miner given a miner id

Example

ExampleClient_MinerByID example using MinerByID()

client, err := NewClient(nil, nil, nil)
if err != nil {
	fmt.Printf("error occurred: %s", err.Error())
	return
}

// Add a miner
if err = client.AddMiner(Miner{Name: testMinerName, MinerID: testMinerID, URL: testMinerURL}); err != nil {
	fmt.Printf("error occurred: %s", err.Error())
	return
}

// Get miner by id
fmt.Printf("created new miner named: %s", client.MinerByID(testMinerID).Name)
Output:

created new miner named: TestMiner

func (*Client) MinerByName

func (c *Client) MinerByName(name string) *Miner

MinerByName will return a miner given a name

Example

ExampleClient_MinerByName example using MinerByName()

client, err := NewClient(nil, nil, nil)
if err != nil {
	fmt.Printf("error occurred: %s", err.Error())
	return
}

// Add a miner
if err = client.AddMiner(Miner{Name: testMinerName, URL: testMinerURL}); err != nil {
	fmt.Printf("error occurred: %s", err.Error())
	return
}

// Get miner by name
fmt.Printf("created new miner named: %s", client.MinerByName(testMinerName).Name)
Output:

created new miner named: TestMiner

func (*Client) MinerUpdateToken

func (c *Client) MinerUpdateToken(name, token string)

MinerUpdateToken will find a miner by name and update the token

Example

ExampleClient_MinerUpdateToken example using MinerUpdateToken()

client, err := NewClient(nil, nil, nil)
if err != nil {
	fmt.Printf("error occurred: %s", err.Error())
	return
}

// Update existing miner token
client.MinerUpdateToken(MinerTaal, "9999")

// Get miner by id
fmt.Printf("miner token found: %s", client.MinerByName(MinerTaal).Token)
Output:

miner token found: 9999

func (*Client) QueryTransaction

func (c *Client) QueryTransaction(ctx context.Context, miner *Miner, txID string) (*QueryTransactionResponse, error)

QueryTransaction will fire a Merchant API request to check the status of a transaction

This endpoint is used to check the current status of a previously submitted transaction. It returns a JSONEnvelope with a payload that contains the transaction status. The purpose of the envelope is to ensure strict consistency in the message content for the purpose of signing responses.

Specs: https://github.com/bitcoin-sv-specs/brfc-merchantapi/tree/v1.2-beta#Query-transaction-status

Example

ExampleClient_QueryTransaction example using QueryTransaction()

// Create a client (using a test client vs NewClient())
client := newTestClient(&mockHTTPValidQuery{})

// Create a req
response, err := client.QueryTransaction(context.Background(), client.MinerByName(MinerTaal), testTx)
if err != nil {
	fmt.Printf("error occurred: %s", err.Error())
	return
}

fmt.Printf("got tx status %s from: %s", response.Query.ReturnResult, response.Miner.Name)
Output:

got tx status success from: Taal

func (*Client) RemoveMiner added in v0.2.0

func (c *Client) RemoveMiner(miner *Miner) bool

RemoveMiner will remove a miner from the list

Example

ExampleClient_MinerUpdateToken example using RemoveMiner()

client, err := NewClient(nil, nil, nil)
if err != nil {
	fmt.Printf("error occurred: %s", err.Error())
	return
}

// Update existing miner token
client.RemoveMiner(client.MinerByName(MinerTaal))

// Show response
fmt.Printf("total miners: %d", len(client.Miners))
Output:

total miners: 2

func (*Client) SubmitTransaction

func (c *Client) SubmitTransaction(ctx context.Context, miner *Miner, tx *Transaction) (*SubmitTransactionResponse, error)

SubmitTransaction will fire a Merchant API request to submit a given transaction

This endpoint is used to send a raw transaction to a miner for inclusion in the next block that the miner creates. It returns a JSONEnvelope with a payload that contains the response to the transaction submission. The purpose of the envelope is to ensure strict consistency in the message content for the purpose of signing responses.

Specs: https://github.com/bitcoin-sv-specs/brfc-merchantapi/tree/v1.2-beta#Submit-transaction

Example

ExampleClient_SubmitTransaction example using SubmitTransaction()

// Create a client (using a test client vs NewClient())
client := newTestClient(&mockHTTPValidSubmission{})

tx := &Transaction{RawTx: submitTestExampleTx}

// Create a req
response, err := client.SubmitTransaction(context.Background(), client.MinerByName(MinerTaal), tx)
if err != nil {
	fmt.Printf("error occurred: %s", err.Error())
	return
}

fmt.Printf("submitted tx to: %s", response.Miner.Name)
Output:

submitted tx to: Taal

type ClientOptions

type ClientOptions struct {
	BackOffExponentFactor          float64       `json:"back_off_exponent_factor"`
	BackOffInitialTimeout          time.Duration `json:"back_off_initial_timeout"`
	BackOffMaximumJitterInterval   time.Duration `json:"back_off_maximum_jitter_interval"`
	BackOffMaxTimeout              time.Duration `json:"back_off_max_timeout"`
	DialerKeepAlive                time.Duration `json:"dialer_keep_alive"`
	DialerTimeout                  time.Duration `json:"dialer_timeout"`
	RequestRetryCount              int           `json:"request_retry_count"`
	RequestTimeout                 time.Duration `json:"request_timeout"`
	TransportExpectContinueTimeout time.Duration `json:"transport_expect_continue_timeout"`
	TransportIdleTimeout           time.Duration `json:"transport_idle_timeout"`
	TransportMaxIdleConnections    int           `json:"transport_max_idle_connections"`
	TransportTLSHandshakeTimeout   time.Duration `json:"transport_tls_handshake_timeout"`
	UserAgent                      string        `json:"user_agent"`
}

ClientOptions holds all the configuration for connection, dialer and transport

func DefaultClientOptions

func DefaultClientOptions() (clientOptions *ClientOptions)

DefaultClientOptions will return a ClientOptions struct with the default settings. Useful for starting with the default and then modifying as needed

Example

ExampleDefaultClientOptions example using DefaultClientOptions()

options := DefaultClientOptions()
options.UserAgent = "Custom UserAgent v1.0"
client, err := NewClient(options, nil, nil)
if err != nil {
	fmt.Printf("error occurred: %s", err.Error())
	return
}

fmt.Printf("created new client with user agent: %s", client.Options.UserAgent)
Output:

created new client with user agent: Custom UserAgent v1.0

type ConflictedWith added in v0.2.6

type ConflictedWith struct {
	TxID string `json:"txid"`
	Size int    `json:"size"`
	Hex  string `json:"hex"`
}

ConflictedWith contains the information about the transactions that conflict with the transaction submitted to mAPI. A conflict could arise if multiple transactions attempt to spend the same UTXO (double spend).

type FeePayload

type FeePayload struct {
	APIVersion                string      `json:"apiVersion"`
	Timestamp                 string      `json:"timestamp"`
	ExpirationTime            string      `json:"expiryTime"`
	MinerID                   string      `json:"minerId"`
	CurrentHighestBlockHash   string      `json:"currentHighestBlockHash"`
	CurrentHighestBlockHeight uint64      `json:"currentHighestBlockHeight"`
	MinerReputation           interface{} `json:"minerReputation"` // Not sure what this value is
	Fees                      []*bt.Fee   `json:"fees"`
}

FeePayload is the unmarshalled version of the payload envelope

func (*FeePayload) CalculateFee

func (f *FeePayload) CalculateFee(feeCategory, feeType string, txBytes uint64) (uint64, error)

CalculateFee will return the fee for the given txBytes Type: "FeeTypeData" or "FeeTypeStandard" Category: "FeeCategoryMining" or "FeeCategoryRelay"

If no fee is found or fee is 0, returns 1 & error

Spec: https://github.com/bitcoin-sv-specs/brfc-misc/tree/master/feespec#deterministic-transaction-fee-calculation-dtfc

Example

ExampleFeePayload_CalculateFee example using CalculateFee()

// Create a client (using a test client vs NewClient())
client := newTestClient(&mockHTTPValidBestQuote{})

// Create a req
response, err := client.BestQuote(context.Background(), FeeCategoryMining, FeeTypeData)
if err != nil {
	fmt.Printf("error occurred: %s", err.Error())
	return
}

// Calculate fee for tx
var fee uint64
fee, err = response.Quote.CalculateFee(FeeCategoryMining, FeeTypeData, 1000)
if err != nil {
	fmt.Printf("error occurred: %s", err.Error())
	return
}

// Note: cannot show response since the miner might be different each time
fmt.Printf("got best quote and fee for 1000 byte tx is: %d", fee)
Output:

got best quote and fee for 1000 byte tx is: 420

func (*FeePayload) GetFee added in v0.2.9

func (f *FeePayload) GetFee(feeType string) *bt.Fee

GetFee will return the fee associated to the type (standard, data)

Example

ExampleFeePayload_GetFee example using GetFee()

// Create a client (using a test client vs NewClient())
client := newTestClient(&mockHTTPValidBestQuote{})

// Create a req
response, err := client.BestQuote(context.Background(), FeeCategoryMining, FeeTypeData)
if err != nil {
	fmt.Printf("error occurred: %s", err.Error())
	return
}

// Get the fee
fee := response.Quote.GetFee(FeeTypeStandard)

fmt.Printf(
	"got best quote and fee for %d byte tx is %d sats",
	fee.MiningFee.Bytes, fee.MiningFee.Satoshis,
)
Output:

got best quote and fee for 1000 byte tx is 500 sats

type FeeQuoteResponse

type FeeQuoteResponse struct {
	JSONEnvelope
	Quote *FeePayload `json:"quote"` // Custom field for unmarshalled payload data
}

FeeQuoteResponse is the raw response from the Merchant API request

Specs: https://github.com/bitcoin-sv-specs/brfc-merchantapi/tree/v1.2-beta#get-fee-quote

type JSONEnvelope

type JSONEnvelope struct {
	Miner     *Miner `json:"miner"`     // Custom field for our internal Miner configuration
	Validated bool   `json:"validated"` // Custom field if the signature has been validated
	envelope.JSONEnvelope
}

JSONEnvelope is a standard response from the Merchant API requests

This type wraps the go-bk JSONEnvelope which performs validation of the signatures (if we have any) and will return true / false if valid.

We wrap this, so we can append some additional miner info and a validated helper property to indicate if the envelope is or isn't valid. Consumers can also independently validate the envelope.

type Miner

type Miner struct {
	MinerID string `json:"miner_id,omitempty"`
	Name    string `json:"name,omitempty"`
	Token   string `json:"token,omitempty"`
	URL     string `json:"url"`
}

Miner is a configuration per miner, including connection url, auth token, etc

type QueryPayload

type QueryPayload struct {
	APIVersion            string `json:"apiVersion"`
	Timestamp             string `json:"timestamp"`
	TxID                  string `json:"txid"`
	ReturnResult          string `json:"returnResult"`
	ResultDescription     string `json:"resultDescription"`
	BlockHash             string `json:"blockHash"`
	BlockHeight           int64  `json:"blockHeight"`
	MinerID               string `json:"minerId"`
	Confirmations         int64  `json:"confirmations"`
	TxSecondMempoolExpiry int64  `json:"txSecondMempoolExpiry"`
}

QueryPayload is the unmarshalled version of the payload envelope

type QueryTransactionResponse

type QueryTransactionResponse struct {
	JSONEnvelope
	Query *QueryPayload `json:"query"` // Custom field for unmarshalled payload data
}

QueryTransactionResponse is the raw response from the Merchant API request

Specs: https://github.com/bitcoin-sv-specs/brfc-merchantapi/tree/v1.2-beta#Query-transaction-status

type RequestResponse

type RequestResponse struct {
	BodyContents []byte `json:"body_contents"` // Raw body response
	Error        error  `json:"error"`         // If an error occurs
	Method       string `json:"method"`        // Method is the HTTP method used
	PostData     string `json:"post_data"`     // PostData is the post data submitted if POST/PUT request
	StatusCode   int    `json:"status_code"`   // StatusCode is the last code from the request
	URL          string `json:"url"`           // URL is used for the request
}

RequestResponse is the response from a request

type SubmissionPayload

type SubmissionPayload struct {
	APIVersion                string            `json:"apiVersion"`
	Timestamp                 string            `json:"timestamp"`
	TxID                      string            `json:"txid"`
	ReturnResult              string            `json:"returnResult"`
	ResultDescription         string            `json:"resultDescription"`
	MinerID                   string            `json:"minerId"`
	CurrentHighestBlockHash   string            `json:"currentHighestBlockHash"`
	ConflictedWith            []*ConflictedWith `json:"conflictedWith"`
	CurrentHighestBlockHeight int64             `json:"currentHighestBlockHeight"`
	TxSecondMempoolExpiry     int64             `json:"txSecondMempoolExpiry"`
}

SubmissionPayload is the unmarshalled version of the payload envelope

type SubmitTransactionResponse

type SubmitTransactionResponse struct {
	JSONEnvelope
	Results *SubmissionPayload `json:"results"` // Custom field for unmarshalled payload data
}

SubmitTransactionResponse is the raw response from the Merchant API request

Specs: https://github.com/bitcoin-sv-specs/brfc-merchantapi/tree/v1.2-beta#Submit-transaction

type Transaction

type Transaction struct {
	RawTx              string `json:"rawtx"`
	CallBackURL        string `json:"callBackUrl,omitempty"`
	CallBackToken      string `json:"callBackToken,omitempty"`
	MerkleFormat       string `json:"merkleFormat,omitempty"`
	CallBackEncryption string `json:"callBackEncryption,omitempty"`
	MerkleProof        bool   `json:"merkleProof,omitempty"`
	DsCheck            bool   `json:"dsCheck,omitempty"`
}

Transaction is the body contents in the "submit transaction" request

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL