From 1ebbc00fd8a17c9c9bd45232a2ddffbf2abb1735 Mon Sep 17 00:00:00 2001 From: Brandon Odiwuor Date: Tue, 24 Sep 2024 10:24:21 +0300 Subject: [PATCH 1/3] wallet: 'Filter' out 'send' addresses from 'listreceivedby*' --- src/wallet/rpc/transactions.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp index 61cf36a6c1..3e98c2452b 100644 --- a/src/wallet/rpc/transactions.cpp +++ b/src/wallet/rpc/transactions.cpp @@ -141,6 +141,9 @@ static UniValue ListReceived(const CWallet& wallet, const UniValue& params, cons const auto& func = [&](const CTxDestination& address, const std::string& label, bool is_change, const std::optional& purpose) { if (is_change) return; // no change addresses + LOCK(wallet.cs_wallet); + if (!wallet.IsMine(address)) return; // no send addresses + auto it = mapTally.find(address); if (it == mapTally.end() && !fIncludeEmpty) return; From c4968d514797090732e587799f181db6e258593b Mon Sep 17 00:00:00 2001 From: Brandon Odiwuor Date: Tue, 24 Sep 2024 10:26:43 +0300 Subject: [PATCH 2/3] test: split 'listreceivedby' tests in subtests Co-authored-by: Andreas Kouloumos --- test/functional/wallet_listreceivedby.py | 79 ++++++++++++++---------- 1 file changed, 46 insertions(+), 33 deletions(-) diff --git a/test/functional/wallet_listreceivedby.py b/test/functional/wallet_listreceivedby.py index 522c7732fe..d12c39560e 100755 --- a/test/functional/wallet_listreceivedby.py +++ b/test/functional/wallet_listreceivedby.py @@ -29,10 +29,17 @@ class ReceivedByTest(BitcoinTestFramework): self.skip_if_no_cli() def run_test(self): + self.test_listreceivedbyaddress() + self.test_getreceivedby() + self.test_receivedbylabel() + self.test_coinbase_inclusion() + + def test_listreceivedbyaddress(self): # save the number of coinbase reward addresses so far num_cb_reward_addresses = len(self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True, include_watchonly=True)) - self.log.info("listreceivedbyaddress Test") + self.log.info("Tests listreceivedbyaddress") + self.log.info("- respects minimum confirmations") # Send from node 0 to 1 addr = self.nodes[1].getnewaddress() @@ -40,36 +47,31 @@ class ReceivedByTest(BitcoinTestFramework): self.sync_all() # Check not listed in listreceivedbyaddress because has 0 confirmations - assert_array_result(self.nodes[1].listreceivedbyaddress(), - {"address": addr}, - {}, - True) + assert_array_result(self.nodes[1].listreceivedbyaddress(), {"address": addr}, {}, True) # Bury Tx under 10 block so it will be returned by listreceivedbyaddress self.generate(self.nodes[1], 10) - assert_array_result(self.nodes[1].listreceivedbyaddress(), - {"address": addr}, - {"address": addr, "label": "", "amount": Decimal("0.1"), "confirmations": 10, "txids": [txid, ]}) + expected = {"address": addr, "label": "", "amount": Decimal("0.1"), "confirmations": 10, "txids": [txid, ]} + assert_array_result(self.nodes[1].listreceivedbyaddress(), {"address": addr}, expected) # With min confidence < 10 - assert_array_result(self.nodes[1].listreceivedbyaddress(5), - {"address": addr}, - {"address": addr, "label": "", "amount": Decimal("0.1"), "confirmations": 10, "txids": [txid, ]}) + assert_array_result(self.nodes[1].listreceivedbyaddress(5), {"address": addr}, expected) # With min confidence > 10, should not find Tx assert_array_result(self.nodes[1].listreceivedbyaddress(11), {"address": addr}, {}, True) # Empty Tx + self.log.info("- includes empty addresses") empty_addr = self.nodes[1].getnewaddress() - assert_array_result(self.nodes[1].listreceivedbyaddress(0, True), - {"address": empty_addr}, - {"address": empty_addr, "label": "", "amount": 0, "confirmations": 0, "txids": []}) + expected_tx = {"address": empty_addr, "label": "", "amount": 0, "confirmations": 0, "txids": []} + assert_array_result(self.nodes[1].listreceivedbyaddress(0, True), {"address": empty_addr}, expected_tx) - # No returned addy should be a change addr + self.log.info("- does not return changes addresses") + # No returned addr should be a change addr for node in self.nodes: for addr_obj in node.listreceivedbyaddress(): assert_equal(node.getaddressinfo(addr_obj["address"])["ischange"], False) + self.log.info("- respects address filtering") # Test Address filtering # Only on addr - expected = {"address": addr, "label": "", "amount": Decimal("0.1"), "confirmations": 10, "txids": [txid, ]} res = self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True, include_watchonly=True, address_filter=addr) assert_array_result(res, {"address": addr}, expected) assert_equal(len(res), 1) @@ -86,7 +88,7 @@ class ReceivedByTest(BitcoinTestFramework): txid2 = self.nodes[0].sendtoaddress(other_addr, 0.1) self.generate(self.nodes[0], 1) # Same test as above should still pass - expected = {"address": addr, "label": "", "amount": Decimal("0.1"), "confirmations": 11, "txids": [txid, ]} + expected["confirmations"] += 1 res = self.nodes[1].listreceivedbyaddress(0, True, True, addr) assert_array_result(res, {"address": addr}, expected) assert_equal(len(res), 1) @@ -104,13 +106,16 @@ class ReceivedByTest(BitcoinTestFramework): res = self.nodes[1].listreceivedbyaddress(0, True, True, other_addr) assert_equal(len(res), 0) - self.log.info("getreceivedbyaddress Test") + def test_getreceivedby(self): + self.log.info("Tests getreceivedbyaddress") # Send from node 0 to 1 addr = self.nodes[1].getnewaddress() - txid = self.nodes[0].sendtoaddress(addr, 0.1) + + self.nodes[0].sendtoaddress(addr, 0.1) self.sync_all() + self.log.info("- respects minimum confirmations") # Check balance is 0 because of 0 confirmations balance = self.nodes[1].getreceivedbyaddress(addr) assert_equal(balance, Decimal("0.0")) @@ -124,11 +129,17 @@ class ReceivedByTest(BitcoinTestFramework): balance = self.nodes[1].getreceivedbyaddress(addr) assert_equal(balance, Decimal("0.1")) + self.log.info("- does not accept address not found in the wallet") # Trying to getreceivedby for an address the wallet doesn't own should return an error assert_raises_rpc_error(-4, "Address not found in wallet", self.nodes[0].getreceivedbyaddress, addr) - self.log.info("listreceivedbylabel + getreceivedbylabel Test") + def test_receivedbylabel(self): + self.log.info("Tests getreceivedbylabel does not accept label not found in the wallet") + # getreceivedbylabel returns an error if the wallet does not own the label + assert_raises_rpc_error(-4, "Label not found in wallet", self.nodes[0].getreceivedbylabel, "dummy") + self.log.info("Tests listreceivedbylabel + getreceivedbylabel") + self.log.info("- returns matching balances") # set pre-state label = '' address = self.nodes[1].getnewaddress() @@ -136,12 +147,9 @@ class ReceivedByTest(BitcoinTestFramework): received_by_label_json = [r for r in self.nodes[1].listreceivedbylabel() if r["label"] == label][0] balance_by_label = self.nodes[1].getreceivedbylabel(label) - txid = self.nodes[0].sendtoaddress(addr, 0.1) + self.nodes[0].sendtoaddress(address, 0.1) self.sync_all() - # getreceivedbylabel returns an error if the wallet doesn't own the label - assert_raises_rpc_error(-4, "Label not found in wallet", self.nodes[0].getreceivedbylabel, "dummy") - # listreceivedbylabel should return received_by_label_json because of 0 confirmations assert_array_result(self.nodes[1].listreceivedbylabel(), {"label": label}, @@ -173,7 +181,8 @@ class ReceivedByTest(BitcoinTestFramework): balance = self.nodes[1].getreceivedbylabel("mynewlabel") assert_equal(balance, Decimal("0.0")) - self.log.info("Tests for including coinbase outputs") + def test_coinbase_inclusion(self): + self.log.info("Tests for including coinbase outputs for immature coinbase") # Generate block reward to address with label label = "label" @@ -183,38 +192,42 @@ class ReceivedByTest(BitcoinTestFramework): self.generatetoaddress(self.nodes[0], 1, address) hash = self.nodes[0].getbestblockhash() - self.log.info("getreceivedbyaddress returns nothing with defaults") + self.log.info("- getreceivedbyaddress") + self.log.info(" - returns nothing with defaults") balance = self.nodes[0].getreceivedbyaddress(address) assert_equal(balance, 0) - self.log.info("getreceivedbyaddress returns block reward when including immature coinbase") + self.log.info(" - returns block reward when including immature coinbase") balance = self.nodes[0].getreceivedbyaddress(address=address, include_immature_coinbase=True) assert_equal(balance, reward) - self.log.info("getreceivedbylabel returns nothing with defaults") + self.log.info("- getreceivedbylabel") + self.log.info(" - returns nothing with defaults") balance = self.nodes[0].getreceivedbylabel("label") assert_equal(balance, 0) - self.log.info("getreceivedbylabel returns block reward when including immature coinbase") + self.log.info(" - returns block reward when including immature coinbase") balance = self.nodes[0].getreceivedbylabel(label="label", include_immature_coinbase=True) assert_equal(balance, reward) - self.log.info("listreceivedbyaddress does not include address with defaults") + self.log.info("- listreceivedbyaddress") + self.log.info(" - does not include address with defaults") assert_array_result(self.nodes[0].listreceivedbyaddress(), {"address": address}, {}, True) - self.log.info("listreceivedbyaddress includes address when including immature coinbase") + self.log.info(" - includes address when including immature coinbase") assert_array_result(self.nodes[0].listreceivedbyaddress(minconf=1, include_immature_coinbase=True), {"address": address}, {"address": address, "amount": reward}) - self.log.info("listreceivedbylabel does not include label with defaults") + self.log.info("- listreceivedbylabel") + self.log.info(" - does not include label with defaults") assert_array_result(self.nodes[0].listreceivedbylabel(), {"label": label}, {}, True) - self.log.info("listreceivedbylabel includes label when including immature coinbase") + self.log.info(" - includes label when including immature coinbase") assert_array_result(self.nodes[0].listreceivedbylabel(minconf=1, include_immature_coinbase=True), {"label": label}, {"label": label, "amount": reward}) From a35115f17caf550326e12ff3044fe617a1a53fd3 Mon Sep 17 00:00:00 2001 From: Brandon Odiwuor Date: Tue, 24 Sep 2024 10:33:39 +0300 Subject: [PATCH 3/3] test: 'listreceivedby*' does not return send address Co-authored-by: Andreas Kouloumos --- test/functional/wallet_listreceivedby.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/test/functional/wallet_listreceivedby.py b/test/functional/wallet_listreceivedby.py index d12c39560e..e518ff8faa 100755 --- a/test/functional/wallet_listreceivedby.py +++ b/test/functional/wallet_listreceivedby.py @@ -30,6 +30,7 @@ class ReceivedByTest(BitcoinTestFramework): def run_test(self): self.test_listreceivedbyaddress() + self.test_listreceivedby() self.test_getreceivedby() self.test_receivedbylabel() self.test_coinbase_inclusion() @@ -106,12 +107,33 @@ class ReceivedByTest(BitcoinTestFramework): res = self.nodes[1].listreceivedbyaddress(0, True, True, other_addr) assert_equal(len(res), 0) + def test_listreceivedby(self): + self.log.info("Tests listreceivedbyaddress does not return address with 'send' purpose") + label = "node0address" + address = self.nodes[0].getnewaddress(label) + # using setlabel for an address that does not belong to the wallet assigns a "send" purpose to that address + self.nodes[1].setlabel(address, label) # address has now assigned a "send" purpose on node1 + assert_equal(self.nodes[0].getaddressesbylabel(label=label), {address: {'purpose': 'receive'}}) + assert_equal(self.nodes[1].getaddressesbylabel(label=label), {address: {'purpose': 'send'}}) + + txid = self.nodes[0].sendtoaddress(address, 0.1) + self.sync_all() + + expected = {"address": address, "label": label, "amount": Decimal("0.1"), "confirmations": 0, "txids": [txid, ]} + assert_array_result(self.nodes[0].listreceivedbyaddress(minconf=0, include_empty=True), {"address": address}, expected) + assert_array_result(self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True), {"address": address}, {}, True) + + self.log.info("Tests listreceivedbylabel does not return label of address with 'send' purpose") + expected = {"label": label, "amount": Decimal("0.1")} + assert_array_result(self.nodes[0].listreceivedbylabel(minconf=0, include_empty=True), {"label": label}, expected) + assert_array_result(self.nodes[1].listreceivedbylabel(minconf=0, include_empty=True), {"label": label}, {}, True) + def test_getreceivedby(self): self.log.info("Tests getreceivedbyaddress") # Send from node 0 to 1 addr = self.nodes[1].getnewaddress() - + self.nodes[0].sendtoaddress(addr, 0.1) self.sync_all()