diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3499,7 +3499,8 @@ CTxDestination address; const CScript &scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey; bool fValidAddress = ExtractDestination(scriptPubKey, address); - bool reused = avoid_reuse && pwallet->IsUsedDestination(address); + bool reused = + avoid_reuse && pwallet->IsUsedDestination(out.tx->GetId(), out.i); if (destinations.size() && (!fValidAddress || !destinations.count(address))) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -904,8 +904,8 @@ EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); // Whether this or any UTXO with the same CTxDestination has been spent. - bool IsUsedDestination(const CTxDestination &dst) const; - bool IsUsedDestination(const TxId &txid, unsigned int n) const; + bool IsUsedDestination(const TxId &txid, unsigned int n) const + EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void SetUsedDestinationState(WalletBatch &batch, const TxId &txid, unsigned int n, bool used) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -780,16 +780,25 @@ } } -bool CWallet::IsUsedDestination(const CTxDestination &dst) const { - LOCK(cs_wallet); - return IsMine(dst) && GetDestData(dst, "used", nullptr); -} - bool CWallet::IsUsedDestination(const TxId &txid, unsigned int n) const { + AssertLockHeld(cs_wallet); CTxDestination dst; const CWalletTx *srctx = GetWalletTx(txid); - return srctx && ExtractDestination(srctx->tx->vout[n].scriptPubKey, dst) && - IsUsedDestination(dst); + if (srctx) { + assert(srctx->tx->vout.size() > n); + LegacyScriptPubKeyMan *spk_man = GetLegacyScriptPubKeyMan(); + // When descriptor wallets arrive, these additional checks are + // likely superfluous and can be optimized out + assert(spk_man != nullptr); + for (const auto &keyid : + GetAffectedKeys(srctx->tx->vout[n].scriptPubKey, *spk_man)) { + PKHash pkh_dest(keyid); + if (GetDestData(pkh_dest, "used", nullptr)) { + return true; + } + } + } + return false; } bool CWallet::AddToWallet(const CWalletTx &wtxIn, bool fFlushOnClose) { diff --git a/test/functional/wallet_avoidreuse.py b/test/functional/wallet_avoidreuse.py --- a/test/functional/wallet_avoidreuse.py +++ b/test/functional/wallet_avoidreuse.py @@ -240,7 +240,7 @@ ''' self.log.info("Test fund send fund send") - fundaddr = self.nodes[1].getnewaddress() + fundaddr = self.nodes[1].getnewaddress(label="", address_type="legacy") retaddr = self.nodes[0].getnewaddress() self.nodes[0].sendtoaddress(fundaddr, 10) @@ -271,7 +271,13 @@ # getbalances should show no used, 5 BCH trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5}) - self.nodes[0].sendtoaddress(fundaddr, 10) + # For the second send, we transmute it to a related single-key address + # to make sure it's also detected as re-use + # NB: this is not very useful for ABC, but we keep the new variable + # name for consistency. + new_fundaddr = fundaddr + + self.nodes[0].sendtoaddress(new_fundaddr, 10) self.nodes[0].generate(1) self.sync_all()