From fd495e464250873113535fb694a7ca27c7933fc9 Mon Sep 17 00:00:00 2001 From: vitalibalashka Date: Mon, 24 Jul 2023 00:44:06 +0200 Subject: [PATCH] feat(BUX-51): go-minercraft v2 --- chainstate/broadcast.go | 2 +- chainstate/broadcast_providers.go | 2 +- chainstate/broadcast_test.go | 2 +- chainstate/client.go | 86 ++++++----- chainstate/client_internal.go | 22 +-- chainstate/client_options.go | 85 ++++------ chainstate/client_options_test.go | 16 +- chainstate/client_test.go | 4 +- chainstate/interface.go | 2 +- chainstate/mock_minercraft.go | 145 ++++++++++++++---- chainstate/transaction.go | 24 +-- client_options.go | 28 +++- .../broadcast_miners/broadcast_miners.go | 17 +- examples/client/custom_rates/custom_rates.go | 17 +- go.mod | 2 +- go.sum | 4 +- mock_chainstate_test.go | 2 +- 17 files changed, 287 insertions(+), 173 deletions(-) diff --git a/chainstate/broadcast.go b/chainstate/broadcast.go index dcf71e86..5bc8ae69 100644 --- a/chainstate/broadcast.go +++ b/chainstate/broadcast.go @@ -91,7 +91,7 @@ func createActiveProviders(c *Client, txID, txHex string) []txBroadcastProvider providers := make([]txBroadcastProvider, 0, 10) if shouldBroadcastWithMAPI(c) { - for _, miner := range c.options.config.mAPI.broadcastMiners { + for _, miner := range c.options.config.minercraftConfig.broadcastMiners { if miner == nil { continue } diff --git a/chainstate/broadcast_providers.go b/chainstate/broadcast_providers.go index 3a853f48..0a5bf097 100644 --- a/chainstate/broadcast_providers.go +++ b/chainstate/broadcast_providers.go @@ -7,7 +7,7 @@ import ( "strings" "github.com/mrz1836/go-nownodes" - "github.com/tonicpow/go-minercraft" + "github.com/tonicpow/go-minercraft/v2" ) // generic broadcast provider diff --git a/chainstate/broadcast_test.go b/chainstate/broadcast_test.go index e7288686..0fee581c 100644 --- a/chainstate/broadcast_test.go +++ b/chainstate/broadcast_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tonicpow/go-minercraft" + "github.com/tonicpow/go-minercraft/v2" ) func Test_doesErrorContain(t *testing.T) { diff --git a/chainstate/client.go b/chainstate/client.go index 755a395a..5f21f8c8 100644 --- a/chainstate/client.go +++ b/chainstate/client.go @@ -7,11 +7,13 @@ import ( "time" "github.com/BuxOrg/bux/utils" + "github.com/libsv/go-bt/v2" zLogger "github.com/mrz1836/go-logger" "github.com/mrz1836/go-nownodes" "github.com/mrz1836/go-whatsonchain" "github.com/newrelic/go-agent/v3/newrelic" - "github.com/tonicpow/go-minercraft" + "github.com/tonicpow/go-minercraft/v2" + "github.com/tonicpow/go-minercraft/v2/apis/mapi" ) type ( @@ -35,7 +37,7 @@ type ( syncConfig struct { excludedProviders []string // List of provider names httpClient HTTPInterface // Custom HTTP client (Minercraft, WOC) - mAPI *mAPIConfig // mAPI configuration + minercraftConfig *minercraftConfig // minercraftConfig configuration minercraft minercraft.ClientInterface // Minercraft client network Network // Current network (mainnet, testnet, stn) nowNodes nownodes.ClientInterface // NOWNodes client @@ -45,12 +47,14 @@ type ( whatsOnChainAPIKey string // If set, use this key } - // mAPIConfig is specific for mAPI configuration - mAPIConfig struct { - broadcastMiners []*Miner // List of loaded miners for broadcasting - queryMiners []*Miner // List of loaded miners for querying transactions - feeUnit *utils.FeeUnit // The lowest fees among all miners - mapiFeeQuotesEnabled bool // If set, feeUnit will be updated with fee quotes from miner's mAPI + // minercraftConfig is specific for minercraft configuration + minercraftConfig struct { + broadcastMiners []*Miner // List of loaded miners for broadcasting + queryMiners []*Miner // List of loaded miners for querying transactions + feeUnit *utils.FeeUnit // The lowest fees among all miners + minercraftFeeQuotes bool // If set, feeUnit will be updated with fee quotes from miner's mAPI + apiType minercraft.APIType // MinerCraft APIType(ARC/mAPI) + minerAPIs []*minercraft.MinerAPIs // List of miners APIs } // Miner is the internal chainstate miner (wraps Minercraft miner with more information) @@ -187,21 +191,21 @@ func (c *Client) QueryTimeout() time.Duration { // BroadcastMiners will return the broadcast miners func (c *Client) BroadcastMiners() []*Miner { - return c.options.config.mAPI.broadcastMiners + return c.options.config.minercraftConfig.broadcastMiners } // QueryMiners will return the query miners func (c *Client) QueryMiners() []*Miner { - return c.options.config.mAPI.queryMiners + return c.options.config.minercraftConfig.queryMiners } // FeeUnit will return feeUnit func (c *Client) FeeUnit() *utils.FeeUnit { - return c.options.config.mAPI.feeUnit + return c.options.config.minercraftConfig.feeUnit } -func (c *Client) isMapiFeeQuotesEnabled() bool { - return c.options.config.mAPI.mapiFeeQuotesEnabled +func (c *Client) isMinercraftFeeQuotesEnabled() bool { + return c.options.config.minercraftConfig.minercraftFeeQuotes } // ValidateMiners will check if miner is reacheble by requesting its FeeQuote @@ -214,7 +218,7 @@ func (c *Client) ValidateMiners(ctx context.Context) { var wg sync.WaitGroup // Loop all broadcast miners - for index := range c.options.config.mAPI.broadcastMiners { + for index := range c.options.config.minercraftConfig.broadcastMiners { wg.Add(1) go func( ctx context.Context, client *Client, @@ -223,33 +227,45 @@ func (c *Client) ValidateMiners(ctx context.Context) { defer wg.Done() // Get the fee quote using the miner // Switched from policyQuote to feeQuote as gorillapool doesn't have such endpoint - quote, err := c.Minercraft().FeeQuote(ctx, miner.Miner) - if err != nil { - client.options.logger.Error(ctx, fmt.Sprintf("No FeeQuote response from miner %s", miner.Miner.Name)) - miner.FeeUnit = nil - return - } + var fee *bt.Fee + if c.Minercraft().APIType() == minercraft.MAPI { + quote, err := c.Minercraft().FeeQuote(ctx, miner.Miner) + if err != nil { + client.options.logger.Error(ctx, fmt.Sprintf("No FeeQuote response from miner %s", miner.Miner.Name)) + miner.FeeUnit = nil + return + } + + fee = quote.Quote.GetFee(mapi.FeeTypeData) + if fee == nil { + client.options.logger.Error(ctx, fmt.Sprintf("Fee is missing in %s's FeeQuote response", miner.Miner.Name)) + return + } + // Arc doesn't support FeeQuote right now(2023.07.21), that's why PolicyQuote is used + } else if c.Minercraft().APIType() == minercraft.Arc { + quote, err := c.Minercraft().PolicyQuote(ctx, miner.Miner) + if err != nil { + client.options.logger.Error(ctx, fmt.Sprintf("No FeeQuote response from miner %s", miner.Miner.Name)) + miner.FeeUnit = nil + return + } - // Get the fee and set the fee - fee := quote.Quote.GetFee(minercraft.FeeTypeData) - if fee == nil { - client.options.logger.Error(ctx, fmt.Sprintf("Fee is missing in %s's FeeQuote response", miner.Miner.Name)) - return + fee = quote.Quote.Fees[0] } - if c.isMapiFeeQuotesEnabled() { + if c.isMinercraftFeeQuotesEnabled() { miner.FeeUnit = &utils.FeeUnit{ Satoshis: fee.MiningFee.Satoshis, Bytes: fee.MiningFee.Bytes, } miner.FeeLastChecked = time.Now().UTC() } - }(ctxWithCancel, c, &wg, c.options.config.mAPI.broadcastMiners[index]) + }(ctxWithCancel, c, &wg, c.options.config.minercraftConfig.broadcastMiners[index]) } wg.Wait() c.DeleteUnreacheableMiners() - if c.isMapiFeeQuotesEnabled() { + if c.isMinercraftFeeQuotesEnabled() { c.SetLowestFees() } } @@ -257,26 +273,26 @@ func (c *Client) ValidateMiners(ctx context.Context) { // SetLowestFees takes the lowest fees among all miners and sets them as the feeUnit for future transactions func (c *Client) SetLowestFees() { minFees := DefaultFee - for _, m := range c.options.config.mAPI.broadcastMiners { + for _, m := range c.options.config.minercraftConfig.broadcastMiners { if float64(minFees.Satoshis)/float64(minFees.Bytes) > float64(m.FeeUnit.Satoshis)/float64(m.FeeUnit.Bytes) { minFees = m.FeeUnit } } - c.options.config.mAPI.feeUnit = minFees + c.options.config.minercraftConfig.feeUnit = minFees } // DeleteUnreacheableMiners deletes miners which can't be reacheable from config func (c *Client) DeleteUnreacheableMiners() { validMinerIndex := 0 - for _, miner := range c.options.config.mAPI.broadcastMiners { + for _, miner := range c.options.config.minercraftConfig.broadcastMiners { if miner.FeeUnit != nil { - c.options.config.mAPI.broadcastMiners[validMinerIndex] = miner + c.options.config.minercraftConfig.broadcastMiners[validMinerIndex] = miner validMinerIndex++ } } // Prevent memory leak by erasing truncated miners - for i := validMinerIndex; i < len(c.options.config.mAPI.broadcastMiners); i++ { - c.options.config.mAPI.broadcastMiners[i] = nil + for i := validMinerIndex; i < len(c.options.config.minercraftConfig.broadcastMiners); i++ { + c.options.config.minercraftConfig.broadcastMiners[i] = nil } - c.options.config.mAPI.broadcastMiners = c.options.config.mAPI.broadcastMiners[:validMinerIndex] + c.options.config.minercraftConfig.broadcastMiners = c.options.config.minercraftConfig.broadcastMiners[:validMinerIndex] } diff --git a/chainstate/client_internal.go b/chainstate/client_internal.go index fe38ae06..c40fb5aa 100644 --- a/chainstate/client_internal.go +++ b/chainstate/client_internal.go @@ -7,7 +7,7 @@ import ( "github.com/mrz1836/go-nownodes" "github.com/mrz1836/go-whatsonchain" "github.com/newrelic/go-agent/v3/newrelic" - "github.com/tonicpow/go-minercraft" + "github.com/tonicpow/go-minercraft/v2" ) // defaultMinercraftOptions will create the defaults @@ -47,24 +47,26 @@ func (c *Client) startMinerCraft(ctx context.Context) (err error) { var loadedMiners []string // Loop all broadcast miners and append to the list of miners - for i := range c.options.config.mAPI.broadcastMiners { - if !utils.StringInSlice(c.options.config.mAPI.broadcastMiners[i].Miner.MinerID, loadedMiners) { - optionalMiners = append(optionalMiners, c.options.config.mAPI.broadcastMiners[i].Miner) - loadedMiners = append(loadedMiners, c.options.config.mAPI.broadcastMiners[i].Miner.MinerID) + for i := range c.options.config.minercraftConfig.broadcastMiners { + if !utils.StringInSlice(c.options.config.minercraftConfig.broadcastMiners[i].Miner.MinerID, loadedMiners) { + optionalMiners = append(optionalMiners, c.options.config.minercraftConfig.broadcastMiners[i].Miner) + loadedMiners = append(loadedMiners, c.options.config.minercraftConfig.broadcastMiners[i].Miner.MinerID) } } // Loop all query miners and append to the list of miners - for i := range c.options.config.mAPI.queryMiners { - if !utils.StringInSlice(c.options.config.mAPI.queryMiners[i].Miner.MinerID, loadedMiners) { - optionalMiners = append(optionalMiners, c.options.config.mAPI.queryMiners[i].Miner) - loadedMiners = append(loadedMiners, c.options.config.mAPI.queryMiners[i].Miner.MinerID) + for i := range c.options.config.minercraftConfig.queryMiners { + if !utils.StringInSlice(c.options.config.minercraftConfig.queryMiners[i].Miner.MinerID, loadedMiners) { + optionalMiners = append(optionalMiners, c.options.config.minercraftConfig.queryMiners[i].Miner) + loadedMiners = append(loadedMiners, c.options.config.minercraftConfig.queryMiners[i].Miner.MinerID) } } c.options.config.minercraft, err = minercraft.NewClient( c.defaultMinercraftOptions(), c.HTTPClient(), - optionalMiners, // If empty, it will use the default miners from Minercraft + c.options.config.minercraftConfig.apiType, + optionalMiners, + c.options.config.minercraftConfig.minerAPIs, ) } diff --git a/chainstate/client_options.go b/chainstate/client_options.go index 9089f29c..c0b8dcab 100644 --- a/chainstate/client_options.go +++ b/chainstate/client_options.go @@ -2,15 +2,13 @@ package chainstate import ( "context" - "reflect" - "strings" "time" zLogger "github.com/mrz1836/go-logger" "github.com/mrz1836/go-nownodes" "github.com/mrz1836/go-whatsonchain" "github.com/newrelic/go-agent/v3/newrelic" - "github.com/tonicpow/go-minercraft" + "github.com/tonicpow/go-minercraft/v2" ) // ClientOps allow functional options to be supplied @@ -24,15 +22,18 @@ func defaultClientOptions() *clientOptions { // Create the default miners bm, qm := defaultMiners() + apis, _ := minercraft.DefaultMinersAPIs() // Set the default options return &clientOptions{ config: &syncConfig{ httpClient: nil, - mAPI: &mAPIConfig{ - broadcastMiners: bm, - queryMiners: qm, - feeUnit: DefaultFee, + minercraftConfig: &minercraftConfig{ + broadcastMiners: bm, + queryMiners: qm, + minerAPIs: apis, + minercraftFeeQuotes: true, + feeUnit: DefaultFee, }, minercraft: nil, network: MainNet, @@ -46,7 +47,6 @@ func defaultClientOptions() *clientOptions { // defaultMiners will return the miners for default configuration func defaultMiners() (broadcastMiners []*Miner, queryMiners []*Miner) { - // Set the broadcast miners miners, _ := minercraft.DefaultMiners() @@ -115,6 +115,20 @@ func WithMinercraft(client minercraft.ClientInterface) ClientOps { } } +// WithMAPI will specify mAPI as an API for minercraft client +func WithMAPI() ClientOps { + return func(c *clientOptions) { + c.config.minercraftConfig.apiType = minercraft.MAPI + } +} + +// WithArc will specify Arc as an API for minercraft client +func WithArc() ClientOps { + return func(c *clientOptions) { + c.config.minercraftConfig.apiType = minercraft.Arc + } +} + // WithWhatsOnChain will set a custom WhatsOnChain client func WithWhatsOnChain(client whatsonchain.ClientInterface) ClientOps { return func(c *clientOptions) { @@ -155,7 +169,7 @@ func WithWhatsOnChainAPIKey(apiKey string) ClientOps { func WithBroadcastMiners(miners []*Miner) ClientOps { return func(c *clientOptions) { if len(miners) > 0 { - c.config.mAPI.broadcastMiners = miners + c.config.minercraftConfig.broadcastMiners = miners } } } @@ -164,7 +178,7 @@ func WithBroadcastMiners(miners []*Miner) ClientOps { func WithQueryMiners(miners []*Miner) ClientOps { return func(c *clientOptions) { if len(miners) > 0 { - c.config.mAPI.queryMiners = miners + c.config.minercraftConfig.queryMiners = miners } } } @@ -233,55 +247,16 @@ func WithExcludedProviders(providers []string) ClientOps { } } -// WithMapiFeeQuotes will set mapiFeeQuotesEnabled flag as true -func WithMapiFeeQuotes() ClientOps { +// WithMinercraftFeeQuotes will set minercraftFeeQuotes flag as true +func WithMinercraftFeeQuotes() ClientOps { return func(c *clientOptions) { - c.config.mAPI.mapiFeeQuotesEnabled = true + c.config.minercraftConfig.minercraftFeeQuotes = true } } -// WithOverridenMAPIConfig will override default config -func WithOverridenMAPIConfig(miners []*minercraft.Miner) ClientOps { +// WithMinercraftAPIs will set miners APIs +func WithMinercraftAPIs(apis []*minercraft.MinerAPIs) ClientOps { return func(c *clientOptions) { - overrideMAPIConfig(c.config.mAPI.broadcastMiners, miners) - overrideMAPIConfig(c.config.mAPI.queryMiners, miners) - } -} - -// Looks for miners by name over the mAPI config, and rewrites fields presented in a custom config -func overrideMAPIConfig(configToOverride []*Miner, customConfig []*minercraft.Miner) { - for _, miner := range customConfig { - var minerToOverride *minercraft.Miner - for _, m := range configToOverride { - if strings.EqualFold(m.Miner.Name, miner.Name) { - minerToOverride = m.Miner - break - } - } - // The miner is not in the configuration, and therefore there is nothing to override. Skip - if minerToOverride == nil { - continue - } - // Reflect values of miners. Needed to loop over all miner's fields and overwrite only some of them - // Miners are pointers in both configs, so we use reflect.ValueOf(miner).Elem() - minerToOverrideReflect := reflect.ValueOf(minerToOverride).Elem() - overrideReflect := reflect.ValueOf(miner).Elem() - // We don't override 'Name' field. Should be skipped - fieldToIgnore := overrideReflect.FieldByName("Name") - - for i := 0; i < overrideReflect.NumField(); i++ { - newField := overrideReflect.Field(i) - if newField == fieldToIgnore { - continue - } - // Only non-zero fields from custom config will used as overwrite fields - if !newField.IsZero() { - name := overrideReflect.Type().Field(i).Name - fieldToOverride := minerToOverrideReflect.FieldByName(name) - if fieldToOverride.CanSet() { - fieldToOverride.Set(newField) - } - } - } + c.config.minercraftConfig.minerAPIs = apis } } diff --git a/chainstate/client_options_test.go b/chainstate/client_options_test.go index 6bb562a6..1bbb050c 100644 --- a/chainstate/client_options_test.go +++ b/chainstate/client_options_test.go @@ -236,21 +236,21 @@ func TestWithBroadcastMiners(t *testing.T) { t.Run("test applying nil", func(t *testing.T) { options := &clientOptions{ - config: &syncConfig{mAPI: &mAPIConfig{}}, + config: &syncConfig{minercraftConfig: &minercraftConfig{}}, } opt := WithBroadcastMiners(nil) opt(options) - assert.Nil(t, options.config.mAPI.broadcastMiners) + assert.Nil(t, options.config.minercraftConfig.broadcastMiners) }) t.Run("test applying option", func(t *testing.T) { options := &clientOptions{ - config: &syncConfig{mAPI: &mAPIConfig{}}, + config: &syncConfig{minercraftConfig: &minercraftConfig{}}, } miners := []*Miner{{Miner: minerTaal}} opt := WithBroadcastMiners(miners) opt(options) - assert.Equal(t, miners, options.config.mAPI.broadcastMiners) + assert.Equal(t, miners, options.config.minercraftConfig.broadcastMiners) }) } @@ -265,21 +265,21 @@ func TestWithQueryMiners(t *testing.T) { t.Run("test applying nil", func(t *testing.T) { options := &clientOptions{ - config: &syncConfig{mAPI: &mAPIConfig{}}, + config: &syncConfig{minercraftConfig: &minercraftConfig{}}, } opt := WithQueryMiners(nil) opt(options) - assert.Nil(t, options.config.mAPI.queryMiners) + assert.Nil(t, options.config.minercraftConfig.queryMiners) }) t.Run("test applying option", func(t *testing.T) { options := &clientOptions{ - config: &syncConfig{mAPI: &mAPIConfig{}}, + config: &syncConfig{minercraftConfig: &minercraftConfig{}}, } miners := []*Miner{{Miner: minerTaal}} opt := WithQueryMiners(miners) opt(options) - assert.Equal(t, miners, options.config.mAPI.queryMiners) + assert.Equal(t, miners, options.config.minercraftConfig.queryMiners) }) } diff --git a/chainstate/client_test.go b/chainstate/client_test.go index 3ddd76c8..2b8e261c 100644 --- a/chainstate/client_test.go +++ b/chainstate/client_test.go @@ -9,7 +9,7 @@ import ( "github.com/mrz1836/go-whatsonchain" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tonicpow/go-minercraft" + "github.com/tonicpow/go-minercraft/v2" ) // TestNewClient will test the method NewClient() @@ -72,7 +72,7 @@ func TestNewClient(t *testing.T) { t.Run("custom minercraft client", func(t *testing.T) { customClient, err := minercraft.NewClient( - minercraft.DefaultClientOptions(), nil, nil, + minercraft.DefaultClientOptions(), nil, "", nil, nil, ) require.NoError(t, err) require.NotNil(t, customClient) diff --git a/chainstate/interface.go b/chainstate/interface.go index eca7992d..d4a7de82 100644 --- a/chainstate/interface.go +++ b/chainstate/interface.go @@ -10,7 +10,7 @@ import ( "github.com/libsv/go-bc" "github.com/mrz1836/go-nownodes" "github.com/mrz1836/go-whatsonchain" - "github.com/tonicpow/go-minercraft" + "github.com/tonicpow/go-minercraft/v2" ) // HTTPInterface is the HTTP client interface diff --git a/chainstate/mock_minercraft.go b/chainstate/mock_minercraft.go index 20250d75..69c92f53 100644 --- a/chainstate/mock_minercraft.go +++ b/chainstate/mock_minercraft.go @@ -9,33 +9,29 @@ import ( "github.com/libsv/go-bk/envelope" "github.com/libsv/go-bt/v2" - "github.com/tonicpow/go-minercraft" + "github.com/tonicpow/go-minercraft/v2" + "github.com/tonicpow/go-minercraft/v2/apis/mapi" ) var ( minerTaal = &minercraft.Miner{ MinerID: "030d1fe5c1b560efe196ba40540ce9017c20daa9504c4c4cec6184fc702d9f274e", Name: "Taal", - URL: "https://merchantapi.taal.com", } minerMempool = &minercraft.Miner{ MinerID: "03e92d3e5c3f7bd945dfbf48e7a99393b1bfb3f11f380ae30d286e7ff2aec5a270", Name: "Mempool", - URL: "https://www.ddpurse.com/openapi", - Token: "561b756d12572020ea9a104c3441b71790acbbce95a6ddbf7e0630971af9424b", } minerMatterPool = &minercraft.Miner{ MinerID: "0253a9b2d017254b91704ba52aad0df5ca32b4fb5cb6b267ada6aefa2bc5833a93", Name: "Matterpool", - URL: "https://merchantapi.matterpool.io", } minerGorillaPool = &minercraft.Miner{ MinerID: "03ad780153c47df915b3d2e23af727c68facaca4facd5f155bf5018b979b9aeb83", Name: "GorillaPool", - URL: "https://merchantapi.gorillapool.io", } allMiners = []*minercraft.Miner{ @@ -44,19 +40,74 @@ var ( minerGorillaPool, minerMatterPool, } + + minerAPIs = []*minercraft.MinerAPIs{ + { + MinerID: "03e92d3e5c3f7bd945dfbf48e7a99393b1bfb3f11f380ae30d286e7ff2aec5a270", + APIs: []minercraft.API{ + { + URL: "https://merchantapi.taal.com", + Type: minercraft.MAPI, + }, + { + URL: "https://tapi.taal.com/arc", + Type: minercraft.Arc, + }, + }, + }, + { + MinerID: "03e92d3e5c3f7bd945dfbf48e7a99393b1bfb3f11f380ae30d286e7ff2aec5a270", + APIs: []minercraft.API{ + { + Token: "561b756d12572020ea9a104c3441b71790acbbce95a6ddbf7e0630971af9424b", + URL: "https://www.ddpurse.com/openapi", + Type: minercraft.MAPI, + }, + }, + }, + { + MinerID: "0253a9b2d017254b91704ba52aad0df5ca32b4fb5cb6b267ada6aefa2bc5833a93", + APIs: []minercraft.API{ + { + URL: "https://merchantapi.matterpool.io", + Type: minercraft.MAPI, + }, + }, + }, + { + MinerID: "03ad780153c47df915b3d2e23af727c68facaca4facd5f155bf5018b979b9aeb83", + APIs: []minercraft.API{ + { + URL: "https://merchantapi.gorillapool.io", + Type: minercraft.MAPI, + }, + { + URL: "https://arc.gorillapool.io", + Type: minercraft.Arc, + }, + }, + }, + } ) // MinerCraftBase is a mock implementation of the minercraft.MinerCraft interface. type MinerCraftBase struct{} // AddMiner adds a new miner to the list of miners. -func (m *MinerCraftBase) AddMiner(miner minercraft.Miner) error { +func (m *MinerCraftBase) AddMiner(miner minercraft.Miner, apis []minercraft.API) error { existingMiner := m.MinerByName(miner.Name) if existingMiner != nil { return fmt.Errorf("miner %s already exists", miner.Name) } // Append the new miner allMiners = append(allMiners, &miner) + + // Append the new miner APIs + minerAPIs = append(minerAPIs, &minercraft.MinerAPIs{ + MinerID: miner.MinerID, + APIs: apis, + }) + return nil } @@ -73,7 +124,7 @@ func (m *MinerCraftBase) FastestQuote(context.Context, time.Duration) (*minercra // FeeQuote returns a fee quote for the given miner. func (m *MinerCraftBase) FeeQuote(context.Context, *minercraft.Miner) (*minercraft.FeeQuoteResponse, error) { return &minercraft.FeeQuoteResponse{ - Quote: &minercraft.FeePayload{ + Quote: &mapi.FeePayload{ Fees: []*bt.Fee{ { FeeType: bt.FeeTypeData, @@ -110,9 +161,10 @@ func (m *MinerCraftBase) Miners() []*minercraft.Miner { } // MinerUpdateToken updates the token for the given miner. -func (m *MinerCraftBase) MinerUpdateToken(name, token string) { +func (m *MinerCraftBase) MinerUpdateToken(name, token string, apiType minercraft.APIType) { if miner := m.MinerByName(name); miner != nil { - miner.Token = token + api, _ := m.MinerAPIByMinerID(miner.MinerID, apiType) + api.Token = token } } @@ -128,8 +180,8 @@ func (m *MinerCraftBase) QueryTransaction(context.Context, *minercraft.Miner, st // RemoveMiner removes a miner from the list of miners. func (m *MinerCraftBase) RemoveMiner(miner *minercraft.Miner) bool { - for i, cm := range allMiners { - if cm.Name == miner.Name || cm.MinerID == miner.MinerID { + for i, miner := range allMiners { + if miner.Name == miner.Name || miner.MinerID == miner.MinerID { allMiners[i] = allMiners[len(allMiners)-1] allMiners = allMiners[:len(allMiners)-1] return true @@ -149,6 +201,35 @@ func (m *MinerCraftBase) SubmitTransactions(context.Context, *minercraft.Miner, return nil, nil } +// APIType will return the API type +func (m *MinerCraftBase) APIType() minercraft.APIType { + return minercraft.MAPI +} + +// MinerAPIByMinerID will return a miner's API given a miner id and API type +func (m *MinerCraftBase) MinerAPIByMinerID(minerID string, apiType minercraft.APIType) (*minercraft.API, error) { + for _, minerAPI := range minerAPIs { + if minerAPI.MinerID == minerID { + for i := range minerAPI.APIs { + if minerAPI.APIs[i].Type == apiType { + return &minerAPI.APIs[i], nil + } + } + } + } + return nil, &minercraft.APINotFoundError{MinerID: minerID, APIType: apiType} +} + +// MinerAPIsByMinerID will return a miner's APIs given a miner id +func (m *MinerCraftBase) MinerAPIsByMinerID(minerID string) *minercraft.MinerAPIs { + for _, minerAPIs := range minerAPIs { + if minerAPIs.MinerID == minerID { + return minerAPIs + } + } + return nil +} + // UserAgent returns the user agent. func (m *MinerCraftBase) UserAgent() string { return "default-user-agent" @@ -177,7 +258,7 @@ func (m *minerCraftTxOnChain) SubmitTransaction(_ context.Context, miner *minerc MimeType: applicationJSONType, }, }, - Results: &minercraft.SubmissionPayload{ + Results: &minercraft.UnifiedSubmissionPayload{ APIVersion: "1.4.0", CurrentHighestBlockHash: "00000000000000000652def5827ad3de6380376f8fc8d3e835503095a761e0d2", CurrentHighestBlockHeight: 724807, @@ -199,7 +280,7 @@ func (m *minerCraftTxOnChain) SubmitTransaction(_ context.Context, miner *minerc MimeType: applicationJSONType, }, }, - Results: &minercraft.SubmissionPayload{ + Results: &minercraft.UnifiedSubmissionPayload{ APIVersion: "", CurrentHighestBlockHash: "0000000000000000064c900b1fceb316302426aedb2242852530b5e78144f2c1", CurrentHighestBlockHeight: 724816, @@ -225,7 +306,7 @@ func (m *minerCraftTxOnChain) SubmitTransaction(_ context.Context, miner *minerc MimeType: applicationJSONType, }, }, - Results: &minercraft.SubmissionPayload{ + Results: &minercraft.UnifiedSubmissionPayload{ APIVersion: "1.1.0-1-g35ba2d3", CurrentHighestBlockHash: "0000000000000000064c900b1fceb316302426aedb2242852530b5e78144f2c1", CurrentHighestBlockHeight: 724816, @@ -251,7 +332,7 @@ func (m *minerCraftTxOnChain) SubmitTransaction(_ context.Context, miner *minerc MimeType: applicationJSONType, }, }, - Results: &minercraft.SubmissionPayload{ + Results: &minercraft.UnifiedSubmissionPayload{ APIVersion: "", CurrentHighestBlockHash: "0000000000000000064c900b1fceb316302426aedb2242852530b5e78144f2c1", CurrentHighestBlockHeight: 724816, @@ -286,7 +367,7 @@ func (m *minerCraftTxOnChain) QueryTransaction(_ context.Context, miner *minercr MimeType: applicationJSONType, }, }, - Query: &minercraft.QueryPayload{ + Query: &minercraft.QueryTxResponse{ APIVersion: "1.4.0", Timestamp: "2022-01-23T19:42:18.6860061Z", TxID: onChainExample1TxID, @@ -312,7 +393,7 @@ func (m *minerCraftTxOnChain) QueryTransaction(_ context.Context, miner *minercr MimeType: applicationJSONType, }, }, - Query: &minercraft.QueryPayload{ + Query: &minercraft.QueryTxResponse{ APIVersion: "", // NOTE: missing from mempool response Timestamp: "2022-01-23T19:51:10.046Z", TxID: onChainExample1TxID, @@ -353,7 +434,7 @@ func (m *minerCraftBroadcastSuccess) SubmitTransaction(_ context.Context, miner MimeType: applicationJSONType, }, }, - Results: &minercraft.SubmissionPayload{ + Results: &minercraft.UnifiedSubmissionPayload{ APIVersion: "1.4.0", CurrentHighestBlockHash: "000000000000000006e6745f6a57a1da8096faf9f71dd59b2bab3f2b0219b7a0", CurrentHighestBlockHeight: 724922, @@ -375,7 +456,7 @@ func (m *minerCraftBroadcastSuccess) SubmitTransaction(_ context.Context, miner MimeType: applicationJSONType, }, }, - Results: &minercraft.SubmissionPayload{ + Results: &minercraft.UnifiedSubmissionPayload{ APIVersion: "", CurrentHighestBlockHash: "000000000000000006e6745f6a57a1da8096faf9f71dd59b2bab3f2b0219b7a0", CurrentHighestBlockHeight: 724922, @@ -401,7 +482,7 @@ func (m *minerCraftBroadcastSuccess) SubmitTransaction(_ context.Context, miner MimeType: applicationJSONType, }, }, - Results: &minercraft.SubmissionPayload{ + Results: &minercraft.UnifiedSubmissionPayload{ APIVersion: "1.1.0-1-g35ba2d3", CurrentHighestBlockHash: "000000000000000006e6745f6a57a1da8096faf9f71dd59b2bab3f2b0219b7a0", CurrentHighestBlockHeight: 724922, @@ -427,7 +508,7 @@ func (m *minerCraftBroadcastSuccess) SubmitTransaction(_ context.Context, miner MimeType: applicationJSONType, }, }, - Results: &minercraft.SubmissionPayload{ + Results: &minercraft.UnifiedSubmissionPayload{ APIVersion: "", CurrentHighestBlockHash: "000000000000000006e6745f6a57a1da8096faf9f71dd59b2bab3f2b0219b7a0", CurrentHighestBlockHeight: 724922, @@ -466,7 +547,7 @@ func (m *minerCraftInMempool) SubmitTransaction(_ context.Context, miner *minerc MimeType: applicationJSONType, }, }, - Results: &minercraft.SubmissionPayload{ + Results: &minercraft.UnifiedSubmissionPayload{ APIVersion: "1.4.0", CurrentHighestBlockHash: "00000000000000000652def5827ad3de6380376f8fc8d3e835503095a761e0d2", CurrentHighestBlockHeight: 724807, @@ -488,7 +569,7 @@ func (m *minerCraftInMempool) SubmitTransaction(_ context.Context, miner *minerc MimeType: applicationJSONType, }, }, - Results: &minercraft.SubmissionPayload{ + Results: &minercraft.UnifiedSubmissionPayload{ APIVersion: "", CurrentHighestBlockHash: "0000000000000000064c900b1fceb316302426aedb2242852530b5e78144f2c1", CurrentHighestBlockHeight: 724816, @@ -514,7 +595,7 @@ func (m *minerCraftInMempool) SubmitTransaction(_ context.Context, miner *minerc MimeType: applicationJSONType, }, }, - Results: &minercraft.SubmissionPayload{ + Results: &minercraft.UnifiedSubmissionPayload{ APIVersion: "1.1.0-1-g35ba2d3", CurrentHighestBlockHash: "0000000000000000064c900b1fceb316302426aedb2242852530b5e78144f2c1", CurrentHighestBlockHeight: 724816, @@ -540,7 +621,7 @@ func (m *minerCraftInMempool) SubmitTransaction(_ context.Context, miner *minerc MimeType: applicationJSONType, }, }, - Results: &minercraft.SubmissionPayload{ + Results: &minercraft.UnifiedSubmissionPayload{ APIVersion: "", CurrentHighestBlockHash: "0000000000000000064c900b1fceb316302426aedb2242852530b5e78144f2c1", CurrentHighestBlockHeight: 724816, @@ -574,7 +655,7 @@ func (m *minerCraftTxNotFound) SubmitTransaction(_ context.Context, miner *miner MimeType: applicationJSONType, }, }, - Results: &minercraft.SubmissionPayload{ + Results: &minercraft.UnifiedSubmissionPayload{ APIVersion: "", CurrentHighestBlockHash: "0000000000000000064c900b1fceb316302426aedb2242852530b5e78144f2c1", CurrentHighestBlockHeight: 724816, @@ -606,7 +687,7 @@ func (m *minerCraftTxNotFound) QueryTransaction(_ context.Context, miner *minerc MimeType: applicationJSONType, }, }, - Query: &minercraft.QueryPayload{ + Query: &minercraft.QueryTxResponse{ APIVersion: "1.4.0", Timestamp: "2022-01-24T01:36:23.0767761Z", TxID: notFoundExample1TxID, @@ -626,7 +707,7 @@ func (m *minerCraftTxNotFound) QueryTransaction(_ context.Context, miner *minerc MimeType: applicationJSONType, }, }, - Query: &minercraft.QueryPayload{ + Query: &minercraft.QueryTxResponse{ APIVersion: "", // NOTE: missing from mempool response Timestamp: "2022-01-24T01:39:58.066Z", TxID: notFoundExample1TxID, @@ -650,7 +731,7 @@ func (m *minerCraftTxNotFound) QueryTransaction(_ context.Context, miner *minerc MimeType: applicationJSONType, }, }, - Query: &minercraft.QueryPayload{ + Query: &minercraft.QueryTxResponse{ APIVersion: "", Timestamp: "2022-01-24T01:40:41.136Z", TxID: notFoundExample1TxID, @@ -674,7 +755,7 @@ func (m *minerCraftTxNotFound) QueryTransaction(_ context.Context, miner *minerc MimeType: applicationJSONType, }, }, - Query: &minercraft.QueryPayload{ + Query: &minercraft.QueryTxResponse{ APIVersion: "1.1.0-1-g35ba2d3", Timestamp: "2022-01-24T01:41:01.683Z", TxID: notFoundExample1TxID, @@ -716,7 +797,7 @@ func (m *minerCraftBroadcastTimeout) SubmitTransaction(_ context.Context, miner MimeType: applicationJSONType, }, }, - Results: &minercraft.SubmissionPayload{ + Results: &minercraft.UnifiedSubmissionPayload{ APIVersion: "", CurrentHighestBlockHash: "0000000000000000064c900b1fceb316302426aedb2242852530b5e78144f2c1", CurrentHighestBlockHeight: 724816, diff --git a/chainstate/transaction.go b/chainstate/transaction.go index f2813335..eae188a6 100644 --- a/chainstate/transaction.go +++ b/chainstate/transaction.go @@ -8,7 +8,7 @@ import ( "github.com/BuxOrg/bux/utils" "github.com/mrz1836/go-nownodes" - "github.com/tonicpow/go-minercraft" + "github.com/tonicpow/go-minercraft/v2" ) // query will try ALL providers in order and return the first "valid" response based on requirements @@ -22,10 +22,10 @@ func (c *Client) query(ctx context.Context, id string, requiredIn RequiredIn, // First: try all mAPI miners (Only supported on main and test right now) if !utils.StringInSlice(ProviderMAPI, c.options.config.excludedProviders) { if c.Network() == MainNet || c.Network() == TestNet { - for index := range c.options.config.mAPI.queryMiners { - if c.options.config.mAPI.queryMiners[index] != nil { - if res, err := queryMAPI( - ctxWithCancel, c, c.options.config.mAPI.queryMiners[index].Miner, id, + for index := range c.options.config.minercraftConfig.queryMiners { + if c.options.config.minercraftConfig.queryMiners[index] != nil { + if res, err := queryMinercraft( + ctxWithCancel, c, c.options.config.minercraftConfig.queryMiners[index].Miner, id, ); err == nil && checkRequirement(requiredIn, id, res) { return res } @@ -76,7 +76,7 @@ func (c *Client) fastestQuery(ctx context.Context, id string, requiredIn Require var wg sync.WaitGroup if !utils.StringInSlice(ProviderMAPI, c.options.config.excludedProviders) { if c.Network() == MainNet || c.Network() == TestNet { - for index := range c.options.config.mAPI.queryMiners { + for index := range c.options.config.minercraftConfig.queryMiners { wg.Add(1) go func( ctx context.Context, client *Client, @@ -84,12 +84,12 @@ func (c *Client) fastestQuery(ctx context.Context, id string, requiredIn Require id string, requiredIn RequiredIn, ) { defer wg.Done() - if res, err := queryMAPI( + if res, err := queryMinercraft( ctx, client, miner, id, ); err == nil && checkRequirement(requiredIn, id, res) { resultsChannel <- res } - }(ctxWithCancel, c, &wg, c.options.config.mAPI.queryMiners[index].Miner, id, requiredIn) + }(ctxWithCancel, c, &wg, c.options.config.minercraftConfig.queryMiners[index].Miner, id, requiredIn) } } } @@ -131,11 +131,11 @@ func (c *Client) fastestQuery(ctx context.Context, id string, requiredIn Require return <-resultsChannel } -// queryMAPI will submit a query transaction request to a miner using mAPI -func queryMAPI(ctx context.Context, client ClientInterface, miner *minercraft.Miner, id string) (*TransactionInfo, error) { - client.DebugLog("executing request in mapi using miner: " + miner.Name) +// queryMinercraft will submit a query transaction request to a miner using Minercraft(mAPI or Arc) +func queryMinercraft(ctx context.Context, client ClientInterface, miner *minercraft.Miner, id string) (*TransactionInfo, error) { + client.DebugLog("executing request in minercraft using miner: " + miner.Name) if resp, err := client.Minercraft().QueryTransaction(ctx, miner, id); err != nil { - client.DebugLog("error executing request in mapi using miner: " + miner.Name + " failed: " + err.Error()) + client.DebugLog("error executing request in minercraft using miner: " + miner.Name + " failed: " + err.Error()) return nil, err } else if resp != nil && resp.Query.ReturnResult == mAPISuccess && strings.EqualFold(resp.Query.TxID, id) { return &TransactionInfo{ diff --git a/client_options.go b/client_options.go index b170cbee..3b3d08de 100644 --- a/client_options.go +++ b/client_options.go @@ -18,7 +18,7 @@ import ( "github.com/mrz1836/go-datastore" zLogger "github.com/mrz1836/go-logger" "github.com/newrelic/go-agent/v3/newrelic" - "github.com/tonicpow/go-minercraft" + "github.com/tonicpow/go-minercraft/v2" "github.com/tonicpow/go-paymail" "github.com/tonicpow/go-paymail/server" taskq "github.com/vmihailenco/taskq/v3" @@ -707,17 +707,24 @@ func WithCustomNotifications(customNotifications notifications.ClientInterface) } } -// WithMapiFeeQuotes will set usage of mapi fee quotes instead of default fees -func WithMapiFeeQuotes() ClientOps { +// WithMinercraftFeeQuotes will set usage of minercraft's fee quotes instead of default fees +func WithMinercraftFeeQuotes() ClientOps { return func(c *clientOptions) { - c.chainstate.options = append(c.chainstate.options, chainstate.WithMapiFeeQuotes()) + c.chainstate.options = append(c.chainstate.options, chainstate.WithMinercraftFeeQuotes()) } } -// WithOverridenMAPIConfig will override mApi config with custom data(custom token, endpoints and etc.) -func WithOverridenMAPIConfig(miners []*minercraft.Miner) ClientOps { +// WithArc will specify Arc as an API for minercraft client +func WithArc() ClientOps { return func(c *clientOptions) { - c.chainstate.options = append(c.chainstate.options, chainstate.WithOverridenMAPIConfig(miners)) + c.chainstate.options = append(c.chainstate.options, chainstate.WithArc()) + } +} + +// WithMAPI will specify Arc as an API for minercraft client +func WithMAPI() ClientOps { + return func(c *clientOptions) { + c.chainstate.options = append(c.chainstate.options, chainstate.WithMAPI()) } } @@ -729,3 +736,10 @@ func WithMinercraft(minercraft minercraft.ClientInterface) ClientOps { } } } + +// WithMinercraftAPIs set custom MinerAPIs for minercraft +func WithMinercraftAPIs(miners []*minercraft.MinerAPIs) ClientOps { + return func(c *clientOptions) { + c.chainstate.options = append(c.chainstate.options, chainstate.WithMinercraftAPIs(miners)) + } +} diff --git a/examples/client/broadcast_miners/broadcast_miners.go b/examples/client/broadcast_miners/broadcast_miners.go index f89c1d93..3ec6a97e 100644 --- a/examples/client/broadcast_miners/broadcast_miners.go +++ b/examples/client/broadcast_miners/broadcast_miners.go @@ -8,7 +8,7 @@ import ( "github.com/BuxOrg/bux" "github.com/BuxOrg/bux/chainstate" "github.com/BuxOrg/bux/taskmanager" - "github.com/tonicpow/go-minercraft" + "github.com/tonicpow/go-minercraft/v2" ) func main() { @@ -16,7 +16,18 @@ func main() { // Create a custom miner (using your api key for custom rates) miners, _ := minercraft.DefaultMiners() minerTaal := minercraft.MinerByName(miners, minercraft.MinerTaal) - minerTaal.Token = os.Getenv("BUX_TAAL_API_KEY") + minerCraftApis := []*minercraft.MinerAPIs{ + { + MinerID: minerTaal.MinerID, + APIs: []minercraft.API{ + { + Token: os.Getenv("BUX_TAAL_API_KEY"), + URL: "https://tapi.taal.com/arc", + Type: minercraft.Arc, + }, + }, + }, + } // Create the client client, err := bux.NewClient( @@ -24,6 +35,8 @@ func main() { bux.WithTaskQ(taskmanager.DefaultTaskQConfig("test_queue"), taskmanager.FactoryMemory), // Tasks bux.WithBroadcastMiners([]*chainstate.Miner{{Miner: minerTaal}}), // This will auto-fetch a policy using the token (api key) bux.WithQueryMiners([]*chainstate.Miner{{Miner: minerTaal}}), // This will only use this as a query provider + bux.WithMinercraftAPIs(minerCraftApis), + bux.WithArc(), ) if err != nil { log.Fatalln("error: " + err.Error()) diff --git a/examples/client/custom_rates/custom_rates.go b/examples/client/custom_rates/custom_rates.go index 9ad68126..b7a638b7 100644 --- a/examples/client/custom_rates/custom_rates.go +++ b/examples/client/custom_rates/custom_rates.go @@ -9,7 +9,7 @@ import ( "github.com/BuxOrg/bux" "github.com/BuxOrg/bux/chainstate" "github.com/BuxOrg/bux/taskmanager" - "github.com/tonicpow/go-minercraft" + "github.com/tonicpow/go-minercraft/v2" ) func main() { @@ -19,7 +19,18 @@ func main() { // Create a custom miner (using your api key for custom rates) miners, _ := minercraft.DefaultMiners() minerTaal := minercraft.MinerByName(miners, minercraft.MinerTaal) - minerTaal.Token = os.Getenv("BUX_TAAL_API_KEY") + minerCraftApis := []*minercraft.MinerAPIs{ + { + MinerID: minerTaal.MinerID, + APIs: []minercraft.API{ + { + Token: os.Getenv("BUX_TAAL_API_KEY"), + URL: "https://tapi.taal.com/arc", + Type: minercraft.Arc, + }, + }, + }, + } // Create the client client, err := bux.NewClient( @@ -27,6 +38,8 @@ func main() { bux.WithAutoMigrate(bux.BaseModels...), // All models bux.WithTaskQ(taskmanager.DefaultTaskQConfig("test_queue"), taskmanager.FactoryMemory), // Tasks bux.WithBroadcastMiners([]*chainstate.Miner{{Miner: minerTaal}}), // This will auto-fetch a policy using the token (api key) + bux.WithMinercraftAPIs(minerCraftApis), + bux.WithArc(), ) if err != nil { log.Fatalln("error: " + err.Error()) diff --git a/go.mod b/go.mod index a906667b..7a241b04 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( github.com/robfig/cron/v3 v3.0.1 github.com/stretchr/testify v1.8.4 github.com/tidwall/gjson v1.14.4 - github.com/tonicpow/go-minercraft v0.9.2 + github.com/tonicpow/go-minercraft/v2 v2.0.1 github.com/tonicpow/go-paymail v0.9.0 github.com/tryvium-travels/memongo v0.9.0 github.com/tylertreat/BoomFilters v0.0.0-20210315201527-1a82519a3e43 diff --git a/go.sum b/go.sum index edf0aff7..e1471fd4 100644 --- a/go.sum +++ b/go.sum @@ -405,8 +405,8 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= -github.com/tonicpow/go-minercraft v0.9.2 h1:8rkUn1+gb3jtorkqzPJhxOTcpU3v0zWoqX+9CZ6axN8= -github.com/tonicpow/go-minercraft v0.9.2/go.mod h1:zBYXMlB13f+wPD49G5xZ0wja0Oz+zEyRRsPhIy/z4/Y= +github.com/tonicpow/go-minercraft/v2 v2.0.1 h1:Rgsfrjkpm6LZ5ZNRpUKgFU18y+53WCvacGKlZ6qsC4Q= +github.com/tonicpow/go-minercraft/v2 v2.0.1/go.mod h1:ShQ6r136+HXsH/Nr9rbHJPzFtQzP5U3da7UmFXSe3PA= github.com/tonicpow/go-paymail v0.9.0 h1:Vq7pyH0/nz8du22x1e8W2Zhx7qaNSZl/gftFkTsooyQ= github.com/tonicpow/go-paymail v0.9.0/go.mod h1:Eo31OjLWTCXUwjp+38PbpJ7Rj42gOibt1/MYNVL7Alw= github.com/tryvium-travels/memongo v0.9.0 h1:k5kuTHSDdITN+aMaNoKr1nzTyNvhwOXtb9SnrMjRg+I= diff --git a/mock_chainstate_test.go b/mock_chainstate_test.go index 6e8db5b3..39d89c7b 100644 --- a/mock_chainstate_test.go +++ b/mock_chainstate_test.go @@ -8,7 +8,7 @@ import ( "github.com/BuxOrg/bux/utils" "github.com/mrz1836/go-nownodes" "github.com/mrz1836/go-whatsonchain" - "github.com/tonicpow/go-minercraft" + "github.com/tonicpow/go-minercraft/v2" ) // chainStateBase is the base interface / methods