decred.org/dcrdex@v1.0.5/client/asset/btc/rpcclient.go (about)

     1  // This code is available on the terms of the project LICENSE.md file,
     2  // also available online at https://blueoakcouncil.org/license/1.0.0.
     3  
     4  package btc
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"encoding/hex"
    10  	"encoding/json"
    11  	"errors"
    12  	"fmt"
    13  	"strings"
    14  	"sync"
    15  	"sync/atomic"
    16  	"time"
    17  
    18  	"decred.org/dcrdex/client/asset"
    19  	"decred.org/dcrdex/dex"
    20  	"decred.org/dcrdex/dex/config"
    21  	dexbtc "decred.org/dcrdex/dex/networks/btc"
    22  	"github.com/btcsuite/btcd/btcec/v2"
    23  	"github.com/btcsuite/btcd/btcjson"
    24  	"github.com/btcsuite/btcd/btcutil"
    25  	"github.com/btcsuite/btcd/chaincfg"
    26  	"github.com/btcsuite/btcd/chaincfg/chainhash"
    27  	"github.com/btcsuite/btcd/wire"
    28  	"github.com/decred/dcrd/dcrjson/v4" // for dcrjson.RPCError returns from rpcclient
    29  )
    30  
    31  const (
    32  	methodGetBalances          = "getbalances"
    33  	methodGetBalance           = "getbalance"
    34  	methodListUnspent          = "listunspent"
    35  	methodLockUnspent          = "lockunspent"
    36  	methodListLockUnspent      = "listlockunspent"
    37  	methodChangeAddress        = "getrawchangeaddress"
    38  	methodNewAddress           = "getnewaddress"
    39  	methodSignTx               = "signrawtransactionwithwallet"
    40  	methodSignTxLegacy         = "signrawtransaction"
    41  	methodUnlock               = "walletpassphrase"
    42  	methodLock                 = "walletlock"
    43  	methodPrivKeyForAddress    = "dumpprivkey"
    44  	methodGetTransaction       = "gettransaction"
    45  	methodSendToAddress        = "sendtoaddress"
    46  	methodSetTxFee             = "settxfee"
    47  	methodGetWalletInfo        = "getwalletinfo"
    48  	methodGetAddressInfo       = "getaddressinfo"
    49  	methodListDescriptors      = "listdescriptors"
    50  	methodValidateAddress      = "validateaddress"
    51  	methodEstimateSmartFee     = "estimatesmartfee"
    52  	methodSendRawTransaction   = "sendrawtransaction"
    53  	methodGetTxOut             = "gettxout"
    54  	methodGetBlock             = "getblock"
    55  	methodGetBlockHash         = "getblockhash"
    56  	methodGetBestBlockHash     = "getbestblockhash"
    57  	methodGetRawMempool        = "getrawmempool"
    58  	methodGetRawTransaction    = "getrawtransaction"
    59  	methodGetBlockHeader       = "getblockheader"
    60  	methodGetNetworkInfo       = "getnetworkinfo"
    61  	methodGetBlockchainInfo    = "getblockchaininfo"
    62  	methodFundRawTransaction   = "fundrawtransaction"
    63  	methodListSinceBlock       = "listsinceblock"
    64  	methodGetReceivedByAddress = "getreceivedbyaddress"
    65  )
    66  
    67  // IsTxNotFoundErr will return true if the error indicates that the requested
    68  // transaction is not known. The error must be dcrjson.RPCError with a numeric
    69  // code equal to btcjson.ErrRPCNoTxInfo. WARNING: This is specific to errors
    70  // from an RPC to a bitcoind (or clone) using dcrd's rpcclient!
    71  func IsTxNotFoundErr(err error) bool {
    72  	// We are using dcrd's client with Bitcoin Core, so errors will be of type
    73  	// dcrjson.RPCError, but numeric codes should come from btcjson.
    74  	const errRPCNoTxInfo = int(btcjson.ErrRPCNoTxInfo)
    75  	var rpcErr *dcrjson.RPCError
    76  	return errors.As(err, &rpcErr) && int(rpcErr.Code) == errRPCNoTxInfo
    77  }
    78  
    79  // isMethodNotFoundErr will return true if the error indicates that the RPC
    80  // method was not found by the RPC server. The error must be dcrjson.RPCError
    81  // with a numeric code equal to btcjson.ErrRPCMethodNotFound.Code or a message
    82  // containing "method not found".
    83  func isMethodNotFoundErr(err error) bool {
    84  	var errRPCMethodNotFound = int(btcjson.ErrRPCMethodNotFound.Code)
    85  	var rpcErr *dcrjson.RPCError
    86  	return errors.As(err, &rpcErr) &&
    87  		(int(rpcErr.Code) == errRPCMethodNotFound ||
    88  			strings.Contains(strings.ToLower(rpcErr.Message), "method not found"))
    89  }
    90  
    91  // RawRequester defines decred's rpcclient RawRequest func where all RPC
    92  // requests sent through. For testing, it can be satisfied by a stub.
    93  type RawRequester interface {
    94  	RawRequest(context.Context, string, []json.RawMessage) (json.RawMessage, error)
    95  }
    96  
    97  // anylist is a list of RPC parameters to be converted to []json.RawMessage and
    98  // sent via RawRequest.
    99  type anylist []any
   100  
   101  type rpcCore struct {
   102  	rpcConfig         *RPCConfig
   103  	cloneParams       *BTCCloneCFG
   104  	requesterV        atomic.Value // RawRequester
   105  	segwit            bool
   106  	decodeAddr        dexbtc.AddressDecoder
   107  	stringAddr        dexbtc.AddressStringer
   108  	legacyRawSends    bool
   109  	minNetworkVersion uint64
   110  	log               dex.Logger
   111  	chainParams       *chaincfg.Params
   112  	omitAddressType   bool
   113  	legacySignTx      bool
   114  	booleanGetBlock   bool
   115  	unlockSpends      bool
   116  
   117  	deserializeTx      func([]byte) (*wire.MsgTx, error)
   118  	serializeTx        func(*wire.MsgTx) ([]byte, error)
   119  	hashTx             func(*wire.MsgTx) *chainhash.Hash
   120  	numericGetRawTxRPC bool
   121  	manualMedianTime   bool
   122  	addrFunc           func() (btcutil.Address, error)
   123  
   124  	deserializeBlock         func([]byte) (*wire.MsgBlock, error)
   125  	legacyValidateAddressRPC bool
   126  	omitRPCOptionsArg        bool
   127  	privKeyFunc              func(addr string) (*btcec.PrivateKey, error)
   128  }
   129  
   130  func (c *rpcCore) requester() RawRequester {
   131  	return c.requesterV.Load().(RawRequester)
   132  }
   133  
   134  // rpcClient is a bitcoind JSON RPC client that uses rpcclient.Client's
   135  // RawRequest for wallet-related calls.
   136  type rpcClient struct {
   137  	*rpcCore
   138  	ctx         context.Context
   139  	descriptors bool // set on connect like ctx
   140  }
   141  
   142  var _ Wallet = (*rpcClient)(nil)
   143  
   144  // newRPCClient is the constructor for a rpcClient.
   145  func newRPCClient(cfg *rpcCore) *rpcClient {
   146  	return &rpcClient{rpcCore: cfg}
   147  }
   148  
   149  // ChainOK is for screening the chain field of the getblockchaininfo result.
   150  func ChainOK(net dex.Network, str string) bool {
   151  	var chainStr string
   152  	switch net {
   153  	case dex.Mainnet:
   154  		chainStr = "main"
   155  	case dex.Testnet:
   156  		chainStr = "test"
   157  	case dex.Regtest:
   158  		chainStr = "reg"
   159  	}
   160  	return strings.Contains(str, chainStr)
   161  }
   162  
   163  func (wc *rpcClient) Connect(ctx context.Context, _ *sync.WaitGroup) error {
   164  	wc.ctx = ctx
   165  	// Check the version. Do it here, so we can also diagnose a bad connection.
   166  	netVer, codeVer, err := wc.getVersion()
   167  	if err != nil {
   168  		return fmt.Errorf("error getting version: %w", err)
   169  	}
   170  	if netVer < wc.minNetworkVersion {
   171  		return fmt.Errorf("reported node version %d is less than minimum %d", netVer, wc.minNetworkVersion)
   172  	}
   173  	// TODO: codeVer is actually asset-dependent. Zcash, for example, is at
   174  	// 170100. So we're just lucking out here, really.
   175  	if codeVer < minProtocolVersion {
   176  		return fmt.Errorf("node software out of date. version %d is less than minimum %d", codeVer, minProtocolVersion)
   177  	}
   178  	chainInfo, err := wc.getBlockchainInfo()
   179  	if err != nil {
   180  		return fmt.Errorf("getblockchaininfo error: %w", err)
   181  	}
   182  	if !ChainOK(wc.cloneParams.Network, chainInfo.Chain) {
   183  		return errors.New("wrong net")
   184  	}
   185  	wiRes, err := wc.GetWalletInfo()
   186  	if err != nil {
   187  		return fmt.Errorf("getwalletinfo failure: %w", err)
   188  	}
   189  	wc.descriptors = wiRes.Descriptors
   190  	if wc.descriptors {
   191  		if netVer < minDescriptorVersion {
   192  			return fmt.Errorf("reported node version %d is less than minimum %d"+
   193  				" for descriptor wallets", netVer, minDescriptorVersion)
   194  		}
   195  		wc.log.Debug("Using a descriptor wallet.")
   196  	}
   197  	return nil
   198  }
   199  
   200  // Reconfigure attempts to reconfigure the rpcClient for the new settings. Live
   201  // reconfiguration is only attempted if the new wallet type is walletTypeRPC. If
   202  // the special_activelyUsed flag is set, reconfigure will fail if we can't
   203  // validate ownership of the current deposit address.
   204  func (wc *rpcClient) Reconfigure(cfg *asset.WalletConfig, currentAddress string) (restartRequired bool, err error) {
   205  	if cfg.Type != wc.cloneParams.WalletCFG.Type {
   206  		restartRequired = true
   207  		return
   208  	}
   209  	if wc.ctx == nil || wc.ctx.Err() != nil {
   210  		return true, nil // not connected, ok to reconfigure, but restart required
   211  	}
   212  
   213  	parsedCfg := new(RPCWalletConfig)
   214  	if err = config.Unmapify(cfg.Settings, parsedCfg); err != nil {
   215  		return
   216  	}
   217  
   218  	// Check the RPC configuration.
   219  	newCfg := &parsedCfg.RPCConfig
   220  	if err = dexbtc.CheckRPCConfig(&newCfg.RPCConfig, wc.cloneParams.WalletInfo.Name,
   221  		wc.cloneParams.Network, wc.cloneParams.Ports); err != nil {
   222  		return
   223  	}
   224  
   225  	// If the RPC configuration has changed, try to update the client.
   226  	oldCfg := wc.rpcConfig
   227  	if *newCfg != *oldCfg {
   228  		cl, err := newRPCConnection(parsedCfg, wc.cloneParams.SingularWallet)
   229  		if err != nil {
   230  			return false, fmt.Errorf("error creating RPC client with new credentials: %v", err)
   231  		}
   232  
   233  		// Require restart if the wallet does not own or understand our current
   234  		// address. We can't use wc.ownsAddress because the rpcClient still has
   235  		// the old requester stored, so we'll call directly.
   236  		method := methodGetAddressInfo
   237  		if wc.legacyValidateAddressRPC {
   238  			method = methodValidateAddress
   239  		}
   240  		ai := new(GetAddressInfoResult)
   241  		if err := Call(wc.ctx, cl, method, anylist{currentAddress}, ai); err != nil {
   242  			return false, fmt.Errorf("error getting address info with new RPC client: %w", err)
   243  		} else if !ai.IsMine {
   244  			// If the wallet is in active use, check the supplied address.
   245  			if parsedCfg.ActivelyUsed { // deny reconfigure
   246  				return false, errors.New("cannot reconfigure to a new RPC wallet during active use")
   247  			}
   248  			// Allow reconfigure, but restart to trigger dep address refresh and
   249  			// full connect checks, which include the getblockchaininfo check.
   250  			return true, nil
   251  		} // else same wallet, skip full reconnect
   252  
   253  		chainInfo := new(GetBlockchainInfoResult)
   254  		if err := Call(wc.ctx, cl, methodGetBlockchainInfo, nil, chainInfo); err != nil {
   255  			return false, fmt.Errorf("%s: %w", methodGetBlockchainInfo, err)
   256  		}
   257  		if !ChainOK(wc.cloneParams.Network, chainInfo.Chain) {
   258  			return false, errors.New("wrong net")
   259  		}
   260  
   261  		wc.requesterV.Store(cl)
   262  		wc.rpcConfig = newCfg
   263  
   264  		// No restart required
   265  	}
   266  	return
   267  }
   268  
   269  // RawRequest passes the request to the wallet's RawRequester.
   270  func (wc *rpcClient) RawRequest(ctx context.Context, method string, params []json.RawMessage) (json.RawMessage, error) {
   271  	return wc.requester().RawRequest(ctx, method, params)
   272  }
   273  
   274  // estimateSmartFee requests the server to estimate a fee level based on the
   275  // given parameters.
   276  func estimateSmartFee(ctx context.Context, rr RawRequester, confTarget uint64, mode *btcjson.EstimateSmartFeeMode) (*btcjson.EstimateSmartFeeResult, error) {
   277  	res := new(btcjson.EstimateSmartFeeResult)
   278  	return res, Call(ctx, rr, methodEstimateSmartFee, anylist{confTarget, mode}, res)
   279  }
   280  
   281  // SendRawTransactionLegacy broadcasts the transaction with an additional legacy
   282  // boolean `allowhighfees` argument set to false.
   283  func (wc *rpcClient) SendRawTransactionLegacy(tx *wire.MsgTx) (*chainhash.Hash, error) {
   284  	txBytes, err := wc.serializeTx(tx)
   285  	if err != nil {
   286  		return nil, err
   287  	}
   288  	return wc.callHashGetter(methodSendRawTransaction, anylist{
   289  		hex.EncodeToString(txBytes), false})
   290  }
   291  
   292  // SendRawTransaction broadcasts the transaction.
   293  func (wc *rpcClient) SendRawTransaction(tx *wire.MsgTx) (*chainhash.Hash, error) {
   294  	b, err := wc.serializeTx(tx)
   295  	if err != nil {
   296  		return nil, err
   297  	}
   298  	var txid string
   299  	err = wc.call(methodSendRawTransaction, anylist{hex.EncodeToString(b)}, &txid)
   300  	if err != nil {
   301  		return nil, err
   302  	}
   303  	return chainhash.NewHashFromStr(txid)
   304  }
   305  
   306  // sendRawTransaction sends the MsgTx.
   307  func (wc *rpcClient) sendRawTransaction(tx *wire.MsgTx) (txHash *chainhash.Hash, err error) {
   308  	if wc.legacyRawSends {
   309  		txHash, err = wc.SendRawTransactionLegacy(tx)
   310  	} else {
   311  		txHash, err = wc.SendRawTransaction(tx)
   312  	}
   313  	if err != nil {
   314  		return nil, err
   315  	}
   316  	if !wc.unlockSpends {
   317  		return txHash, nil
   318  	}
   319  
   320  	// TODO: lockUnspent should really just take a []*OutPoint, since it doesn't
   321  	// need the value.
   322  	ops := make([]*Output, 0, len(tx.TxIn))
   323  	for _, txIn := range tx.TxIn {
   324  		prevOut := &txIn.PreviousOutPoint
   325  		ops = append(ops, &Output{Pt: NewOutPoint(&prevOut.Hash, prevOut.Index)})
   326  	}
   327  	if err := wc.LockUnspent(true, ops); err != nil {
   328  		wc.log.Warnf("error unlocking spent outputs: %v", err)
   329  	}
   330  	return txHash, nil
   331  }
   332  
   333  // GetTxOut returns the transaction output info if it's unspent and
   334  // nil, otherwise.
   335  func (wc *rpcClient) GetTxOut(txHash *chainhash.Hash, index uint32, _ []byte, _ time.Time) (*wire.TxOut, uint32, error) {
   336  	txOut, err := wc.getTxOutput(txHash, index)
   337  	if err != nil {
   338  		return nil, 0, fmt.Errorf("getTxOut error: %w", err)
   339  	}
   340  	if txOut == nil {
   341  		return nil, 0, nil
   342  	}
   343  	outputScript, _ := hex.DecodeString(txOut.ScriptPubKey.Hex)
   344  	// Check equivalence of pkScript and outputScript?
   345  	return wire.NewTxOut(int64(toSatoshi(txOut.Value)), outputScript), uint32(txOut.Confirmations), nil
   346  }
   347  
   348  // getTxOut returns the transaction output info if it's unspent and
   349  // nil, otherwise.
   350  func (wc *rpcClient) getTxOutput(txHash *chainhash.Hash, index uint32) (*btcjson.GetTxOutResult, error) {
   351  	// Note that we pass to call pointer to a pointer (&res) so that
   352  	// json.Unmarshal can nil the pointer if the method returns the JSON null.
   353  	var res *btcjson.GetTxOutResult
   354  	return res, wc.call(methodGetTxOut, anylist{txHash.String(), index, true},
   355  		&res)
   356  }
   357  
   358  func (wc *rpcClient) callHashGetter(method string, args anylist) (*chainhash.Hash, error) {
   359  	var txid string
   360  	err := wc.call(method, args, &txid)
   361  	if err != nil {
   362  		return nil, err
   363  	}
   364  	return chainhash.NewHashFromStr(txid)
   365  }
   366  
   367  // GetBlock fetches the MsgBlock.
   368  func (wc *rpcClient) GetBlock(h chainhash.Hash) (*wire.MsgBlock, error) {
   369  	var blkB dex.Bytes
   370  	args := anylist{h.String()}
   371  	if wc.booleanGetBlock {
   372  		args = append(args, false)
   373  	} else {
   374  		args = append(args, 0)
   375  	}
   376  	err := wc.call(methodGetBlock, args, &blkB)
   377  	if err != nil {
   378  		return nil, err
   379  	}
   380  
   381  	return wc.deserializeBlock(blkB)
   382  }
   383  
   384  // GetBlockHash returns the hash of the block in the best block chain at the
   385  // given height.
   386  func (wc *rpcClient) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) {
   387  	return wc.callHashGetter(methodGetBlockHash, anylist{blockHeight})
   388  }
   389  
   390  // GetBestBlockHash returns the hash of the best block in the longest block
   391  // chain (aka mainchain).
   392  func (wc *rpcClient) GetBestBlockHash() (*chainhash.Hash, error) {
   393  	return wc.callHashGetter(methodGetBestBlockHash, nil)
   394  }
   395  
   396  // GetBestBlockHeader returns the height of the top mainchain block.
   397  func (wc *rpcClient) GetBestBlockHeader() (*BlockHeader, error) {
   398  	tipHash, err := wc.GetBestBlockHash()
   399  	if err != nil {
   400  		return nil, err
   401  	}
   402  	hdr, _, err := wc.GetBlockHeader(tipHash)
   403  	return hdr, err
   404  }
   405  
   406  // GetBestBlockHeight returns the height of the top mainchain block.
   407  func (wc *rpcClient) GetBestBlockHeight() (int32, error) {
   408  	header, err := wc.GetBestBlockHeader()
   409  	if err != nil {
   410  		return -1, err
   411  	}
   412  	return int32(header.Height), nil
   413  }
   414  
   415  // getChainStamp satisfies chainStamper for manual median time calculations.
   416  func (wc *rpcClient) getChainStamp(blockHash *chainhash.Hash) (stamp time.Time, prevHash *chainhash.Hash, err error) {
   417  	hdr, _, err := wc.GetBlockHeader(blockHash)
   418  	if err != nil {
   419  		return
   420  	}
   421  	prevHash, err = chainhash.NewHashFromStr(hdr.PreviousBlockHash)
   422  	if err != nil {
   423  		return
   424  	}
   425  	return time.Unix(hdr.Time, 0).UTC(), prevHash, nil
   426  }
   427  
   428  // MedianTime is the median time for the current best block.
   429  func (wc *rpcClient) MedianTime() (stamp time.Time, err error) {
   430  	tipHash, err := wc.GetBestBlockHash()
   431  	if err != nil {
   432  		return
   433  	}
   434  	if wc.manualMedianTime {
   435  		return CalcMedianTime(func(blockHash *chainhash.Hash) (stamp time.Time, prevHash *chainhash.Hash, err error) {
   436  			hdr, _, err := wc.GetBlockHeader(blockHash)
   437  			if err != nil {
   438  				return
   439  			}
   440  			prevHash, err = chainhash.NewHashFromStr(hdr.PreviousBlockHash)
   441  			if err != nil {
   442  				return
   443  			}
   444  			return time.Unix(hdr.Time, 0), prevHash, nil
   445  		}, tipHash)
   446  	}
   447  	hdr, err := wc.getRPCBlockHeader(tipHash)
   448  	if err != nil {
   449  		return
   450  	}
   451  	return time.Unix(hdr.MedianTime, 0).UTC(), nil
   452  }
   453  
   454  // GetRawMempool returns the hashes of all transactions in the memory pool.
   455  func (wc *rpcClient) GetRawMempool() ([]*chainhash.Hash, error) {
   456  	var mempool []string
   457  	err := wc.call(methodGetRawMempool, nil, &mempool)
   458  	if err != nil {
   459  		return nil, err
   460  	}
   461  
   462  	// Convert received hex hashes to chainhash.Hash
   463  	hashes := make([]*chainhash.Hash, 0, len(mempool))
   464  	for _, h := range mempool {
   465  		hash, err := chainhash.NewHashFromStr(h)
   466  		if err != nil {
   467  			return nil, err
   468  		}
   469  		hashes = append(hashes, hash)
   470  	}
   471  	return hashes, nil
   472  }
   473  
   474  // GetRawTransaction retrieves the MsgTx.
   475  func (wc *rpcClient) GetRawTransaction(txHash *chainhash.Hash) (*wire.MsgTx, error) {
   476  	var txB dex.Bytes
   477  	args := anylist{txHash.String(), false}
   478  	if wc.numericGetRawTxRPC {
   479  		args[1] = 0
   480  	}
   481  	err := wc.call(methodGetRawTransaction, args, &txB)
   482  	if err != nil {
   483  		return nil, err
   484  	}
   485  
   486  	return wc.deserializeTx(txB)
   487  }
   488  
   489  // Balances retrieves a wallet's balance details.
   490  func (wc *rpcClient) Balances() (*GetBalancesResult, error) {
   491  	var balances GetBalancesResult
   492  	return &balances, wc.call(methodGetBalances, nil, &balances)
   493  }
   494  
   495  // ListUnspent retrieves a list of the wallet's UTXOs.
   496  func (wc *rpcClient) ListUnspent() ([]*ListUnspentResult, error) {
   497  	unspents := make([]*ListUnspentResult, 0)
   498  	// TODO: listunspent 0 9999999 []string{}, include_unsafe=false
   499  	return unspents, wc.call(methodListUnspent, anylist{uint8(0)}, &unspents)
   500  }
   501  
   502  // LockUnspent locks and unlocks outputs for spending. An output that is part of
   503  // an order, but not yet spent, should be locked until spent or until the order
   504  // is canceled or fails.
   505  func (wc *rpcClient) LockUnspent(unlock bool, ops []*Output) error {
   506  	var rpcops []*RPCOutpoint // To clear all, this must be nil->null, not empty slice.
   507  	for _, op := range ops {
   508  		rpcops = append(rpcops, &RPCOutpoint{
   509  			TxID: op.txHash().String(),
   510  			Vout: op.vout(),
   511  		})
   512  	}
   513  	var success bool
   514  	err := wc.call(methodLockUnspent, anylist{unlock, rpcops}, &success)
   515  	if err == nil && !success {
   516  		return fmt.Errorf("lockunspent unsuccessful")
   517  	}
   518  	return err
   519  }
   520  
   521  // ListLockUnspent returns a slice of outpoints for all unspent outputs marked
   522  // as locked by a wallet.
   523  func (wc *rpcClient) ListLockUnspent() ([]*RPCOutpoint, error) {
   524  	var unspents []*RPCOutpoint
   525  	err := wc.call(methodListLockUnspent, nil, &unspents)
   526  	if err != nil {
   527  		return nil, err
   528  	}
   529  	if !wc.unlockSpends {
   530  		return unspents, nil
   531  	}
   532  	// This is quirky wallet software that does not unlock spent outputs, so
   533  	// we'll verify that each output is actually unspent.
   534  	var i int // for in-place filter
   535  	for _, utxo := range unspents {
   536  		var gtxo *btcjson.GetTxOutResult
   537  		err = wc.call(methodGetTxOut, anylist{utxo.TxID, utxo.Vout, true}, &gtxo)
   538  		if err != nil {
   539  			wc.log.Warnf("gettxout(%v:%d): %v", utxo.TxID, utxo.Vout, err)
   540  			continue
   541  		}
   542  		if gtxo != nil {
   543  			unspents[i] = utxo // unspent, keep it
   544  			i++
   545  			continue
   546  		}
   547  		// actually spent, unlock
   548  		var success bool
   549  		op := []*RPCOutpoint{{
   550  			TxID: utxo.TxID,
   551  			Vout: utxo.Vout,
   552  		}}
   553  		err = wc.call(methodLockUnspent, anylist{true, op}, &success)
   554  		if err != nil || !success {
   555  			wc.log.Warnf("lockunspent(unlocking %v:%d): success = %v, err = %v",
   556  				utxo.TxID, utxo.Vout, success, err)
   557  			continue
   558  		}
   559  		wc.log.Debugf("Unlocked spent outpoint %v:%d", utxo.TxID, utxo.Vout)
   560  	}
   561  	unspents = unspents[:i]
   562  	return unspents, nil
   563  }
   564  
   565  // ChangeAddress gets a new internal address from the wallet. The address will
   566  // be bech32-encoded (P2WPKH).
   567  func (wc *rpcClient) ChangeAddress() (btcutil.Address, error) {
   568  	var addrStr string
   569  	var err error
   570  	switch {
   571  	case wc.omitAddressType:
   572  		err = wc.call(methodChangeAddress, nil, &addrStr)
   573  	case wc.segwit:
   574  		err = wc.call(methodChangeAddress, anylist{"bech32"}, &addrStr)
   575  	default:
   576  		err = wc.call(methodChangeAddress, anylist{"legacy"}, &addrStr)
   577  	}
   578  	if err != nil {
   579  		return nil, err
   580  	}
   581  	return wc.decodeAddr(addrStr, wc.chainParams)
   582  }
   583  
   584  func (wc *rpcClient) ExternalAddress() (btcutil.Address, error) {
   585  	if wc.segwit {
   586  		return wc.address("bech32")
   587  	}
   588  	return wc.address("legacy")
   589  }
   590  
   591  // address is used internally for fetching addresses of various types from the
   592  // wallet.
   593  func (wc *rpcClient) address(aType string) (btcutil.Address, error) {
   594  	var addrStr string
   595  	args := anylist{""}
   596  	if !wc.omitAddressType {
   597  		args = append(args, aType)
   598  	}
   599  	err := wc.call(methodNewAddress, args, &addrStr)
   600  	if err != nil {
   601  		return nil, err
   602  	}
   603  	return wc.decodeAddr(addrStr, wc.chainParams) // we should consider returning a string
   604  }
   605  
   606  // SignTx attempts to have the wallet sign the transaction inputs.
   607  func (wc *rpcClient) SignTx(inTx *wire.MsgTx) (*wire.MsgTx, error) {
   608  	txBytes, err := wc.serializeTx(inTx)
   609  	if err != nil {
   610  		return nil, fmt.Errorf("tx serialization error: %w", err)
   611  	}
   612  	res := new(SignTxResult)
   613  	method := methodSignTx
   614  	if wc.legacySignTx {
   615  		method = methodSignTxLegacy
   616  	}
   617  
   618  	err = wc.call(method, anylist{hex.EncodeToString(txBytes)}, res)
   619  	if err != nil {
   620  		return nil, fmt.Errorf("tx signing error: %w", err)
   621  	}
   622  	if !res.Complete {
   623  		sep := ""
   624  		errMsg := ""
   625  		for _, e := range res.Errors {
   626  			errMsg += e.Error + sep
   627  			sep = ";"
   628  		}
   629  		return nil, fmt.Errorf("signing incomplete. %d signing errors encountered: %s", len(res.Errors), errMsg)
   630  	}
   631  	outTx, err := wc.deserializeTx(res.Hex)
   632  	if err != nil {
   633  		return nil, fmt.Errorf("error deserializing transaction response: %w", err)
   634  	}
   635  	return outTx, nil
   636  }
   637  
   638  func (wc *rpcClient) listDescriptors(private bool) (*listDescriptorsResult, error) {
   639  	descriptors := new(listDescriptorsResult)
   640  	return descriptors, wc.call(methodListDescriptors, anylist{private}, descriptors)
   641  }
   642  
   643  func (wc *rpcClient) ListTransactionsSinceBlock(blockHeight int32) ([]*ListTransactionsResult, error) {
   644  	blockHash, err := wc.GetBlockHash(int64(blockHeight))
   645  	if err != nil {
   646  		return nil, fmt.Errorf("getBlockHash error: %w", err)
   647  	}
   648  	result := new(struct {
   649  		Transactions []btcjson.ListTransactionsResult `json:"transactions"`
   650  	})
   651  	err = wc.call(methodListSinceBlock, anylist{blockHash.String()}, result)
   652  	if err != nil {
   653  		return nil, fmt.Errorf("listtransactions error: %w", err)
   654  	}
   655  
   656  	txs := make([]*ListTransactionsResult, 0, len(result.Transactions))
   657  	for _, tx := range result.Transactions {
   658  		var blockHeight uint32
   659  		if tx.BlockHeight != nil {
   660  			blockHeight = uint32(*tx.BlockHeight)
   661  		}
   662  		txs = append(txs, &ListTransactionsResult{
   663  			TxID:        tx.TxID,
   664  			BlockHeight: blockHeight,
   665  			BlockTime:   uint64(tx.BlockTime),
   666  			Fee:         tx.Fee,
   667  			Send:        tx.Category == "send",
   668  		})
   669  	}
   670  
   671  	return txs, nil
   672  }
   673  
   674  // PrivKeyForAddress retrieves the private key associated with the specified
   675  // address.
   676  func (wc *rpcClient) PrivKeyForAddress(addr string) (*btcec.PrivateKey, error) {
   677  	// Use a specialized client's privKey function
   678  	if wc.privKeyFunc != nil {
   679  		return wc.privKeyFunc(addr)
   680  	}
   681  	// Descriptor wallets do not have dumpprivkey.
   682  	if !wc.descriptors {
   683  		var keyHex string
   684  		err := wc.call(methodPrivKeyForAddress, anylist{addr}, &keyHex)
   685  		if err != nil {
   686  			return nil, err
   687  		}
   688  		wif, err := btcutil.DecodeWIF(keyHex)
   689  		if err != nil {
   690  			return nil, err
   691  		}
   692  		return wif.PrivKey, nil
   693  	}
   694  
   695  	// With descriptor wallets, we have to get the address' descriptor from
   696  	// getaddressinfo, parse out its key origin (fingerprint of the master
   697  	// private key followed by derivation path to the address) and the pubkey of
   698  	// the address itself. Then we get the private key using listdescriptors
   699  	// private=true, which returns a set of master private keys and derivation
   700  	// paths, one of which corresponds to the fingerprint and path from
   701  	// getaddressinfo. When the parent master private key is identified, we
   702  	// derive the private key for the address.
   703  	ai := new(GetAddressInfoResult)
   704  	if err := wc.call(methodGetAddressInfo, anylist{addr}, ai); err != nil {
   705  		return nil, fmt.Errorf("getaddressinfo RPC failure: %w", err)
   706  	}
   707  	wc.log.Tracef("Address %v descriptor: %v", addr, ai.Descriptor)
   708  	desc, err := dexbtc.ParseDescriptor(ai.Descriptor)
   709  	if err != nil {
   710  		return nil, fmt.Errorf("failed to parse descriptor %q: %w", ai.Descriptor, err)
   711  	}
   712  	if desc.KeyOrigin == nil {
   713  		return nil, errors.New("address descriptor has no key origin")
   714  	}
   715  	// For addresses from imported private keys that have no derivation path in
   716  	// the key origin, we inspect private keys of type KeyWIFPriv. For addresses
   717  	// with a derivation path, we match KeyExtended private keys based on the
   718  	// master key fingerprint and derivation path.
   719  	fp, addrPath := desc.KeyOrigin.Fingerprint, desc.KeyOrigin.Steps
   720  	// Should match:
   721  	//   fp, path = ai.HDMasterFingerprint, ai.HDKeyPath
   722  	//   addrPath, _, err = dexbtc.ParsePath(path)
   723  	bareKey := len(addrPath) == 0
   724  
   725  	if desc.KeyFmt != dexbtc.KeyHexPub {
   726  		return nil, fmt.Errorf("not a hexadecimal pubkey: %v", desc.Key)
   727  	}
   728  	// The key was validated by ParseDescriptor, but check again.
   729  	addrPubKeyB, err := hex.DecodeString(desc.Key)
   730  	if err != nil {
   731  		return nil, fmt.Errorf("address pubkey not hexadecimal: %w", err)
   732  	}
   733  	addrPubKey, err := btcec.ParsePubKey(addrPubKeyB)
   734  	if err != nil {
   735  		return nil, fmt.Errorf("invalid pubkey for address: %w", err)
   736  	}
   737  	addrPubKeyC := addrPubKey.SerializeCompressed() // may or may not equal addrPubKeyB
   738  
   739  	// Get the private key descriptors.
   740  	masterDescs, err := wc.listDescriptors(true)
   741  	if err != nil {
   742  		return nil, fmt.Errorf("listdescriptors RPC failure: %w", err)
   743  	}
   744  
   745  	// We're going to decode a number of private keys that we need to zero.
   746  	var toClear []interface{ Zero() }
   747  	defer func() {
   748  		for _, k := range toClear {
   749  			k.Zero()
   750  		}
   751  	}() // surprisingly, much cleaner than making the loop body below into a function
   752  	deferZero := func(z interface{ Zero() }) { toClear = append(toClear, z) }
   753  
   754  masters:
   755  	for _, d := range masterDescs.Descriptors {
   756  		masterDesc, err := dexbtc.ParseDescriptor(d.Descriptor)
   757  		if err != nil {
   758  			wc.log.Errorf("Failed to parse descriptor %q: %v", d.Descriptor, err)
   759  			continue // unexpected, but check the others
   760  		}
   761  		if bareKey { // match KeyHexPub -> KeyWIFPriv
   762  			if masterDesc.KeyFmt != dexbtc.KeyWIFPriv {
   763  				continue
   764  			}
   765  			wif, err := btcutil.DecodeWIF(masterDesc.Key)
   766  			if err != nil {
   767  				wc.log.Errorf("Invalid WIF private key: %v", err)
   768  				continue // ParseDescriptor already validated it, so shouldn't happen
   769  			}
   770  			if !bytes.Equal(addrPubKeyC, wif.PrivKey.PubKey().SerializeCompressed()) {
   771  				continue // not the one
   772  			}
   773  			return wif.PrivKey, nil
   774  		}
   775  
   776  		// match KeyHexPub -> [fingerprint/path]KeyExtended
   777  		if masterDesc.KeyFmt != dexbtc.KeyExtended {
   778  			continue
   779  		}
   780  		// Break the key into its parts and compute the fingerprint of the
   781  		// master private key.
   782  		xPriv, fingerprint, pathStr, isRange, err := dexbtc.ParseKeyExtended(masterDesc.Key)
   783  		if err != nil {
   784  			wc.log.Debugf("Failed to parse descriptor extended key: %v", err)
   785  			continue
   786  		}
   787  		deferZero(xPriv)
   788  		if fingerprint != fp {
   789  			continue
   790  		}
   791  		if !xPriv.IsPrivate() { // imported xpub with no private key?
   792  			wc.log.Debugf("Not an extended private key. Fingerprint: %v", fingerprint)
   793  			continue
   794  		}
   795  		// NOTE: After finding the xprv with the matching fingerprint, we could
   796  		// skip to checking the private key for a match instead of first
   797  		// matching the path. Let's just check the path too since fingerprint
   798  		// collision are possible, and the different address types are allowed
   799  		// to use descriptors with different fingerprints.
   800  		if !isRange {
   801  			continue // imported?
   802  		}
   803  		path, _, err := dexbtc.ParsePath(pathStr)
   804  		if err != nil {
   805  			wc.log.Debugf("Failed to parse descriptor extended key path %q: %v", pathStr, err)
   806  			continue
   807  		}
   808  		if len(addrPath) != len(path)+1 { // addrPath includes index of self
   809  			continue
   810  		}
   811  		for i := range path {
   812  			if addrPath[i] != path[i] {
   813  				continue masters // different path
   814  			}
   815  		}
   816  
   817  		// NOTE: We could conceivably cache the extended private key for this
   818  		// address range/branch, but it could be a security risk:
   819  		// childIdx := addrPath[len(addrPath)-1]
   820  		// branch, err := dexbtc.DeepChild(xPriv, path)
   821  		// child, err := branch.Derive(childIdx)
   822  		child, err := dexbtc.DeepChild(xPriv, addrPath)
   823  		if err != nil {
   824  			return nil, fmt.Errorf("address key derivation failed: %v", err) // any point in checking the rest?
   825  		}
   826  		deferZero(child)
   827  		privkey, err := child.ECPrivKey()
   828  		if err != nil { // only errors if the extended key is not private
   829  			return nil, err // hdkeychain.ErrNotPrivExtKey
   830  		}
   831  		// That's the private key, but do a final check that the pubkey matches
   832  		// the "pubkey" field of the getaddressinfo response.
   833  		pubkey := privkey.PubKey().SerializeCompressed()
   834  		if !bytes.Equal(pubkey, addrPubKeyC) {
   835  			wc.log.Warnf("Derived wrong pubkey for address %v from matching descriptor %v: %x != %x",
   836  				addr, d.Descriptor, pubkey, addrPubKey)
   837  			continue // theoretically could be a fingerprint collision (see KeyOrigin docs)
   838  		}
   839  		return privkey, nil
   840  	}
   841  
   842  	return nil, errors.New("no private key found for address")
   843  }
   844  
   845  // GetWalletTransaction retrieves the JSON-RPC gettransaction result.
   846  func (wc *rpcClient) GetWalletTransaction(txHash *chainhash.Hash) (*GetTransactionResult, error) {
   847  	tx := new(GetTransactionResult)
   848  	err := wc.call(methodGetTransaction, anylist{txHash.String()}, tx)
   849  	if err != nil {
   850  		if IsTxNotFoundErr(err) {
   851  			return nil, asset.CoinNotFoundError
   852  		}
   853  		return nil, err
   854  	}
   855  	return tx, nil
   856  }
   857  
   858  // WalletUnlock unlocks the wallet.
   859  func (wc *rpcClient) WalletUnlock(pw []byte) error {
   860  	// 100000000 comes from bitcoin-cli help walletpassphrase
   861  	return wc.call(methodUnlock, anylist{string(pw), 100000000}, nil)
   862  }
   863  
   864  // WalletLock locks the wallet.
   865  func (wc *rpcClient) WalletLock() error {
   866  	return wc.call(methodLock, nil, nil)
   867  }
   868  
   869  // Locked returns the wallet's lock state.
   870  func (wc *rpcClient) Locked() bool {
   871  	walletInfo, err := wc.GetWalletInfo()
   872  	if err != nil {
   873  		wc.log.Errorf("GetWalletInfo error: %w", err)
   874  		return false
   875  	}
   876  	if walletInfo.UnlockedUntil == nil {
   877  		// This wallet is not encrypted.
   878  		return false
   879  	}
   880  
   881  	return time.Unix(*walletInfo.UnlockedUntil, 0).Before(time.Now())
   882  }
   883  
   884  // EstimateSendTxFee returns the fee required to send tx using the provided
   885  // feeRate.
   886  func (wc *rpcClient) EstimateSendTxFee(tx *wire.MsgTx, feeRate uint64, subtract bool) (txfee uint64, err error) {
   887  	txBytes, err := wc.serializeTx(tx)
   888  	if err != nil {
   889  		return 0, fmt.Errorf("tx serialization error: %w", err)
   890  	}
   891  	args := anylist{hex.EncodeToString(txBytes)}
   892  
   893  	// 1e-5 = 1e-8 for satoshis * 1000 for kB.
   894  	feeRateOption := float64(feeRate) / 1e5
   895  	options := &btcjson.FundRawTransactionOpts{
   896  		FeeRate: &feeRateOption,
   897  	}
   898  	if !wc.omitAddressType {
   899  		if wc.segwit {
   900  			options.ChangeType = &btcjson.ChangeTypeBech32
   901  		} else {
   902  			options.ChangeType = &btcjson.ChangeTypeLegacy
   903  		}
   904  	}
   905  	if subtract {
   906  		options.SubtractFeeFromOutputs = []int{0}
   907  	}
   908  	args = append(args, options)
   909  
   910  	var res struct {
   911  		TxBytes dex.Bytes `json:"hex"`
   912  		Fees    float64   `json:"fee"`
   913  	}
   914  	err = wc.call(methodFundRawTransaction, args, &res)
   915  	if err != nil {
   916  		wc.log.Debugf("%s fundrawtranasaction error for args %+v: %v \n", wc.cloneParams.WalletInfo.Name, args, err)
   917  		// This is a work around for ZEC wallet, which does not support options
   918  		// argument for fundrawtransaction.
   919  		if wc.omitRPCOptionsArg {
   920  			var sendAmount uint64
   921  			for _, txOut := range tx.TxOut {
   922  				sendAmount += uint64(txOut.Value)
   923  			}
   924  			var bal float64
   925  			// args: "(dummy)" minconf includeWatchonly inZat
   926  			// Using default inZat = false for compatibility with ZCL.
   927  			if err := wc.call(methodGetBalance, anylist{"", 0, false}, &bal); err != nil {
   928  				return 0, err
   929  			}
   930  			if subtract && sendAmount <= toSatoshi(bal) {
   931  				return 0, errors.New("wallet does not support options")
   932  			}
   933  		}
   934  		return 0, fmt.Errorf("error calculating transaction fee: %w", err)
   935  	}
   936  	return toSatoshi(res.Fees), nil
   937  }
   938  
   939  // GetWalletInfo gets the getwalletinfo RPC result.
   940  func (wc *rpcClient) GetWalletInfo() (*GetWalletInfoResult, error) {
   941  	wi := new(GetWalletInfoResult)
   942  	return wi, wc.call(methodGetWalletInfo, nil, wi)
   943  }
   944  
   945  // Fingerprint returns an identifier for this wallet. Only HD wallets will have
   946  // an identifier. Descriptor wallets will not.
   947  func (wc *rpcClient) Fingerprint() (string, error) {
   948  	walletInfo, err := wc.GetWalletInfo()
   949  	if err != nil {
   950  		return "", err
   951  	}
   952  
   953  	if walletInfo.HdSeedID == "" {
   954  		return "", fmt.Errorf("fingerprint not availble")
   955  	}
   956  
   957  	return walletInfo.HdSeedID, nil
   958  }
   959  
   960  // GetAddressInfo gets information about the given address by calling
   961  // getaddressinfo RPC command.
   962  func (wc *rpcClient) getAddressInfo(addr btcutil.Address, method string) (*GetAddressInfoResult, error) {
   963  	ai := new(GetAddressInfoResult)
   964  	addrStr, err := wc.stringAddr(addr, wc.chainParams)
   965  	if err != nil {
   966  		return nil, err
   967  	}
   968  	return ai, wc.call(method, anylist{addrStr}, ai)
   969  }
   970  
   971  // OwnsAddress indicates if an address belongs to the wallet.
   972  func (wc *rpcClient) OwnsAddress(addr btcutil.Address) (bool, error) {
   973  	method := methodGetAddressInfo
   974  	if wc.legacyValidateAddressRPC {
   975  		method = methodValidateAddress
   976  	}
   977  	ai, err := wc.getAddressInfo(addr, method)
   978  	if err != nil {
   979  		return false, err
   980  	}
   981  	return ai.IsMine, nil
   982  }
   983  
   984  // SyncStatus is information about the blockchain sync status.
   985  func (wc *rpcClient) SyncStatus() (*asset.SyncStatus, error) {
   986  	chainInfo, err := wc.getBlockchainInfo()
   987  	if err != nil {
   988  		return nil, fmt.Errorf("getblockchaininfo error: %w", err)
   989  	}
   990  	synced := !chainInfo.Syncing()
   991  	return &asset.SyncStatus{
   992  		Synced:       synced,
   993  		TargetHeight: uint64(chainInfo.Headers),
   994  		Blocks:       uint64(chainInfo.Blocks),
   995  	}, nil
   996  }
   997  
   998  // SwapConfirmations gets the number of confirmations for the specified coin ID
   999  // by first checking for a unspent output, and if not found, searching indexed
  1000  // wallet transactions.
  1001  func (wc *rpcClient) SwapConfirmations(txHash *chainhash.Hash, vout uint32, _ []byte, _ time.Time) (confs uint32, spent bool, err error) {
  1002  	// Check for an unspent output.
  1003  	txOut, err := wc.getTxOutput(txHash, vout)
  1004  	if err == nil && txOut != nil {
  1005  		return uint32(txOut.Confirmations), false, nil
  1006  	}
  1007  	// Check wallet transactions.
  1008  	tx, err := wc.GetWalletTransaction(txHash)
  1009  	if err != nil {
  1010  		if IsTxNotFoundErr(err) {
  1011  			return 0, false, asset.CoinNotFoundError
  1012  		}
  1013  		return 0, false, err
  1014  	}
  1015  	return uint32(tx.Confirmations), true, nil
  1016  }
  1017  
  1018  // getBlockHeader gets the *rpcBlockHeader for the specified block hash.
  1019  func (wc *rpcClient) getRPCBlockHeader(blockHash *chainhash.Hash) (*BlockHeader, error) {
  1020  	blkHeader := new(BlockHeader)
  1021  	err := wc.call(methodGetBlockHeader,
  1022  		anylist{blockHash.String(), true}, blkHeader)
  1023  	if err != nil {
  1024  		return nil, err
  1025  	}
  1026  
  1027  	return blkHeader, nil
  1028  }
  1029  
  1030  // GetBlockHeader gets the *BlockHeader for the specified block hash. It also
  1031  // returns a bool value to indicate whether this block is a part of main chain.
  1032  // For orphaned blocks header.Confirmations is negative (typically -1).
  1033  func (wc *rpcClient) GetBlockHeader(blockHash *chainhash.Hash) (header *BlockHeader, mainchain bool, err error) {
  1034  	hdr, err := wc.getRPCBlockHeader(blockHash)
  1035  	if err != nil {
  1036  		return nil, false, err
  1037  	}
  1038  	// RPC wallet must return negative confirmations number for orphaned blocks.
  1039  	mainchain = hdr.Confirmations >= 0
  1040  	return hdr, mainchain, nil
  1041  }
  1042  
  1043  // GetBlockHeight gets the mainchain height for the specified block. Returns
  1044  // error for orphaned blocks.
  1045  func (wc *rpcClient) GetBlockHeight(blockHash *chainhash.Hash) (int32, error) {
  1046  	hdr, _, err := wc.GetBlockHeader(blockHash)
  1047  	if err != nil {
  1048  		return -1, err
  1049  	}
  1050  	if hdr.Height < 0 {
  1051  		return -1, fmt.Errorf("block is not a mainchain block")
  1052  	}
  1053  	return int32(hdr.Height), nil
  1054  }
  1055  
  1056  func (wc *rpcClient) PeerCount() (uint32, error) {
  1057  	var r struct {
  1058  		Connections uint32 `json:"connections"`
  1059  	}
  1060  	err := wc.call(methodGetNetworkInfo, nil, &r)
  1061  	if err != nil {
  1062  		return 0, err
  1063  	}
  1064  	return r.Connections, nil
  1065  }
  1066  
  1067  // getBlockchainInfo sends the getblockchaininfo request and returns the result.
  1068  func (wc *rpcClient) getBlockchainInfo() (*GetBlockchainInfoResult, error) {
  1069  	chainInfo := new(GetBlockchainInfoResult)
  1070  	err := wc.call(methodGetBlockchainInfo, nil, chainInfo)
  1071  	if err != nil {
  1072  		return nil, err
  1073  	}
  1074  	return chainInfo, nil
  1075  }
  1076  
  1077  // getVersion gets the current BTC network and protocol versions.
  1078  func (wc *rpcClient) getVersion() (uint64, uint64, error) {
  1079  	r := &struct {
  1080  		Version         uint64 `json:"version"`
  1081  		SubVersion      string `json:"subversion"`
  1082  		ProtocolVersion uint64 `json:"protocolversion"`
  1083  	}{}
  1084  	err := wc.call(methodGetNetworkInfo, nil, r)
  1085  	if err != nil {
  1086  		return 0, 0, err
  1087  	}
  1088  	// TODO: We might consider checking getnetworkinfo's "subversion" field,
  1089  	// which is something like "/Satoshi:24.0.1/".
  1090  	wc.log.Debugf("Node at %v reports subversion \"%v\"", wc.rpcConfig.RPCBind, r.SubVersion)
  1091  	return r.Version, r.ProtocolVersion, nil
  1092  }
  1093  
  1094  // FindRedemptionsInMempool attempts to find spending info for the specified
  1095  // contracts by searching every input of all txs in the mempool.
  1096  func (wc *rpcClient) FindRedemptionsInMempool(ctx context.Context, reqs map[OutPoint]*FindRedemptionReq) (discovered map[OutPoint]*FindRedemptionResult) {
  1097  	return FindRedemptionsInMempool(ctx, wc.log, reqs, wc.GetRawMempool, wc.GetRawTransaction, wc.segwit, wc.hashTx, wc.chainParams)
  1098  }
  1099  
  1100  func FindRedemptionsInMempool(
  1101  	ctx context.Context,
  1102  	log dex.Logger,
  1103  	reqs map[OutPoint]*FindRedemptionReq,
  1104  	getMempool func() ([]*chainhash.Hash, error),
  1105  	getTx func(txHash *chainhash.Hash) (*wire.MsgTx, error),
  1106  	segwit bool,
  1107  	hashTx func(*wire.MsgTx) *chainhash.Hash,
  1108  	chainParams *chaincfg.Params,
  1109  
  1110  ) (discovered map[OutPoint]*FindRedemptionResult) {
  1111  	contractsCount := len(reqs)
  1112  	log.Debugf("finding redemptions for %d contracts in mempool", contractsCount)
  1113  
  1114  	discovered = make(map[OutPoint]*FindRedemptionResult, len(reqs))
  1115  
  1116  	var totalFound, totalCanceled int
  1117  	logAbandon := func(reason string) {
  1118  		// Do not remove the contracts from the findRedemptionQueue
  1119  		// as they could be subsequently redeemed in some mined tx(s),
  1120  		// which would be captured when a new tip is reported.
  1121  		if totalFound+totalCanceled > 0 {
  1122  			log.Debugf("%d redemptions found, %d canceled out of %d contracts in mempool",
  1123  				totalFound, totalCanceled, contractsCount)
  1124  		}
  1125  		log.Errorf("abandoning mempool redemption search for %d contracts because of %s",
  1126  			contractsCount-totalFound-totalCanceled, reason)
  1127  	}
  1128  
  1129  	mempoolTxs, err := getMempool()
  1130  	if err != nil {
  1131  		logAbandon(fmt.Sprintf("error retrieving transactions: %v", err))
  1132  		return
  1133  	}
  1134  
  1135  	for _, txHash := range mempoolTxs {
  1136  		if ctx.Err() != nil {
  1137  			return nil
  1138  		}
  1139  		tx, err := getTx(txHash)
  1140  		if err != nil {
  1141  			logAbandon(fmt.Sprintf("getrawtransaction error for tx hash %v: %v", txHash, err))
  1142  			return
  1143  		}
  1144  		newlyDiscovered := FindRedemptionsInTxWithHasher(ctx, segwit, reqs, tx, chainParams, hashTx)
  1145  		for outPt, res := range newlyDiscovered {
  1146  			discovered[outPt] = res
  1147  		}
  1148  
  1149  	}
  1150  	return
  1151  }
  1152  
  1153  // SearchBlockForRedemptions attempts to find spending info for the specified
  1154  // contracts by searching every input of all txs in the provided block range.
  1155  func (wc *rpcClient) SearchBlockForRedemptions(ctx context.Context, reqs map[OutPoint]*FindRedemptionReq, blockHash chainhash.Hash) (discovered map[OutPoint]*FindRedemptionResult) {
  1156  	msgBlock, err := wc.GetBlock(blockHash)
  1157  	if err != nil {
  1158  		wc.log.Errorf("RPC GetBlock error: %v", err)
  1159  		return
  1160  	}
  1161  	return SearchBlockForRedemptions(ctx, reqs, msgBlock, wc.segwit, wc.hashTx, wc.chainParams)
  1162  }
  1163  
  1164  func SearchBlockForRedemptions(
  1165  	ctx context.Context,
  1166  	reqs map[OutPoint]*FindRedemptionReq,
  1167  	msgBlock *wire.MsgBlock,
  1168  	segwit bool,
  1169  	hashTx func(*wire.MsgTx) *chainhash.Hash,
  1170  	chainParams *chaincfg.Params,
  1171  ) (discovered map[OutPoint]*FindRedemptionResult) {
  1172  
  1173  	discovered = make(map[OutPoint]*FindRedemptionResult, len(reqs))
  1174  
  1175  	for _, msgTx := range msgBlock.Transactions {
  1176  		newlyDiscovered := FindRedemptionsInTxWithHasher(ctx, segwit, reqs, msgTx, chainParams, hashTx)
  1177  		for outPt, res := range newlyDiscovered {
  1178  			discovered[outPt] = res
  1179  		}
  1180  	}
  1181  	return
  1182  }
  1183  
  1184  func (wc *rpcClient) AddressUsed(addr string) (bool, error) {
  1185  	var recv float64
  1186  	const minConf = 0
  1187  	if err := wc.call(methodGetReceivedByAddress, []any{addr, minConf}, &recv); err != nil {
  1188  		return false, err
  1189  	}
  1190  	return recv != 0, nil
  1191  }
  1192  
  1193  // call is used internally to marshal parameters and send requests to the RPC
  1194  // server via (*rpcclient.Client).RawRequest. If thing is non-nil, the result
  1195  // will be marshaled into thing.
  1196  func (wc *rpcClient) call(method string, args anylist, thing any) error {
  1197  	return Call(wc.ctx, wc.requester(), method, args, thing)
  1198  }
  1199  
  1200  func Call(ctx context.Context, r RawRequester, method string, args anylist, thing any) error {
  1201  	params := make([]json.RawMessage, 0, len(args))
  1202  	for i := range args {
  1203  		p, err := json.Marshal(args[i])
  1204  		if err != nil {
  1205  			return err
  1206  		}
  1207  		params = append(params, p)
  1208  	}
  1209  
  1210  	b, err := r.RawRequest(ctx, method, params)
  1211  	if err != nil {
  1212  		return fmt.Errorf("rawrequest (%v) error: %w", method, err)
  1213  	}
  1214  	if thing != nil {
  1215  		return json.Unmarshal(b, thing)
  1216  	}
  1217  	return nil
  1218  }