Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions client/asset/bch/spv.go
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,18 @@ func (w *bchSPVWallet) RemovePeer(addr string) error {
return w.peerManager.RemovePeer(addr)
}

func (w *bchSPVWallet) TotalReceivedForAddr(btcAddr btcutil.Address, minConf int32) (btcutil.Amount, error) {
bchAddr, err := dexbch.BTCAddrToBCHAddr(btcAddr, w.btcParams)
if err != nil {
return 0, err
}
amt, err := w.Wallet.TotalReceivedForAddr(bchAddr, 0)
if err != nil {
return 0, err
}
return btcutil.Amount(amt), nil
}

// secretSource is used to locate keys and redemption scripts while signing a
// transaction. secretSource satisfies the txauthor.SecretsSource interface.
type secretSource struct {
Expand Down
6 changes: 6 additions & 0 deletions client/asset/btc/btc.go
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,7 @@ var _ asset.Authenticator = (*ExchangeWalletAccelerator)(nil)
var _ asset.Authenticator = (*ExchangeWalletCustom)(nil)
var _ asset.AddressReturner = (*baseWallet)(nil)
var _ asset.WalletHistorian = (*ExchangeWalletSPV)(nil)
var _ asset.NewAddresser = (*baseWallet)(nil)

// RecoveryCfg is the information that is transferred from the old wallet
// to the new one when the wallet is recovered.
Expand Down Expand Up @@ -4494,6 +4495,11 @@ func (btc *baseWallet) NewAddress() (string, error) {
return btc.DepositAddress()
}

// AddressUsed checks if a wallet address has been used.
func (btc *baseWallet) AddressUsed(addrStr string) (bool, error) {
return btc.node.AddressUsed(addrStr)
}

// Withdraw withdraws funds to the specified address. Fees are subtracted from
// the value. feeRate is in units of sats/byte.
// Withdraw satisfies asset.Withdrawer.
Expand Down
8 changes: 8 additions & 0 deletions client/asset/btc/electrum_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1211,3 +1211,11 @@ func (ew *electrumWallet) findOutputSpender(ctx context.Context, txHash *chainha

return nil, 0, nil // caller should check msgTx (internal method)
}

func (ew *electrumWallet) AddressUsed(addrStr string) (bool, error) {
txs, err := ew.wallet.GetAddressHistory(ew.ctx, addrStr)
if err != nil {
return false, fmt.Errorf("error getting address history: %w", err)
}
return len(txs) > 0, nil
}
74 changes: 42 additions & 32 deletions client/asset/btc/rpcclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,38 +29,39 @@ import (
)

const (
methodGetBalances = "getbalances"
methodGetBalance = "getbalance"
methodListUnspent = "listunspent"
methodLockUnspent = "lockunspent"
methodListLockUnspent = "listlockunspent"
methodChangeAddress = "getrawchangeaddress"
methodNewAddress = "getnewaddress"
methodSignTx = "signrawtransactionwithwallet"
methodSignTxLegacy = "signrawtransaction"
methodUnlock = "walletpassphrase"
methodLock = "walletlock"
methodPrivKeyForAddress = "dumpprivkey"
methodGetTransaction = "gettransaction"
methodSendToAddress = "sendtoaddress"
methodSetTxFee = "settxfee"
methodGetWalletInfo = "getwalletinfo"
methodGetAddressInfo = "getaddressinfo"
methodListDescriptors = "listdescriptors"
methodValidateAddress = "validateaddress"
methodEstimateSmartFee = "estimatesmartfee"
methodSendRawTransaction = "sendrawtransaction"
methodGetTxOut = "gettxout"
methodGetBlock = "getblock"
methodGetBlockHash = "getblockhash"
methodGetBestBlockHash = "getbestblockhash"
methodGetRawMempool = "getrawmempool"
methodGetRawTransaction = "getrawtransaction"
methodGetBlockHeader = "getblockheader"
methodGetNetworkInfo = "getnetworkinfo"
methodGetBlockchainInfo = "getblockchaininfo"
methodFundRawTransaction = "fundrawtransaction"
methodListSinceBlock = "listsinceblock"
methodGetBalances = "getbalances"
methodGetBalance = "getbalance"
methodListUnspent = "listunspent"
methodLockUnspent = "lockunspent"
methodListLockUnspent = "listlockunspent"
methodChangeAddress = "getrawchangeaddress"
methodNewAddress = "getnewaddress"
methodSignTx = "signrawtransactionwithwallet"
methodSignTxLegacy = "signrawtransaction"
methodUnlock = "walletpassphrase"
methodLock = "walletlock"
methodPrivKeyForAddress = "dumpprivkey"
methodGetTransaction = "gettransaction"
methodSendToAddress = "sendtoaddress"
methodSetTxFee = "settxfee"
methodGetWalletInfo = "getwalletinfo"
methodGetAddressInfo = "getaddressinfo"
methodListDescriptors = "listdescriptors"
methodValidateAddress = "validateaddress"
methodEstimateSmartFee = "estimatesmartfee"
methodSendRawTransaction = "sendrawtransaction"
methodGetTxOut = "gettxout"
methodGetBlock = "getblock"
methodGetBlockHash = "getblockhash"
methodGetBestBlockHash = "getbestblockhash"
methodGetRawMempool = "getrawmempool"
methodGetRawTransaction = "getrawtransaction"
methodGetBlockHeader = "getblockheader"
methodGetNetworkInfo = "getnetworkinfo"
methodGetBlockchainInfo = "getblockchaininfo"
methodFundRawTransaction = "fundrawtransaction"
methodListSinceBlock = "listsinceblock"
methodGetReceivedByAddress = "getreceivedbyaddress"
)

// IsTxNotFoundErr will return true if the error indicates that the requested
Expand Down Expand Up @@ -1180,6 +1181,15 @@ func SearchBlockForRedemptions(
return
}

func (wc *rpcClient) AddressUsed(addr string) (bool, error) {
var recv float64
const minConf = 0
if err := wc.call(methodGetReceivedByAddress, []any{addr, minConf}, &recv); err != nil {
return false, err
}
return recv != 0, nil
}

// call is used internally to marshal parameters and send requests to the RPC
// server via (*rpcclient.Client).RawRequest. If thing is non-nil, the result
// will be marshaled into thing.
Expand Down
4 changes: 4 additions & 0 deletions client/asset/btc/spv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,10 @@ func (c *tBtcWallet) RemovePeer(string) error {
return nil
}

func (c *tBtcWallet) TotalReceivedForAddr(addr btcutil.Address, minConf int32) (btcutil.Amount, error) {
return 0, nil
}

type tNeutrinoClient struct {
*testData
}
Expand Down
21 changes: 21 additions & 0 deletions client/asset/btc/spv_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@ type BTCWallet interface {
AddPeer(string) error
RemovePeer(string) error
GetTransactions(startHeight, endHeight int32, accountName string, cancel <-chan struct{}) (*wallet.GetTransactionsResult, error)
ListSinceBlock(start, end, syncHeight int32) ([]btcjson.ListTransactionsResult, error)
TotalReceivedForAddr(addr btcutil.Address, minConf int32) (btcutil.Amount, error)
}

type XCWalletAccount struct {
AccountName string
AccountNumber uint32
}

// BlockNotification is block hash and height delivered by a BTCWallet when it
Expand Down Expand Up @@ -1309,6 +1316,20 @@ func (w *spvWallet) GetWalletTransaction(txHash *chainhash.Hash) (*GetTransactio
*/
}

func (w *spvWallet) AddressUsed(addrStr string) (bool, error) {
addr, err := w.decodeAddr(addrStr, w.chainParams)
if err != nil {
return false, fmt.Errorf("error decoding address: %w", err)
}

const minConfs = 0
amt, err := w.wallet.TotalReceivedForAddr(addr, minConfs)
if err != nil {
return false, fmt.Errorf("error getting address received: %v", err)
}
return amt != 0, nil
}

func confirms(txHeight, curHeight int32) int32 {
switch {
case txHeight == -1, txHeight > curHeight:
Expand Down
1 change: 1 addition & 0 deletions client/asset/btc/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type Wallet interface {
Reconfigure(walletCfg *asset.WalletConfig, currentAddress string) (restartRequired bool, err error)
Fingerprint() (string, error)
ListTransactionsSinceBlock(blockHeight int32) ([]*ListTransactionsResult, error)
AddressUsed(addr string) (bool, error)
}

type TipRedemptionWallet interface {
Expand Down
6 changes: 6 additions & 0 deletions client/asset/dcr/dcr.go
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,7 @@ var _ asset.Bonder = (*ExchangeWallet)(nil)
var _ asset.Authenticator = (*ExchangeWallet)(nil)
var _ asset.TicketBuyer = (*ExchangeWallet)(nil)
var _ asset.WalletHistorian = (*ExchangeWallet)(nil)
var _ asset.NewAddresser = (*ExchangeWallet)(nil)

type block struct {
height int64
Expand Down Expand Up @@ -4127,6 +4128,11 @@ func (dcr *ExchangeWallet) NewAddress() (string, error) {
return dcr.DepositAddress()
}

// AddressUsed checks if a wallet address has been used.
func (dcr *ExchangeWallet) AddressUsed(addrStr string) (bool, error) {
return dcr.wallet.AddressUsed(dcr.ctx, addrStr)
}

// Unlock unlocks the exchange wallet.
func (dcr *ExchangeWallet) Unlock(pw []byte) error {
// Older SPV wallet potentially need an upgrade while we have a password.
Expand Down
4 changes: 4 additions & 0 deletions client/asset/dcr/dcr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,10 @@ func (c *tRPCClient) SetTxFee(ctx context.Context, fee dcrutil.Amount) error {
return nil
}

func (c *tRPCClient) GetReceivedByAddressMinConf(ctx context.Context, address stdaddr.Address, minConfs int) (dcrutil.Amount, error) {
return 0, nil
}

func (c *tRPCClient) ListSinceBlock(ctx context.Context, hash *chainhash.Hash) (*walletjson.ListSinceBlockResult, error) {
return nil, nil
}
Expand Down
14 changes: 14 additions & 0 deletions client/asset/dcr/rpcwallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ type rpcClient interface {
SetVoteChoice(ctx context.Context, agendaID, choiceID string) error
SetTxFee(ctx context.Context, fee dcrutil.Amount) error
ListSinceBlock(ctx context.Context, hash *chainhash.Hash) (*walletjson.ListSinceBlockResult, error)
GetReceivedByAddressMinConf(ctx context.Context, address stdaddr.Address, minConfs int) (dcrutil.Amount, error)
}

// newRPCWallet creates an rpcClient and uses it to construct a new instance
Expand Down Expand Up @@ -1217,6 +1218,19 @@ func (w *rpcWallet) SetTxFee(ctx context.Context, feePerKB dcrutil.Amount) error
return w.rpcClient.SetTxFee(ctx, feePerKB)
}

func (w *rpcWallet) AddressUsed(ctx context.Context, addrStr string) (bool, error) {
addr, err := stdaddr.DecodeAddress(addrStr, w.chainParams)
if err != nil {
return false, err
}
const minConf = 0
recv, err := w.rpcClient.GetReceivedByAddressMinConf(ctx, addr, minConf)
if err != nil {
return false, err
}
return recv != 0, nil
}

// anylist is a list of RPC parameters to be converted to []json.RawMessage and
// sent via nodeRawRequest.
type anylist []any
Expand Down
14 changes: 14 additions & 0 deletions client/asset/dcr/spv.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ type dcrWallet interface {
NewVSPTicket(ctx context.Context, hash *chainhash.Hash) (*wallet.VSPTicket, error)
RescanProgressFromHeight(ctx context.Context, n wallet.NetworkBackend, startHeight int32, p chan<- wallet.RescanProgress)
RescanPoint(ctx context.Context) (*chainhash.Hash, error)
TotalReceivedForAddr(ctx context.Context, addr stdaddr.Address, minConf int32) (dcrutil.Amount, error)
}

// Interface for *spv.Syncer so that we can test with a stub.
Expand Down Expand Up @@ -1410,6 +1411,19 @@ func (w *spvWallet) SetTxFee(_ context.Context, feePerKB dcrutil.Amount) error {
return nil
}

func (w *spvWallet) AddressUsed(ctx context.Context, addrStr string) (bool, error) {
addr, err := stdaddr.DecodeAddress(addrStr, w.chainParams)
if err != nil {
return false, err
}
const minConf = 0
recv, err := w.TotalReceivedForAddr(ctx, addr, minConf)
if err != nil {
return false, err
}
return recv != 0, nil
}

// cacheBlock caches a block for future use. The block has a lastAccess stamp
// added, and will be discarded if not accessed again within 2 hours.
func (w *spvWallet) cacheBlock(block *wire.MsgBlock) {
Expand Down
4 changes: 4 additions & 0 deletions client/asset/dcr/spv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,10 @@ func (w *tDcrWallet) RescanPoint(ctx context.Context) (*chainhash.Hash, error) {
return nil, nil
}

func (w *tDcrWallet) TotalReceivedForAddr(ctx context.Context, addr stdaddr.Address, minConf int32) (dcrutil.Amount, error) {
return 0, nil
}

func tNewSpvWallet() (*spvWallet, *tDcrWallet) {
dcrw := &tDcrWallet{
blockInfo: make(map[int32]*wallet.BlockInfo),
Expand Down
1 change: 1 addition & 0 deletions client/asset/dcr/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ type Wallet interface {
StakeInfo(ctx context.Context) (*wallet.StakeInfoData, error)
Reconfigure(ctx context.Context, cfg *asset.WalletConfig, net dex.Network, currentAddress string) (restart bool, err error)
WalletOwnsAddress(ctx context.Context, addr stdaddr.Address) (bool, error)
AddressUsed(ctx context.Context, addrStr string) (bool, error)
}

// WalletTransaction is a pared down version of walletjson.GetTransactionResult.
Expand Down
1 change: 1 addition & 0 deletions client/asset/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,7 @@ type Sweeper interface {
// NewAddresser is a wallet that can generate new deposit addresses.
type NewAddresser interface {
NewAddress() (string, error)
AddressUsed(string) (bool, error)
}

// AddressReturner is a wallet that allows recycling of unused redemption or refund
Expand Down
12 changes: 12 additions & 0 deletions client/asset/ltc/spv.go
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,18 @@ func (w *ltcSPVWallet) RemovePeer(addr string) error {
return w.peerManager.RemovePeer(addr)
}

func (w *ltcSPVWallet) TotalReceivedForAddr(btcAddr btcutil.Address, minConf int32) (btcutil.Amount, error) {
ltcAddr, err := w.addrBTC2LTC(btcAddr)
if err != nil {
return 0, err
}
amt, err := w.Wallet.TotalReceivedForAddr(ltcAddr, 0)
if err != nil {
return 0, err
}
return btcutil.Amount(amt), nil
}

// secretSource is used to locate keys and redemption scripts while signing a
// transaction. secretSource satisfies the txauthor.SecretsSource interface.
type secretSource struct {
Expand Down
6 changes: 6 additions & 0 deletions client/asset/zec/transparent_rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,3 +410,9 @@ func walletInfo(c rpcCaller) (*walletInfoRes, error) {
}
return &res, nil
}

func getReceivedByAddress(c rpcCaller, addrStr string) (recv uint64, _ error) {
const minConf = 0
const inZats = true
return recv, c.CallRPC("getreceivedbyaddress", []any{addrStr, minConf, inZats}, &recv)
}
8 changes: 8 additions & 0 deletions client/asset/zec/zec.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ type zecWallet struct {
var _ asset.FeeRater = (*zecWallet)(nil)
var _ asset.Wallet = (*zecWallet)(nil)
var _ asset.WalletHistorian = (*zecWallet)(nil)
var _ asset.NewAddresser = (*zecWallet)(nil)

// TODO: Implement LiveReconfigurer
// var _ asset.LiveReconfigurer = (*zecWallet)(nil)
Expand Down Expand Up @@ -1680,6 +1681,13 @@ func (w *zecWallet) NewAddress() (string, error) {
return w.DepositAddress()
}

// AddressUsed checks if a wallet address has been used.
func (w *zecWallet) AddressUsed(addrStr string) (bool, error) {
// TODO: Resolve with new unified address encoding in https://github.com/decred/dcrdex/pull/2675
recv, err := getReceivedByAddress(w, addrStr)
return recv != 0, err
}

func (w *zecWallet) FindRedemption(ctx context.Context, coinID, contract dex.Bytes) (redemptionCoin, secret dex.Bytes, err error) {
return w.rf.FindRedemption(ctx, coinID)
}
Expand Down
15 changes: 15 additions & 0 deletions client/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -4012,6 +4012,21 @@ func (c *Core) NewDepositAddress(assetID uint32) (string, error) {
return addr, nil
}

// AddressUsed checks whether an address for a NewAddresser has been used.
func (c *Core) AddressUsed(assetID uint32, addr string) (bool, error) {
w, exists := c.wallet(assetID)
if !exists {
return false, newError(missingWalletErr, "no wallet found for %s", unbip(assetID))
}

na, ok := w.Wallet.(asset.NewAddresser)
if !ok {
return false, errors.New("wallet is not a NewAddresser")
}

return na.AddressUsed(addr)
Comment on lines +4022 to +4027
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Other functions of this kind are methods on *xcWallet in wallet.go

}

// AutoWalletConfig attempts to load setting from a wallet package's
// asset.WalletInfo.DefaultConfigPath. If settings are not found, an empty map
// is returned.
Expand Down
4 changes: 4 additions & 0 deletions client/core/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,10 @@ func (w *TXCWallet) NewAddress() (string, error) {
return "", w.addrErr
}

func (w *TXCWallet) AddressUsed(addr string) (bool, error) {
return false, nil
}

func (w *TXCWallet) Unlock(pw []byte) error {
return w.unlockErr
}
Expand Down
Loading
Loading