Skip to content

Commit

Permalink
chain+wallet: define and use MapRPCErr for chain backends
Browse files Browse the repository at this point in the history
This commit adds a new interface method `MapRPCErr` and use it in
`TestMempoolAccept` and `SendRawTransaction`.
  • Loading branch information
yyforyongyu committed Jul 3, 2024
1 parent db32a63 commit 4006f9b
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 16 deletions.
29 changes: 27 additions & 2 deletions chain/bitcoind_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,27 @@ func (c *BitcoindClient) GetTxOut(txHash *chainhash.Hash, index uint32,
func (c *BitcoindClient) SendRawTransaction(tx *wire.MsgTx,
allowHighFees bool) (*chainhash.Hash, error) {

return c.chainConn.client.SendRawTransaction(tx, allowHighFees)
txid, err := c.chainConn.client.SendRawTransaction(tx, allowHighFees)
if err != nil {
return nil, c.MapRPCErr(err)
}

return txid, nil
}

// MapRPCErr takes an error returned from calling RPC methods from various
// chain backend and map it to an defined error here.
func (c *BitcoindClient) MapRPCErr(rpcErr error) error {
// Try to match it against bitcoind's error.
for i := uint32(0); i < uint32(errSentinel); i++ {
err := RPCErr(i)
if matchErrStr(rpcErr, err.Error()) {
return err
}
}

// If not matched, return the original error wrapped.
return fmt.Errorf("%w: %v", ErrUndefined, rpcErr)
}

// TestMempoolAcceptCmd returns result of mempool acceptance tests indicating
Expand All @@ -224,7 +244,12 @@ func (c *BitcoindClient) SendRawTransaction(tx *wire.MsgTx,
func (c *BitcoindClient) TestMempoolAccept(txns []*wire.MsgTx,
maxFeeRate float64) ([]*btcjson.TestMempoolAcceptResult, error) {

return c.chainConn.client.TestMempoolAccept(txns, maxFeeRate)
result, err := c.chainConn.client.TestMempoolAccept(txns, maxFeeRate)
if err != nil {
return nil, c.MapRPCErr(err)
}

return result, nil
}

// Notifications returns a channel to retrieve notifications from.
Expand Down
45 changes: 45 additions & 0 deletions chain/btcd.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package chain

import (
"errors"
"fmt"
"sync"
"time"

Expand Down Expand Up @@ -578,3 +579,47 @@ func (c *RPCClient) LookupInputMempoolSpend(op wire.OutPoint) (

return getTxSpendingPrevOut(op, c.Client)
}

// MapRPCErr takes an error returned from calling RPC methods from various
// chain backend and map it to an defined error here. It uses the `BtcdErrMap`,
// whose keys are btcd error strings and values are errors made from bitcoind
// error strings.
func (c *RPCClient) MapRPCErr(rpcErr error) error {
// Iterate the map and find the matching error.
for btcdErr, err := range BtcdErrMap {
// Match it against btcd's error.
if matchErrStr(rpcErr, btcdErr) {
return err
}
}

// If not matched, return the original error wrapped.
return fmt.Errorf("%w: %v", ErrUndefined, rpcErr)
}

// 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 *RPCClient) TestMempoolAccept(txns []*wire.MsgTx,
maxFeeRate float64) ([]*btcjson.TestMempoolAcceptResult, error) {

result, err := c.Client.TestMempoolAccept(txns, maxFeeRate)
if err != nil {
return nil, c.MapRPCErr(err)
}

return result, nil
}

// SendRawTransaction sends a raw transaction via btcd.
func (c *RPCClient) SendRawTransaction(tx *wire.MsgTx,
allowHighFees bool) (*chainhash.Hash, error) {

txid, err := c.Client.SendRawTransaction(tx, allowHighFees)
if err != nil {
return nil, c.MapRPCErr(err)
}

return txid, nil
}
1 change: 1 addition & 0 deletions chain/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type Interface interface {
Notifications() <-chan interface{}
BackEnd() string
TestMempoolAccept([]*wire.MsgTx, float64) ([]*btcjson.TestMempoolAcceptResult, error)
MapRPCErr(err error) error
}

// Notification types. These are defined here and processed from from reading
Expand Down
21 changes: 20 additions & 1 deletion chain/neutrino.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ func (s *NeutrinoClient) SendRawTransaction(tx *wire.MsgTx, allowHighFees bool)
*chainhash.Hash, error) {
err := s.CS.SendTransaction(tx)
if err != nil {
return nil, rpcclient.MapRPCErr(err)
return nil, s.MapRPCErr(err)
}
hash := tx.TxHash()
return &hash, nil
Expand Down Expand Up @@ -815,3 +815,22 @@ out:
close(s.dequeueNotification)
s.wg.Done()
}

// MapRPCErr takes an error returned from calling RPC methods from various
// chain backend and map it to an defined error here. It uses the `BtcdErrMap`,
// whose keys are btcd error strings and values are errors made from bitcoind
// error strings.
//
// NOTE: we assume neutrino shares the same error strings as btcd.
func (s *NeutrinoClient) MapRPCErr(rpcErr error) error {
// Iterate the map and find the matching error.
for btcdErr, err := range BtcdErrMap {
// Match it against btcd's error.
if matchErrStr(rpcErr, btcdErr) {
return err
}
}

// If not matched, return the original error wrapped.
return fmt.Errorf("%w: %v", ErrUndefined, rpcErr)
}
4 changes: 4 additions & 0 deletions wallet/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,7 @@ func (m *mockChainClient) TestMempoolAccept(txns []*wire.MsgTx,

return nil, nil
}

func (m *mockChainClient) MapRPCErr(err error) error {
return nil
}
18 changes: 5 additions & 13 deletions wallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"github.com/btcsuite/btcd/btcutil/hdkeychain"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/rpcclient"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcwallet/chain"
Expand Down Expand Up @@ -3793,25 +3792,18 @@ func (w *Wallet) publishTransaction(tx *wire.MsgTx) (*chainhash.Hash, error) {
}

txid := tx.TxHash()
_, err = chainClient.SendRawTransaction(tx, false)
if err == nil {
_, rpcErr := chainClient.SendRawTransaction(tx, false)
if rpcErr == nil {
return &txid, nil
}

// Map the error to an RPC-specific error type.
//
// NOTE: all the errors returned here are mapped to an error type
// defined in `rpcclient` package, where the error strings are taken
// from bitcoind.
rpcErr := rpcclient.MapRPCErr(err)

switch {
case errors.Is(rpcErr, rpcclient.ErrTxAlreadyInMempool):
case errors.Is(rpcErr, chain.ErrTxAlreadyInMempool):
log.Infof("%v: tx already in mempool", txid)
return &txid, nil

case errors.Is(rpcErr, rpcclient.ErrTxAlreadyKnown),
errors.Is(rpcErr, rpcclient.ErrTxAlreadyConfirmed):
case errors.Is(rpcErr, chain.ErrTxAlreadyKnown),
errors.Is(rpcErr, chain.ErrTxAlreadyConfirmed):

dbErr := walletdb.Update(w.db, func(dbTx walletdb.ReadWriteTx) error {
txmgrNs := dbTx.ReadWriteBucket(wtxmgrNamespaceKey)
Expand Down

0 comments on commit 4006f9b

Please sign in to comment.