From 4cf49bd72c7da1e095bc3c8a992678e81fc03333 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Fri, 23 Feb 2024 22:09:44 +0800 Subject: [PATCH] multi: turn `BackendVersion` into an interface This commit adds a new interface, `BackendVersion`, to support checking versions for multiple both btcd and bitcoind. Once neutrino version is also pinned here, it should also be checked. --- rpcclient/backend_version.go | 76 ++++++++++++++++++++++++++++--- rpcclient/backend_version_test.go | 44 +++++++++++++++++- rpcclient/chain.go | 2 +- rpcclient/infrastructure.go | 19 ++++---- rpcclient/rawtransactions.go | 8 ++-- 5 files changed, 129 insertions(+), 20 deletions(-) diff --git a/rpcclient/backend_version.go b/rpcclient/backend_version.go index e2d56b0d..cb2a46fc 100644 --- a/rpcclient/backend_version.go +++ b/rpcclient/backend_version.go @@ -2,13 +2,32 @@ package rpcclient import "strings" -// BackendVersion represents the version of the backend the client is currently -// connected to. -type BackendVersion uint8 +// BackendVersion defines an interface to handle the version of the backend +// used by the client. +type BackendVersion interface { + // String returns a human-readable backend version. + String() string + + // SupportUnifiedSoftForks returns true if the backend supports the + // unified softforks format. + SupportUnifiedSoftForks() bool + + // SupportTestMempoolAccept returns true if the backend supports the + // testmempoolaccept RPC. + SupportTestMempoolAccept() bool + + // SupportGetTxSpendingPrevOut returns true if the backend supports the + // gettxspendingprevout RPC. + SupportGetTxSpendingPrevOut() bool +} + +// BitcoindVersion represents the version of the bitcoind the client is +// currently connected to. +type BitcoindVersion uint8 const ( // BitcoindPre19 represents a bitcoind version before 0.19.0. - BitcoindPre19 BackendVersion = iota + BitcoindPre19 BitcoindVersion = iota // BitcoindPre22 represents a bitcoind version equal to or greater than // 0.19.0 and smaller than 22.0.0. @@ -28,7 +47,7 @@ const ( ) // String returns a human-readable backend version. -func (b BackendVersion) String() string { +func (b BitcoindVersion) String() string { switch b { case BitcoindPre19: return "bitcoind 0.19 and below" @@ -50,6 +69,29 @@ func (b BackendVersion) String() string { } } +// SupportUnifiedSoftForks returns true if the backend supports the unified +// softforks format. +func (b BitcoindVersion) SupportUnifiedSoftForks() bool { + // Versions of bitcoind on or after v0.19.0 use the unified format. + return b > BitcoindPre19 +} + +// SupportTestMempoolAccept returns true if bitcoind version is 22.0.0 or +// above. +func (b BitcoindVersion) SupportTestMempoolAccept() bool { + return b > BitcoindPre22 +} + +// SupportGetTxSpendingPrevOut returns true if bitcoind version is 24.0.0 or +// above. +func (b BitcoindVersion) SupportGetTxSpendingPrevOut() bool { + return b > BitcoindPre24 +} + +// Compile-time checks to ensure that BitcoindVersion satisfy the +// BackendVersion interface. +var _ BackendVersion = BitcoindVersion(0) + const ( // bitcoind19Str is the string representation of bitcoind v0.19.0. bitcoind19Str = "0.19.0" @@ -74,7 +116,7 @@ const ( // parseBitcoindVersion parses the bitcoind version from its string // representation. -func parseBitcoindVersion(version string) BackendVersion { +func parseBitcoindVersion(version string) BitcoindVersion { // Trim the version of its prefix and suffix to determine the // appropriate version number. version = strings.TrimPrefix( @@ -127,6 +169,28 @@ func (b BtcdVersion) String() string { } } +// SupportUnifiedSoftForks returns true if the backend supports the unified +// softforks format. +// +// NOTE: always true for btcd as we didn't track it before. +func (b BtcdVersion) SupportUnifiedSoftForks() bool { + return true +} + +// SupportTestMempoolAccept returns true if btcd version is 24.1.0 or above. +func (b BtcdVersion) SupportTestMempoolAccept() bool { + return b > BtcdPre2401 +} + +// SupportGetTxSpendingPrevOut returns true if btcd version is 24.1.0 or above. +func (b BtcdVersion) SupportGetTxSpendingPrevOut() bool { + return b > BtcdPre2401 +} + +// Compile-time checks to ensure that BtcdVersion satisfy the BackendVersion +// interface. +var _ BackendVersion = BtcdVersion(0) + const ( // btcd2401Val is the int representation of btcd v0.24.1. btcd2401Val = 240100 diff --git a/rpcclient/backend_version_test.go b/rpcclient/backend_version_test.go index 91c84ad4..3a4baec1 100644 --- a/rpcclient/backend_version_test.go +++ b/rpcclient/backend_version_test.go @@ -14,7 +14,7 @@ func TestParseBitcoindVersion(t *testing.T) { testCases := []struct { name string rpcVersion string - parsedVersion BackendVersion + parsedVersion BitcoindVersion }{ { name: "parse version 0.19 and below", @@ -104,3 +104,45 @@ func TestParseBtcdVersion(t *testing.T) { }) } } + +// TestVersionSupports checks all the versions of bitcoind and btcd to ensure +// that the RPCs are supported correctly. +func TestVersionSupports(t *testing.T) { + t.Parallel() + + require := require.New(t) + + // For bitcoind, unified softforks format is supported in 19.0 and + // above. + require.False(BitcoindPre19.SupportUnifiedSoftForks()) + require.True(BitcoindPre22.SupportUnifiedSoftForks()) + require.True(BitcoindPre24.SupportUnifiedSoftForks()) + require.True(BitcoindPre25.SupportUnifiedSoftForks()) + require.True(BitcoindPost25.SupportUnifiedSoftForks()) + + // For bitcoind, `testmempoolaccept` is supported in 22.0 and above. + require.False(BitcoindPre19.SupportTestMempoolAccept()) + require.False(BitcoindPre22.SupportTestMempoolAccept()) + require.True(BitcoindPre24.SupportTestMempoolAccept()) + require.True(BitcoindPre25.SupportTestMempoolAccept()) + require.True(BitcoindPost25.SupportTestMempoolAccept()) + + // For bitcoind, `gettxspendingprevout` is supported in 24.0 and above. + require.False(BitcoindPre19.SupportGetTxSpendingPrevOut()) + require.False(BitcoindPre22.SupportGetTxSpendingPrevOut()) + require.False(BitcoindPre24.SupportGetTxSpendingPrevOut()) + require.True(BitcoindPre25.SupportGetTxSpendingPrevOut()) + require.True(BitcoindPost25.SupportGetTxSpendingPrevOut()) + + // For btcd, unified softforks format is supported in all versions. + require.True(BtcdPre2401.SupportUnifiedSoftForks()) + require.True(BtcdPost2401.SupportUnifiedSoftForks()) + + // For btcd, `testmempoolaccept` is supported in 24.1 and above. + require.False(BtcdPre2401.SupportTestMempoolAccept()) + require.True(BtcdPost2401.SupportTestMempoolAccept()) + + // For btcd, `gettxspendingprevout` is supported in 24.1 and above. + require.False(BtcdPre2401.SupportGetTxSpendingPrevOut()) + require.True(BtcdPost2401.SupportGetTxSpendingPrevOut()) +} diff --git a/rpcclient/chain.go b/rpcclient/chain.go index f2ce1ea6..e65c40b3 100644 --- a/rpcclient/chain.go +++ b/rpcclient/chain.go @@ -441,7 +441,7 @@ func unmarshalGetBlockChainInfoResultSoftForks(chainInfo *btcjson.GetBlockChainI version BackendVersion, res []byte) error { // Versions of bitcoind on or after v0.19.0 use the unified format. - if version > BitcoindPre19 { + if version.SupportUnifiedSoftForks() { var softForks btcjson.UnifiedSoftForks if err := json.Unmarshal(res, &softForks); err != nil { return err diff --git a/rpcclient/infrastructure.go b/rpcclient/infrastructure.go index 404a7b4a..39cfa3f4 100644 --- a/rpcclient/infrastructure.go +++ b/rpcclient/infrastructure.go @@ -134,7 +134,7 @@ type Client struct { // backendVersion is the version of the backend the client is currently // connected to. This should be retrieved through GetVersion. backendVersionMu sync.Mutex - backendVersion *BackendVersion + backendVersion BackendVersion // mtx is a mutex to protect access to connection related fields. mtx sync.Mutex @@ -1577,7 +1577,7 @@ func (c *Client) BackendVersion() (BackendVersion, error) { defer c.backendVersionMu.Unlock() if c.backendVersion != nil { - return *c.backendVersion, nil + return c.backendVersion, nil } // We'll start by calling GetInfo. This method doesn't exist for @@ -1589,20 +1589,20 @@ func (c *Client) BackendVersion() (BackendVersion, error) { // Parse the btcd version and cache it. case nil: log.Debugf("Detected btcd version: %v", info.Version) - version := Btcd - c.backendVersion = &version - return *c.backendVersion, nil + version := parseBtcdVersion(info.Version) + c.backendVersion = version + return c.backendVersion, nil // Inspect the RPC error to ensure the method was not found, otherwise // we actually ran into an error. case *btcjson.RPCError: if err.Code != btcjson.ErrRPCMethodNotFound.Code { - return 0, fmt.Errorf("unable to detect btcd version: "+ + return nil, fmt.Errorf("unable to detect btcd version: "+ "%v", err) } default: - return 0, fmt.Errorf("unable to detect btcd version: %v", err) + return nil, fmt.Errorf("unable to detect btcd version: %v", err) } // Since the GetInfo method was not found, we assume the client is @@ -1610,7 +1610,8 @@ func (c *Client) BackendVersion() (BackendVersion, error) { // GetNetworkInfo. networkInfo, err := c.GetNetworkInfo() if err != nil { - return 0, fmt.Errorf("unable to detect bitcoind version: %v", err) + return nil, fmt.Errorf("unable to detect bitcoind version: %v", + err) } // Parse the bitcoind version and cache it. @@ -1618,7 +1619,7 @@ func (c *Client) BackendVersion() (BackendVersion, error) { version := parseBitcoindVersion(networkInfo.SubVersion) c.backendVersion = &version - return *c.backendVersion, nil + return c.backendVersion, nil } func (c *Client) sendAsync() FutureGetBulkResult { diff --git a/rpcclient/rawtransactions.go b/rpcclient/rawtransactions.go index 4737aee5..3197808f 100644 --- a/rpcclient/rawtransactions.go +++ b/rpcclient/rawtransactions.go @@ -360,7 +360,9 @@ func (c *Client) SendRawTransactionAsync(tx *wire.MsgTx, allowHighFees bool) Fut var cmd *btcjson.SendRawTransactionCmd // Starting from bitcoind v0.19.0, the MaxFeeRate field should be used. - if version > BitcoindPre19 { + // + // When unified softforks format is supported, it's 0.19 and above. + if version.SupportUnifiedSoftForks() { // Using a 0 MaxFeeRate is interpreted as a maximum fee rate not // being enforced by bitcoind. var maxFeeRate int32 @@ -943,7 +945,7 @@ func (c *Client) TestMempoolAcceptAsync(txns []*wire.MsgTx, // // We decide to not support this call for versions below 22.0.0. as the // request/response formats are very different. - if version < BitcoindPre22 { + if !version.SupportTestMempoolAccept() { err := fmt.Errorf("%w: %v", ErrBitcoindVersion, version) return newFutureError(err) } @@ -1057,7 +1059,7 @@ func (c *Client) GetTxSpendingPrevOutAsync( log.Debugf("GetTxSpendingPrevOutAsync: backend version %s", version) // Exit early if the version is below 24.0.0. - if version < BitcoindPre24 { + if !version.SupportGetTxSpendingPrevOut() { err := fmt.Errorf("%w: %v", ErrBitcoindVersion, version) return newFutureError(err) }