From 03f9973b77f69f0c8ac0527cc1f7f6e5e15f34ad Mon Sep 17 00:00:00 2001 From: Jonathan Chappelow Date: Thu, 15 Jul 2021 15:06:05 -0500 Subject: [PATCH] btc: with dcrd's rpcclient, errors are dcrjson.RPCError With both client and server btc asset backends using dcrd's rpcclient, the error type from all methods including RawRequest would be dcrjson.RPCError not btcjson.RPCError. The numeric codes will still pertain the btcjson constants, even though the dcrjson.RPCError.Code field is of type dcrjson.RPCErrorCode. Thus, error matching first interprets the error as a dcrjson.RPCError, and then compares the int representation of Code to the expected int code form btcjson. --- client/asset/btc/btc.go | 28 +++++++++++++++++++++------- client/asset/btc/rpcclient.go | 2 +- dex/testing/btc/base-harness.sh | 4 ++-- server/asset/btc/btc.go | 15 ++++++++------- server/asset/btc/rpcclient.go | 3 ++- 5 files changed, 34 insertions(+), 18 deletions(-) diff --git a/client/asset/btc/btc.go b/client/asset/btc/btc.go index c54a642002..a1b107638d 100644 --- a/client/asset/btc/btc.go +++ b/client/asset/btc/btc.go @@ -30,6 +30,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" + "github.com/decred/dcrd/dcrjson/v3" // for dcrjson.RPCError returns from rpcclient "github.com/decred/dcrd/rpcclient/v7" ) @@ -588,10 +589,7 @@ func (btc *ExchangeWallet) Connect(ctx context.Context) (*sync.WaitGroup, error) } // Check for method unknown error for feeRate method. _, err = btc.estimateFee(ctx, btc.node.requester, 1) - var rpcErr *btcjson.RPCError - if errors.As(err, &rpcErr) && - (rpcErr.Code == btcjson.ErrRPCMethodNotFound.Code || rpcErr.Message == "Method not found") { - + if isMethodNotFoundErr(err) { return nil, fmt.Errorf("fee estimation method not found. Are you configured for the correct RPC?") } @@ -2797,10 +2795,26 @@ func decodeCoinID(coinID dex.Bytes) (*chainhash.Hash, uint32, error) { } // isTxNotFoundErr will return true if the error indicates that the requested -// transaction is not known. +// transaction is not known. The error must be dcrjson.RPCError with a numeric +// code equal to btcjson.ErrRPCNoTxInfo. func isTxNotFoundErr(err error) bool { - var rpcErr *btcjson.RPCError - return errors.As(err, &rpcErr) && rpcErr.Code == btcjson.ErrRPCInvalidAddressOrKey + // We are using dcrd's client with Bitcoin Core, so errors will be of type + // dcrjson.RPCError, but numeric codes should come from btcjson. + const errRPCNoTxInfo = int(btcjson.ErrRPCNoTxInfo) + var rpcErr *dcrjson.RPCError + return errors.As(err, &rpcErr) && int(rpcErr.Code) == errRPCNoTxInfo +} + +// isMethodNotFoundErr will return true if the error indicates that the RPC +// method was not found by the RPC server. The error must be dcrjson.RPCError +// with a numeric code equal to btcjson.ErrRPCMethodNotFound.Code or a message +// containing "method not found". +func isMethodNotFoundErr(err error) bool { + var errRPCMethodNotFound = int(btcjson.ErrRPCMethodNotFound.Code) + var rpcErr *dcrjson.RPCError + return errors.As(err, &rpcErr) && + (int(rpcErr.Code) == errRPCMethodNotFound || + strings.Contains(strings.ToLower(rpcErr.Message), "method not found")) } // toBTC returns a float representation in conventional units for the sats. diff --git a/client/asset/btc/rpcclient.go b/client/asset/btc/rpcclient.go index 5b25cd2145..49fae14335 100644 --- a/client/asset/btc/rpcclient.go +++ b/client/asset/btc/rpcclient.go @@ -47,7 +47,7 @@ const ( // RawRequester is for sending context-aware RPC requests, and has methods for // shutting down the underlying connection. For testing, it can be satisfied -// by a stub. +// by a stub. The returned error should be of type dcrjson.RPCError if non-nil. type RawRequester interface { RawRequest(context.Context, string, []json.RawMessage) (json.RawMessage, error) } diff --git a/dex/testing/btc/base-harness.sh b/dex/testing/btc/base-harness.sh index abbc14b3b8..f88f7ec7b2 100755 --- a/dex/testing/btc/base-harness.sh +++ b/dex/testing/btc/base-harness.sh @@ -79,7 +79,7 @@ tmux send-keys -t $SESSION:0 "set +o history" C-m tmux send-keys -t $SESSION:0 "cd ${ALPHA_DIR}" C-m echo "Starting simnet alpha node" tmux send-keys -t $SESSION:0 "${DAEMON} -rpcuser=user -rpcpassword=pass \ - -rpcport=${ALPHA_RPC_PORT} -datadir=${ALPHA_DIR} \ + -rpcport=${ALPHA_RPC_PORT} -debug=rpc -datadir=${ALPHA_DIR} \ -whitelist=127.0.0.0/8 -whitelist=::1 \ -txindex=1 -regtest=1 -port=${ALPHA_LISTEN_PORT} -fallbackfee=0.00001 \ ${EXTRA_ARGS}; tmux wait-for -S alpha${SYMBOL}" C-m @@ -95,7 +95,7 @@ tmux send-keys -t $SESSION:1 "cd ${BETA_DIR}" C-m echo "Starting simnet beta node" tmux send-keys -t $SESSION:1 "${DAEMON} -rpcuser=user -rpcpassword=pass \ - -rpcport=${BETA_RPC_PORT} -datadir=${BETA_DIR} -txindex=1 -regtest=1 \ + -rpcport=${BETA_RPC_PORT} -debug=rpc -datadir=${BETA_DIR} -txindex=1 -regtest=1 \ -whitelist=127.0.0.0/8 -whitelist=::1 \ -port=${BETA_LISTEN_PORT} -fallbackfee=0.00001 ${EXTRA_ARGS}; \ tmux wait-for -S beta${SYMBOL}" C-m diff --git a/server/asset/btc/btc.go b/server/asset/btc/btc.go index b79b79b7a7..6b3cfad998 100644 --- a/server/asset/btc/btc.go +++ b/server/asset/btc/btc.go @@ -24,6 +24,7 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" + "github.com/decred/dcrd/dcrjson/v3" // for dcrjson.RPCError returns from rpcclient "github.com/decred/dcrd/rpcclient/v7" ) @@ -719,11 +720,8 @@ func (btc *Backend) transaction(txHash *chainhash.Hash, verboseTx *btcjson.TxRaw func (btc *Backend) getTxOutInfo(txHash *chainhash.Hash, vout uint32) (*btcjson.GetTxOutResult, *btcjson.TxRawResult, []byte, error) { txOut, err := btc.node.GetTxOut(txHash, vout, true) if err != nil { - var rpcErr *btcjson.RPCError - if errors.As(err, &rpcErr) { - if rpcErr.Code == btcjson.ErrRPCInvalidAddressOrKey { - return nil, nil, nil, asset.CoinNotFoundError - } + if isTxNotFoundErr(err) { + return nil, nil, nil, asset.CoinNotFoundError } return nil, nil, nil, fmt.Errorf("GetTxOut error for output %s:%d: %w", txHash, vout, err) } @@ -974,8 +972,11 @@ func toSat(v float64) uint64 { // isTxNotFoundErr will return true if the error indicates that the requested // transaction is not known. func isTxNotFoundErr(err error) bool { - var rpcErr *btcjson.RPCError - return errors.As(err, &rpcErr) && rpcErr.Code == btcjson.ErrRPCInvalidAddressOrKey + // We are using dcrd's client with Bitcoin Core, so errors will be of type + // dcrjson.RPCError, but numeric codes should come from btcjson. + const errRPCNoTxInfo = int(btcjson.ErrRPCNoTxInfo) + var rpcErr *dcrjson.RPCError + return errors.As(err, &rpcErr) && int(rpcErr.Code) == errRPCNoTxInfo } // feeRate returns the current optimal fee rate in sat / byte using the diff --git a/server/asset/btc/rpcclient.go b/server/asset/btc/rpcclient.go index 4c123da38a..52dc713447 100644 --- a/server/asset/btc/rpcclient.go +++ b/server/asset/btc/rpcclient.go @@ -23,7 +23,8 @@ const ( ) // RawRequester is for sending context-aware RPC requests, and has methods for -// shutting down the underlying connection. +// shutting down the underlying connection. The returned error should be of type +// dcrjson.RPCError if non-nil. type RawRequester interface { RawRequest(context.Context, string, []json.RawMessage) (json.RawMessage, error) Shutdown()