From 12514f6b811be15513721f150fd2995d8e04fbe7 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Fri, 5 Jan 2024 16:52:50 +0800 Subject: [PATCH 1/4] gomod: update btcd version --- go.mod | 8 ++++---- go.sum | 19 +++++++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 99ac697bde..890e993160 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,11 @@ module github.com/btcsuite/btcwallet require ( - github.com/btcsuite/btcd v0.23.5-0.20230711222809-7faa9b266231 + github.com/btcsuite/btcd v0.24.1-0.20240116200649-17fdc5219b36 github.com/btcsuite/btcd/btcec/v2 v2.2.2 - github.com/btcsuite/btcd/btcutil v1.1.3 + github.com/btcsuite/btcd/btcutil v1.1.5 github.com/btcsuite/btcd/btcutil/psbt v1.1.8 - github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 + github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2 github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 @@ -23,7 +23,7 @@ require ( github.com/lightninglabs/neutrino/cache v1.1.1 github.com/lightningnetwork/lnd/ticker v1.0.0 github.com/lightningnetwork/lnd/tlv v1.0.2 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.8.4 golang.org/x/crypto v0.7.0 golang.org/x/net v0.10.0 golang.org/x/term v0.8.0 diff --git a/go.sum b/go.sum index 0eebc143e0..45d3a0af00 100644 --- a/go.sum +++ b/go.sum @@ -4,10 +4,10 @@ github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13P github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.22.0-beta.0.20220204213055-eaf0459ff879/go.mod h1:osu7EoKiL36UThEgzYPqdRaxeo0NU8VoXqgcnwpey0g= github.com/btcsuite/btcd v0.22.0-beta.0.20220207191057-4dc4ff7963b4/go.mod h1:7alexyj/lHlOtr2PJK7L/+HDJZpcGDn/pAU98r7DY08= -github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= github.com/btcsuite/btcd v0.23.1/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= -github.com/btcsuite/btcd v0.23.5-0.20230711222809-7faa9b266231 h1:FZR6mILlSI/GDx8ydNVBZAlXlRXsoRBWX2Un64mpfsI= -github.com/btcsuite/btcd v0.23.5-0.20230711222809-7faa9b266231/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= +github.com/btcsuite/btcd v0.24.1-0.20240116200649-17fdc5219b36 h1:Us/FoCuHjjn1OfE278h9QTGuuydc0n+SA+NlycvfNsM= +github.com/btcsuite/btcd v0.24.1-0.20240116200649-17fdc5219b36/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.1/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= @@ -16,13 +16,14 @@ github.com/btcsuite/btcd/btcec/v2 v2.2.2/go.mod h1:9/CSmJxmuvqzX9Wh2fXMWToLOHhPd github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= github.com/btcsuite/btcd/btcutil v1.1.1/go.mod h1:nbKlBMNm9FGsdvKvu0essceubPiAcI57pYBNnsLAa34= -github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= -github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= +github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= github.com/btcsuite/btcd/btcutil/psbt v1.1.8 h1:4voqtT8UppT7nmKQkXV+T9K8UyQjKOn2z/ycpmJK8wg= github.com/btcsuite/btcd/btcutil/psbt v1.1.8/go.mod h1:kA6FLH/JfUx++j9pYU0pyu+Z8XGBQuuTmuKYUf6q7/U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= @@ -77,6 +78,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= @@ -130,8 +133,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= From c56e9c09ebd28cf82fbfa2703bdd3c27c9442cf7 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Fri, 5 Jan 2024 04:17:31 +0800 Subject: [PATCH 2/4] chain+wallet: add `TestMempoolAccept` to chain interface --- chain/bitcoind_client.go | 10 ++++++++++ chain/btcd.go | 4 ++++ chain/interface.go | 2 ++ chain/neutrino.go | 19 +++++++++++++++++++ wallet/mock.go | 11 +++++++++++ 5 files changed, 46 insertions(+) diff --git a/chain/bitcoind_client.go b/chain/bitcoind_client.go index dad4722f86..4082b729df 100644 --- a/chain/bitcoind_client.go +++ b/chain/bitcoind_client.go @@ -217,6 +217,16 @@ func (c *BitcoindClient) SendRawTransaction(tx *wire.MsgTx, return c.chainConn.client.SendRawTransaction(tx, allowHighFees) } +// TestMempoolAcceptCmd returns result of mempool acceptance tests indicating +// if raw transaction(s) would be accepted by mempool. +// +// NOTE: This is part of the chain.Interface interface. +func (c *BitcoindClient) TestMempoolAccept(txns []*wire.MsgTx, + maxFeeRate float64) ([]*btcjson.TestMempoolAcceptResult, error) { + + return c.chainConn.client.TestMempoolAccept(txns, maxFeeRate) +} + // Notifications returns a channel to retrieve notifications from. // // NOTE: This is part of the chain.Interface interface. diff --git a/chain/btcd.go b/chain/btcd.go index 14678853bb..4ddb183c40 100644 --- a/chain/btcd.go +++ b/chain/btcd.go @@ -39,6 +39,10 @@ type RPCClient struct { quitMtx sync.Mutex } +// A compile-time check to ensure that RPCClient satisfies the chain.Interface +// interface. +var _ Interface = (*RPCClient)(nil) + // NewRPCClient creates a client connection to the server described by the // connect string. If disableTLS is false, the remote RPC certificate must be // provided in the certs slice. The connection is not established immediately, diff --git a/chain/interface.go b/chain/interface.go index 6d1300263a..5691e792ee 100644 --- a/chain/interface.go +++ b/chain/interface.go @@ -3,6 +3,7 @@ package chain import ( "time" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/rpcclient" @@ -47,6 +48,7 @@ type Interface interface { NotifyBlocks() error Notifications() <-chan interface{} BackEnd() string + TestMempoolAccept([]*wire.MsgTx, float64) ([]*btcjson.TestMempoolAcceptResult, error) } // Notification types. These are defined here and processed from from reading diff --git a/chain/neutrino.go b/chain/neutrino.go index e30e9ac94a..b794a667e5 100644 --- a/chain/neutrino.go +++ b/chain/neutrino.go @@ -6,6 +6,7 @@ import ( "sync" "time" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil/gcs" "github.com/btcsuite/btcd/btcutil/gcs/builder" @@ -20,6 +21,10 @@ import ( "github.com/lightninglabs/neutrino/headerfs" ) +// ErrUnimplemented is returned when a certain method is not implemented for a +// given interface. +var ErrUnimplemented = errors.New("unimplemented") + // NeutrinoClient is an implementation of the btcwallet chain.Interface interface. type NeutrinoClient struct { CS NeutrinoChainService @@ -63,6 +68,10 @@ type NeutrinoClient struct { clientMtx sync.Mutex } +// A compile-time check to ensure that RPCClient satisfies the chain.Interface +// interface. +var _ Interface = (*NeutrinoClient)(nil) + // NewNeutrinoClient creates a new NeutrinoClient struct with a backing // ChainService. func NewNeutrinoClient(chainParams *chaincfg.Params, @@ -217,6 +226,16 @@ func (s *NeutrinoClient) SendRawTransaction(tx *wire.MsgTx, allowHighFees bool) return &hash, nil } +// TestMempoolAcceptCmd returns result of mempool acceptance tests indicating +// if raw transaction(s) would be accepted by mempool. +// +// NOTE: This is part of the chain.Interface interface. +func (s *NeutrinoClient) TestMempoolAccept(txns []*wire.MsgTx, + maxFeeRate float64) ([]*btcjson.TestMempoolAcceptResult, error) { + + return nil, ErrUnimplemented +} + // FilterBlocks scans the blocks contained in the FilterBlocksRequest for any // addresses of interest. For each requested block, the corresponding compact // filter will first be checked for matches, skipping those that do not report diff --git a/wallet/mock.go b/wallet/mock.go index edcf469831..8c995ebb57 100644 --- a/wallet/mock.go +++ b/wallet/mock.go @@ -3,6 +3,7 @@ package wallet import ( "time" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" @@ -83,3 +84,13 @@ func (m *mockChainClient) Notifications() <-chan interface{} { func (m *mockChainClient) BackEnd() string { return "mock" } + +// TestMempoolAcceptCmd returns result of mempool acceptance tests indicating +// if raw transaction(s) would be accepted by mempool. +// +// NOTE: This is part of the chain.Interface interface. +func (m *mockChainClient) TestMempoolAccept(txns []*wire.MsgTx, + maxFeeRate float64) ([]*btcjson.TestMempoolAcceptResult, error) { + + return nil, nil +} From e4d38e8d0266f64cbae5fd2cf1b7562ce0e6e2e1 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Sat, 6 Jan 2024 05:42:24 +0800 Subject: [PATCH 3/4] wallet: fix error match pattern for `missing-inputs` --- wallet/wallet.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/wallet/wallet.go b/wallet/wallet.go index efe92e3943..0e5ee53769 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -4129,9 +4129,8 @@ func MapBroadcastBackendError(err error) error { // Returned by bitcoind on the RPC when broadcasting a transaction that // is spending either output that is missing or already spent. // - // https://github.com/bitcoin/bitcoin/blob/9bf5768dd628b3a7c30dd42b5ed477a92c4d3540/src/node/transaction.cpp#L49 - // https://github.com/bitcoin/bitcoin/blob/0.20/src/validation.cpp#L642 - case match(err, "missing inputs") || + // https://github.com/bitcoin/bitcoin/blob/3ba8de1b704d590fa4e1975620bd21d830d11666/test/functional/mempool_accept.py#L163C1-L163C1 + case match(err, "missing-inputs") || match(err, "bad-txns-inputs-missingorspent"): returnErr = &ErrDoubleSpend{ From 38c00dc00e9489fb7c9bcda01486efffa39fb9e3 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Mon, 15 Jan 2024 14:09:07 +0800 Subject: [PATCH 4/4] wallet: add `matchBitcoindErr` to properly match bitcoind errors --- wallet/wallet.go | 61 +++++++++++++++++++++++----------------- wallet/wallet_test.go | 65 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 25 deletions(-) diff --git a/wallet/wallet.go b/wallet/wallet.go index 0e5ee53769..7e66727872 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -4029,15 +4029,26 @@ func OpenWithRetry(db walletdb.DB, pubPass []byte, cbs *waddrmgr.OpenCallbacks, return w, nil } +// matchBitcoindErr takes an error returned from bitcoind RPC client and +// matches it against the specified string. If the expected string pattern is +// found in the error passed, return true. +func matchBitcoindErr(err error, s string) bool { + // Replace all dashes found in the error string with spaces. + strippedErrStr := strings.Replace(err.Error(), "-", " ", -1) + + // Replace all dashes found in the error string with spaces. + strippedMatchStr := strings.Replace(s, "-", " ", -1) + + // Match against the lowercase. + return strings.Contains( + strings.ToLower(strippedErrStr), + strings.ToLower(strippedMatchStr), + ) +} + // MapBroadcastBackendError maps the different backend errors when broadcasting // a transaction to the bitcoin network to an internal error type. func MapBroadcastBackendError(err error) error { - // match is a helper method to easily string match on the error - // message. - match := func(err error, s string) bool { - return strings.Contains(strings.ToLower(err.Error()), s) - } - // Determine if this was an RPC error thrown due to the transaction // already confirming. var rpcTxConfirmed bool @@ -4059,13 +4070,13 @@ func MapBroadcastBackendError(err error) error { // This error is returned when broadcasting/sending a transaction to a // btcd node that already has it in their mempool. // https://github.com/btcsuite/btcd/blob/130ea5bddde33df32b06a1cdb42a6316eb73cff5/mempool/mempool.go#L953 - case match(err, "already have transaction"): + case matchBitcoindErr(err, "already have transaction"): fallthrough // This error is returned when broadcasting a transaction to a bitcoind // node that already has it in their mempool. // https://github.com/bitcoin/bitcoin/blob/9bf5768dd628b3a7c30dd42b5ed477a92c4d3540/src/validation.cpp#L590 - case match(err, "txn-already-in-mempool"): + case matchBitcoindErr(err, "txn-already-in-mempool"): return &ErrInMempool{ backendError: err, } @@ -4085,13 +4096,13 @@ func MapBroadcastBackendError(err error) error { // This error is returned when broadcasting a transaction that has // already confirmed to a btcd node over the P2P network. // https://github.com/btcsuite/btcd/blob/130ea5bddde33df32b06a1cdb42a6316eb73cff5/mempool/mempool.go#L1036 - case match(err, "transaction already exists"): + case matchBitcoindErr(err, "transaction already exists"): fallthrough // This error is returned when broadcasting a transaction that has // already confirmed to a bitcoind node over the P2P network. // https://github.com/bitcoin/bitcoin/blob/9bf5768dd628b3a7c30dd42b5ed477a92c4d3540/src/validation.cpp#L648 - case match(err, "txn-already-known"): + case matchBitcoindErr(err, "txn-already-known"): return &ErrAlreadyConfirmed{ backendError: err, } @@ -4104,34 +4115,34 @@ func MapBroadcastBackendError(err error) error { // not signaling replacement in the mempool that spends one of the // referenced outputs. // https://github.com/btcsuite/btcd/blob/130ea5bddde33df32b06a1cdb42a6316eb73cff5/mempool/mempool.go#L591 - case match(err, "already spent"): + case matchBitcoindErr(err, "already spent"): fallthrough // This error is returned from btcd when a referenced output cannot be // found, meaning it etiher has been spent or doesn't exist. // https://github.com/btcsuite/btcd/blob/130ea5bddde33df32b06a1cdb42a6316eb73cff5/blockchain/chain.go#L405 - case match(err, "already been spent"): + case matchBitcoindErr(err, "already been spent"): fallthrough // This error is returned from btcd when a transaction is spending // either output that is missing or already spent, and orphans aren't // allowed. // https://github.com/btcsuite/btcd/blob/130ea5bddde33df32b06a1cdb42a6316eb73cff5/mempool/mempool.go#L1409 - case match(err, "orphan transaction"): + case matchBitcoindErr(err, "orphan transaction"): fallthrough // Error returned from bitcoind when output was spent by other // non-replacable transaction already in the mempool. // https://github.com/bitcoin/bitcoin/blob/9bf5768dd628b3a7c30dd42b5ed477a92c4d3540/src/validation.cpp#L622 - case match(err, "txn-mempool-conflict"): + case matchBitcoindErr(err, "txn-mempool-conflict"): fallthrough // Returned by bitcoind on the RPC when broadcasting a transaction that // is spending either output that is missing or already spent. // // https://github.com/bitcoin/bitcoin/blob/3ba8de1b704d590fa4e1975620bd21d830d11666/test/functional/mempool_accept.py#L163C1-L163C1 - case match(err, "missing-inputs") || - match(err, "bad-txns-inputs-missingorspent"): + case matchBitcoindErr(err, "missing-inputs") || + matchBitcoindErr(err, "bad-txns-inputs-missingorspent"): returnErr = &ErrDoubleSpend{ backendError: err, @@ -4140,7 +4151,7 @@ func MapBroadcastBackendError(err error) error { // Returned by bitcoind if the transaction spends outputs that would be // replaced by it. // https://github.com/bitcoin/bitcoin/blob/9bf5768dd628b3a7c30dd42b5ed477a92c4d3540/src/validation.cpp#L790 - case match(err, "bad-txns-spends-conflicting-tx"): + case matchBitcoindErr(err, "bad-txns-spends-conflicting-tx"): fallthrough // Returned by bitcoind when a replacement transaction did not have @@ -4148,19 +4159,19 @@ func MapBroadcastBackendError(err error) error { // https://github.com/bitcoin/bitcoin/blob/9bf5768dd628b3a7c30dd42b5ed477a92c4d3540/src/validation.cpp#L830 // https://github.com/bitcoin/bitcoin/blob/9bf5768dd628b3a7c30dd42b5ed477a92c4d3540/src/validation.cpp#L894 // https://github.com/bitcoin/bitcoin/blob/9bf5768dd628b3a7c30dd42b5ed477a92c4d3540/src/validation.cpp#L904 - case match(err, "insufficient fee"): + case matchBitcoindErr(err, "insufficient fee"): fallthrough // Returned by bitcoind in case the transaction would replace too many // transaction in the mempool. // https://github.com/bitcoin/bitcoin/blob/9bf5768dd628b3a7c30dd42b5ed477a92c4d3540/src/validation.cpp#L858 - case match(err, "too many potential replacements"): + case matchBitcoindErr(err, "too many potential replacements"): fallthrough // Returned by bitcoind if the transaction spends an output that is // unconfimed and not spent by the transaction it replaces. // https://github.com/bitcoin/bitcoin/blob/9bf5768dd628b3a7c30dd42b5ed477a92c4d3540/src/validation.cpp#L882 - case match(err, "replacement-adds-unconfirmed"): + case matchBitcoindErr(err, "replacement-adds-unconfirmed"): fallthrough // Returned by btcd when replacement transaction was rejected for @@ -4170,7 +4181,7 @@ func MapBroadcastBackendError(err error) error { // https://github.com/btcsuite/btcd/blob/130ea5bddde33df32b06a1cdb42a6316eb73cff5/mempool/mempool.go#L875 // https://github.com/btcsuite/btcd/blob/130ea5bddde33df32b06a1cdb42a6316eb73cff5/mempool/mempool.go#L896 // https://github.com/btcsuite/btcd/blob/130ea5bddde33df32b06a1cdb42a6316eb73cff5/mempool/mempool.go#L913 - case match(err, "replacement transaction"): + case matchBitcoindErr(err, "replacement transaction"): returnErr = &ErrReplacement{ backendError: err, } @@ -4179,13 +4190,13 @@ func MapBroadcastBackendError(err error) error { // requirements to be accepted into mempool. This happens when the // mempool reached its limits and is now purging low fee transactions. // https://github.com/bitcoin/bitcoin/blob/9bf5768dd628b3a7c30dd42b5ed477a92c4d3540/src/validation.cpp#L510 - case match(err, "mempool min fee not met"): + case matchBitcoindErr(err, "mempool min fee not met"): fallthrough // Returned by btcd when a transaction does not meet the fee // requirements to be accepted into mempool. // https://github.com/btcsuite/btcd/blob/9c16d23918b15c468c5647c388b9b7db3bc48dc7/mempool/mempool.go#L1151 - case match(err, "fees which is under the required amount"): + case matchBitcoindErr(err, "fees which is under the required amount"): fallthrough // Returned by btcd when a transaction does not meet the fee @@ -4193,7 +4204,7 @@ func MapBroadcastBackendError(err error) error { // but decreasing the min relay fee prevents checking for the priority // in the first place therefore we consider this a mempool fee error. // https://github.com/btcsuite/btcd/blob/9c16d23918b15c468c5647c388b9b7db3bc48dc7/mempool/mempool.go#L1162 - case match(err, "has insufficient priority"): + case matchBitcoindErr(err, "has insufficient priority"): fallthrough // Returned by bitcoind when a transaction does not meet the fee @@ -4201,7 +4212,7 @@ func MapBroadcastBackendError(err error) error { // mempool has a higher min relay fee. Default value for bitcoind is // 1000 sat/kvbyte but this is configurable. // https://github.com/bitcoin/bitcoin/blob/9bf5768dd628b3a7c30dd42b5ed477a92c4d3540/src/validation.cpp#L514 - case match(err, "min relay fee not met"): + case matchBitcoindErr(err, "min relay fee not met"): returnErr = &ErrMempoolFee{ backendError: err, } diff --git a/wallet/wallet_test.go b/wallet/wallet_test.go index bf479d7735..c7920e06de 100644 --- a/wallet/wallet_test.go +++ b/wallet/wallet_test.go @@ -2,6 +2,7 @@ package wallet import ( "encoding/hex" + "errors" "testing" "time" @@ -305,3 +306,67 @@ func TestGetTransaction(t *testing.T) { }) } } + +// TestMatchBitcoindErr checks that `matchBitcoindErr` can correctly replace +// the dashes with spaces and turn title cases into lowercases for a given +// error and match it against the specified string pattern. +func TestMatchBitcoindErr(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + bitcoindErr error + matchStr string + matched bool + }{ + { + name: "error without dashes", + bitcoindErr: errors.New("missing input"), + matchStr: "missing input", + matched: true, + }, + { + name: "match str without dashes", + bitcoindErr: errors.New("missing-input"), + matchStr: "missing input", + matched: true, + }, + { + name: "error with dashes", + bitcoindErr: errors.New("missing-input"), + matchStr: "missing input", + matched: true, + }, + { + name: "match str with dashes", + bitcoindErr: errors.New("missing-input"), + matchStr: "missing-input", + matched: true, + }, + { + name: "error with title case and dash", + bitcoindErr: errors.New("Missing-Input"), + matchStr: "missing input", + matched: true, + }, + { + name: "match str with title case and dash", + bitcoindErr: errors.New("missing-input"), + matchStr: "Missing-Input", + matched: true, + }, + { + name: "unmatched error", + bitcoindErr: errors.New("missing input"), + matchStr: "missingorspent", + matched: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + matched := matchBitcoindErr(tc.bitcoindErr, tc.matchStr) + require.Equal(t, tc.matched, matched) + }) + } +}