Skip to content

Commit

Permalink
btc: with dcrd's rpcclient, errors are dcrjson.RPCError
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
chappjc committed Jul 23, 2021
1 parent 6231cf4 commit 03f9973
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 18 deletions.
28 changes: 21 additions & 7 deletions client/asset/btc/btc.go
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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?")
}

Expand Down Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion client/asset/btc/rpcclient.go
Expand Up @@ -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)
}
Expand Down
4 changes: 2 additions & 2 deletions dex/testing/btc/base-harness.sh
Expand Up @@ -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
Expand All @@ -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
Expand Down
15 changes: 8 additions & 7 deletions server/asset/btc/btc.go
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion server/asset/btc/rpcclient.go
Expand Up @@ -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()
Expand Down

0 comments on commit 03f9973

Please sign in to comment.