From 873c0548059b2860b3718551078763d227186bd6 Mon Sep 17 00:00:00 2001 From: pablomartin4btc Date: Thu, 6 Aug 2026 23:28:30 -0300 Subject: [PATCH 1/2] wallet: Exclude non-owned addresses from listreceivedby* listreceivedbyaddress/listreceivedbylabel with include_empty=true walked the full address book and returned every entry with no matching mapTally record, including addresses with a "send" purpose (foreign addresses labeled via setlabel, the GUI, or addmultisigaddress) that the wallet never received funds to and does not own. Filter these out via IsMine() rather than the address book's "purpose" field, since purpose is set inconsistently across several code paths and IsMine() is the same check mapTally itself is already built from. Fixes #16159. Co-authored-by: Brandon Odiwuor --- src/wallet/rpc/transactions.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp index d8535694453..1db66bebfde 100644 --- a/src/wallet/rpc/transactions.cpp +++ b/src/wallet/rpc/transactions.cpp @@ -139,12 +139,17 @@ static UniValue ListReceived(const CWallet& wallet, const UniValue& params, cons UniValue ret(UniValue::VARR); std::map label_tally; - const auto& func = [&](const CTxDestination& address, const std::string& label, bool is_change, const std::optional& purpose) { + const auto& func = [&](const CTxDestination& address, const std::string& label, bool is_change, + const std::optional& purpose) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet) { if (is_change) return; // no change addresses + // Entries in mapTally are only ever added for wallet.IsMine() addresses (see the tally + // loop above), so it's only addresses missing from mapTally that need the IsMine() check. auto it = mapTally.find(address); - if (it == mapTally.end() && !fIncludeEmpty) - return; + if (it == mapTally.end()) { + if (!fIncludeEmpty) return; + if (!wallet.IsMine(address)) return; // exclude addresses not owned by the wallet (e.g. "send" purpose) + } CAmount nAmount = 0; int nConf = std::numeric_limits::max(); From 089c883c558e01c2a18a92f861fa7de9a7cc607a Mon Sep 17 00:00:00 2001 From: pablomartin4btc Date: Thu, 6 Aug 2026 23:28:30 -0300 Subject: [PATCH 2/2] test: Add coverage for listreceivedby* excluding "send" addresses Regression test for #16159: an address labeled via setlabel by a wallet that doesn't own it is assigned a "send" purpose and must not appear in listreceivedbyaddress/listreceivedbylabel results, even with include_empty=true. Co-authored-by: Andreas Kouloumos --- test/functional/wallet_listreceivedby.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/functional/wallet_listreceivedby.py b/test/functional/wallet_listreceivedby.py index a1339da3463..f7a08ebd4ad 100755 --- a/test/functional/wallet_listreceivedby.py +++ b/test/functional/wallet_listreceivedby.py @@ -101,6 +101,17 @@ class ReceivedByTest(BitcoinTestFramework): res = self.nodes[1].listreceivedbyaddress(0, True, True, other_addr) assert_equal(len(res), 0) + self.log.info("listreceivedbyaddress and listreceivedbylabel exclude not owned addresses") + # setlabel assigns a "send" purpose when the wallet doesn't own the address. + send_label = "external-address" + external_addr = self.nodes[0].getnewaddress(send_label) + self.nodes[1].setlabel(external_addr, send_label) + assert_equal(self.nodes[1].getaddressinfo(external_addr)["ismine"], False) + assert_array_result(self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True), + {"address": external_addr}, {}, True) + assert_array_result(self.nodes[1].listreceivedbylabel(minconf=0, include_empty=True), + {"label": send_label}, {}, True) + self.log.info("getreceivedbyaddress Test") # Send from node 0 to 1