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.
This commit is contained in:
yyforyongyu 2024-02-23 22:09:44 +08:00
parent 9e6070a707
commit 4cf49bd72c
No known key found for this signature in database
GPG Key ID: 9BCD95C4FF296868
5 changed files with 129 additions and 20 deletions

View File

@ -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

View File

@ -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())
}

View File

@ -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

View File

@ -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 {

View File

@ -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)
}