Skip to content

Commit ddff332

Browse files
authored
multi: check for used addresses (#2690)
* check for used addresses * fix docstring and check for wallet trait on tx * add dcr recipient and mempool txs
1 parent e3da655 commit ddff332

27 files changed

+252
-41
lines changed

client/asset/bch/spv.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,18 @@ func (w *bchSPVWallet) RemovePeer(addr string) error {
792792
return w.peerManager.RemovePeer(addr)
793793
}
794794

795+
func (w *bchSPVWallet) TotalReceivedForAddr(btcAddr btcutil.Address, minConf int32) (btcutil.Amount, error) {
796+
bchAddr, err := dexbch.BTCAddrToBCHAddr(btcAddr, w.btcParams)
797+
if err != nil {
798+
return 0, err
799+
}
800+
amt, err := w.Wallet.TotalReceivedForAddr(bchAddr, 0)
801+
if err != nil {
802+
return 0, err
803+
}
804+
return btcutil.Amount(amt), nil
805+
}
806+
795807
// secretSource is used to locate keys and redemption scripts while signing a
796808
// transaction. secretSource satisfies the txauthor.SecretsSource interface.
797809
type secretSource struct {

client/asset/btc/btc.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,7 @@ var _ asset.Authenticator = (*ExchangeWalletAccelerator)(nil)
931931
var _ asset.Authenticator = (*ExchangeWalletCustom)(nil)
932932
var _ asset.AddressReturner = (*baseWallet)(nil)
933933
var _ asset.WalletHistorian = (*ExchangeWalletSPV)(nil)
934+
var _ asset.NewAddresser = (*baseWallet)(nil)
934935

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

4498+
// AddressUsed checks if a wallet address has been used.
4499+
func (btc *baseWallet) AddressUsed(addrStr string) (bool, error) {
4500+
return btc.node.AddressUsed(addrStr)
4501+
}
4502+
44974503
// Withdraw withdraws funds to the specified address. Fees are subtracted from
44984504
// the value. feeRate is in units of sats/byte.
44994505
// Withdraw satisfies asset.Withdrawer.

client/asset/btc/electrum_client.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,3 +1211,11 @@ func (ew *electrumWallet) findOutputSpender(ctx context.Context, txHash *chainha
12111211

12121212
return nil, 0, nil // caller should check msgTx (internal method)
12131213
}
1214+
1215+
func (ew *electrumWallet) AddressUsed(addrStr string) (bool, error) {
1216+
txs, err := ew.wallet.GetAddressHistory(ew.ctx, addrStr)
1217+
if err != nil {
1218+
return false, fmt.Errorf("error getting address history: %w", err)
1219+
}
1220+
return len(txs) > 0, nil
1221+
}

client/asset/btc/rpcclient.go

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -29,38 +29,39 @@ import (
2929
)
3030

3131
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"
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"
6465
)
6566

6667
// IsTxNotFoundErr will return true if the error indicates that the requested
@@ -1180,6 +1181,15 @@ func SearchBlockForRedemptions(
11801181
return
11811182
}
11821183

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+
11831193
// call is used internally to marshal parameters and send requests to the RPC
11841194
// server via (*rpcclient.Client).RawRequest. If thing is non-nil, the result
11851195
// will be marshaled into thing.

client/asset/btc/spv_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,10 @@ func (c *tBtcWallet) RemovePeer(string) error {
342342
return nil
343343
}
344344

345+
func (c *tBtcWallet) TotalReceivedForAddr(addr btcutil.Address, minConf int32) (btcutil.Amount, error) {
346+
return 0, nil
347+
}
348+
345349
type tNeutrinoClient struct {
346350
*testData
347351
}

client/asset/btc/spv_wrapper.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,13 @@ type BTCWallet interface {
115115
AddPeer(string) error
116116
RemovePeer(string) error
117117
GetTransactions(startHeight, endHeight int32, accountName string, cancel <-chan struct{}) (*wallet.GetTransactionsResult, error)
118+
ListSinceBlock(start, end, syncHeight int32) ([]btcjson.ListTransactionsResult, error)
119+
TotalReceivedForAddr(addr btcutil.Address, minConf int32) (btcutil.Amount, error)
120+
}
121+
122+
type XCWalletAccount struct {
123+
AccountName string
124+
AccountNumber uint32
118125
}
119126

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

1319+
func (w *spvWallet) AddressUsed(addrStr string) (bool, error) {
1320+
addr, err := w.decodeAddr(addrStr, w.chainParams)
1321+
if err != nil {
1322+
return false, fmt.Errorf("error decoding address: %w", err)
1323+
}
1324+
1325+
const minConfs = 0
1326+
amt, err := w.wallet.TotalReceivedForAddr(addr, minConfs)
1327+
if err != nil {
1328+
return false, fmt.Errorf("error getting address received: %v", err)
1329+
}
1330+
return amt != 0, nil
1331+
}
1332+
13121333
func confirms(txHeight, curHeight int32) int32 {
13131334
switch {
13141335
case txHeight == -1, txHeight > curHeight:

client/asset/btc/wallet.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ type Wallet interface {
4545
Reconfigure(walletCfg *asset.WalletConfig, currentAddress string) (restartRequired bool, err error)
4646
Fingerprint() (string, error)
4747
ListTransactionsSinceBlock(blockHeight int32) ([]*ListTransactionsResult, error)
48+
AddressUsed(addr string) (bool, error)
4849
}
4950

5051
type TipRedemptionWallet interface {

client/asset/dcr/dcr.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,7 @@ var _ asset.Bonder = (*ExchangeWallet)(nil)
707707
var _ asset.Authenticator = (*ExchangeWallet)(nil)
708708
var _ asset.TicketBuyer = (*ExchangeWallet)(nil)
709709
var _ asset.WalletHistorian = (*ExchangeWallet)(nil)
710+
var _ asset.NewAddresser = (*ExchangeWallet)(nil)
710711

711712
type block struct {
712713
height int64
@@ -4127,6 +4128,11 @@ func (dcr *ExchangeWallet) NewAddress() (string, error) {
41274128
return dcr.DepositAddress()
41284129
}
41294130

4131+
// AddressUsed checks if a wallet address has been used.
4132+
func (dcr *ExchangeWallet) AddressUsed(addrStr string) (bool, error) {
4133+
return dcr.wallet.AddressUsed(dcr.ctx, addrStr)
4134+
}
4135+
41304136
// Unlock unlocks the exchange wallet.
41314137
func (dcr *ExchangeWallet) Unlock(pw []byte) error {
41324138
// Older SPV wallet potentially need an upgrade while we have a password.

client/asset/dcr/dcr_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,10 @@ func (c *tRPCClient) SetTxFee(ctx context.Context, fee dcrutil.Amount) error {
721721
return nil
722722
}
723723

724+
func (c *tRPCClient) GetReceivedByAddressMinConf(ctx context.Context, address stdaddr.Address, minConfs int) (dcrutil.Amount, error) {
725+
return 0, nil
726+
}
727+
724728
func (c *tRPCClient) ListSinceBlock(ctx context.Context, hash *chainhash.Hash) (*walletjson.ListSinceBlockResult, error) {
725729
return nil, nil
726730
}

client/asset/dcr/rpcwallet.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ type rpcClient interface {
156156
SetVoteChoice(ctx context.Context, agendaID, choiceID string) error
157157
SetTxFee(ctx context.Context, fee dcrutil.Amount) error
158158
ListSinceBlock(ctx context.Context, hash *chainhash.Hash) (*walletjson.ListSinceBlockResult, error)
159+
GetReceivedByAddressMinConf(ctx context.Context, address stdaddr.Address, minConfs int) (dcrutil.Amount, error)
159160
}
160161

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

1221+
func (w *rpcWallet) AddressUsed(ctx context.Context, addrStr string) (bool, error) {
1222+
addr, err := stdaddr.DecodeAddress(addrStr, w.chainParams)
1223+
if err != nil {
1224+
return false, err
1225+
}
1226+
const minConf = 0
1227+
recv, err := w.rpcClient.GetReceivedByAddressMinConf(ctx, addr, minConf)
1228+
if err != nil {
1229+
return false, err
1230+
}
1231+
return recv != 0, nil
1232+
}
1233+
12201234
// anylist is a list of RPC parameters to be converted to []json.RawMessage and
12211235
// sent via nodeRawRequest.
12221236
type anylist []any

0 commit comments

Comments
 (0)