bitcoin/test/functional/wallet_listreceivedby.py
Andrew Chow 5ebff43025
Merge bitcoin/bitcoin#25122: rpc: getreceivedbylabel, return early if no addresses were found in the address book
baa3ddc49c doc: add release notes about `getreceivedbylabel` returning an error if the label is not in the address book. (furszy)
8897a21658 rpc: getreceivedbylabel, don't loop over the entire wallet txs map if no destinations were found for the input label. (furszy)

Pull request description:

  Built on top of #23662, coming from comment https://github.com/bitcoin/bitcoin/pull/23662#pullrequestreview-971407999.

  If `wallet.GetLabelAddresses()` returns an empty vector (the wallet does not have stored destinations with that label in the addressbook) or if none of the returned destinations are from the wallet, we can return the function right away.
  Otherwise, we are walking through all the wallet txs + outputs for no reason (`output_scripts` is empty).

ACKs for top commit:
  achow101:
    ACK baa3ddc49c
  theStack:
    re-ACK baa3ddc49c
  w0xlt:
    ACK baa3ddc49c

Tree-SHA512: 00e10365b179bf008da2f3ef8fbb3ee04a330426374020e3f2d0151b16991baba4ef2b944e4659452f3e4d6cb20f128d0918ddf0453933a25a4d9fd8414a1911
2022-05-23 12:15:14 -04:00

256 lines
12 KiB
Python
Executable file

#!/usr/bin/env python3
# Copyright (c) 2014-2021 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the listreceivedbyaddress, listreceivedbylabel, getreceivedybaddress, and getreceivedbylabel RPCs."""
from decimal import Decimal
from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_array_result,
assert_equal,
assert_raises_rpc_error,
)
from test_framework.wallet_util import test_address
class ReceivedByTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
self.skip_if_no_cli()
def run_test(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")
# Send from node 0 to 1
addr = self.nodes[1].getnewaddress()
txid = self.nodes[0].sendtoaddress(addr, 0.1)
self.sync_all()
# Check not listed in listreceivedbyaddress because has 0 confirmations
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, ]})
# 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, ]})
# With min confidence > 10, should not find Tx
assert_array_result(self.nodes[1].listreceivedbyaddress(11), {"address": addr}, {}, True)
# Empty Tx
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": []})
# 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)
# Test for regression on CLI calls with address string (#14173)
cli_res = self.nodes[1].cli.listreceivedbyaddress(0, True, True, addr)
assert_array_result(cli_res, {"address": addr}, expected)
assert_equal(len(cli_res), 1)
# Error on invalid address
assert_raises_rpc_error(-4, "address_filter parameter was invalid", self.nodes[1].listreceivedbyaddress, minconf=0, include_empty=True, include_watchonly=True, address_filter="bamboozling")
# Another address receive money
res = self.nodes[1].listreceivedbyaddress(0, True, True)
assert_equal(len(res), 2 + num_cb_reward_addresses) # Right now 2 entries
other_addr = self.nodes[1].getnewaddress()
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, ]}
res = self.nodes[1].listreceivedbyaddress(0, True, True, addr)
assert_array_result(res, {"address": addr}, expected)
assert_equal(len(res), 1)
# Same test as above but with other_addr should still pass
expected = {"address": other_addr, "label": "", "amount": Decimal("0.1"), "confirmations": 1, "txids": [txid2, ]}
res = self.nodes[1].listreceivedbyaddress(0, True, True, other_addr)
assert_array_result(res, {"address": other_addr}, expected)
assert_equal(len(res), 1)
# Should be two entries though without filter
res = self.nodes[1].listreceivedbyaddress(0, True, True)
assert_equal(len(res), 3 + num_cb_reward_addresses) # Became 3 entries
# Not on random addr
other_addr = self.nodes[0].getnewaddress() # note on node[0]! just a random addr
res = self.nodes[1].listreceivedbyaddress(0, True, True, other_addr)
assert_equal(len(res), 0)
self.log.info("getreceivedbyaddress Test")
# Send from node 0 to 1
addr = self.nodes[1].getnewaddress()
txid = self.nodes[0].sendtoaddress(addr, 0.1)
self.sync_all()
# Check balance is 0 because of 0 confirmations
balance = self.nodes[1].getreceivedbyaddress(addr)
assert_equal(balance, Decimal("0.0"))
# Check balance is 0.1
balance = self.nodes[1].getreceivedbyaddress(addr, 0)
assert_equal(balance, Decimal("0.1"))
# Bury Tx under 10 block so it will be returned by the default getreceivedbyaddress
self.generate(self.nodes[1], 10)
balance = self.nodes[1].getreceivedbyaddress(addr)
assert_equal(balance, Decimal("0.1"))
# 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")
# set pre-state
label = ''
address = self.nodes[1].getnewaddress()
test_address(self.nodes[1], address, labels=[label])
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.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},
received_by_label_json)
# getreceivedbyaddress should return same balance because of 0 confirmations
balance = self.nodes[1].getreceivedbylabel(label)
assert_equal(balance, balance_by_label)
self.generate(self.nodes[1], 10)
# listreceivedbylabel should return updated received list
assert_array_result(self.nodes[1].listreceivedbylabel(),
{"label": label},
{"label": received_by_label_json["label"], "amount": (received_by_label_json["amount"] + Decimal("0.1"))})
# getreceivedbylabel should return updated receive total
balance = self.nodes[1].getreceivedbylabel(label)
assert_equal(balance, balance_by_label + Decimal("0.1"))
# Create a new label named "mynewlabel" that has a 0 balance
address = self.nodes[1].getnewaddress()
self.nodes[1].setlabel(address, "mynewlabel")
received_by_label_json = [r for r in self.nodes[1].listreceivedbylabel(0, True) if r["label"] == "mynewlabel"][0]
# Test includeempty of listreceivedbylabel
assert_equal(received_by_label_json["amount"], Decimal("0.0"))
# Test getreceivedbylabel for 0 amount labels
balance = self.nodes[1].getreceivedbylabel("mynewlabel")
assert_equal(balance, Decimal("0.0"))
self.log.info("Tests for including coinbase outputs")
# Generate block reward to address with label
label = "label"
address = self.nodes[0].getnewaddress(label)
reward = Decimal("25")
self.generatetoaddress(self.nodes[0], 1, address)
hash = self.nodes[0].getbestblockhash()
self.log.info("getreceivedbyaddress 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")
balance = self.nodes[0].getreceivedbyaddress(address=address, include_immature_coinbase=True)
assert_equal(balance, reward)
self.log.info("getreceivedbylabel 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")
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")
assert_array_result(self.nodes[0].listreceivedbyaddress(),
{"address": address},
{}, True)
self.log.info("listreceivedbyaddress 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")
assert_array_result(self.nodes[0].listreceivedbylabel(),
{"label": label},
{}, True)
self.log.info("listreceivedbylabel 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})
self.log.info("Generate 100 more blocks")
self.generate(self.nodes[0], COINBASE_MATURITY)
self.log.info("getreceivedbyaddress returns reward with defaults")
balance = self.nodes[0].getreceivedbyaddress(address)
assert_equal(balance, reward)
self.log.info("getreceivedbylabel returns reward with defaults")
balance = self.nodes[0].getreceivedbylabel("label")
assert_equal(balance, reward)
self.log.info("listreceivedbyaddress includes address with defaults")
assert_array_result(self.nodes[0].listreceivedbyaddress(),
{"address": address},
{"address": address, "amount": reward})
self.log.info("listreceivedbylabel includes label with defaults")
assert_array_result(self.nodes[0].listreceivedbylabel(),
{"label": label},
{"label": label, "amount": reward})
self.log.info("Invalidate block that paid to address")
self.nodes[0].invalidateblock(hash)
self.log.info("getreceivedbyaddress does not include invalidated block when minconf is 0 when including immature coinbase")
balance = self.nodes[0].getreceivedbyaddress(address=address, minconf=0, include_immature_coinbase=True)
assert_equal(balance, 0)
self.log.info("getreceivedbylabel does not include invalidated block when minconf is 0 when including immature coinbase")
balance = self.nodes[0].getreceivedbylabel(label="label", minconf=0, include_immature_coinbase=True)
assert_equal(balance, 0)
self.log.info("listreceivedbyaddress does not include invalidated block when minconf is 0 when including immature coinbase")
assert_array_result(self.nodes[0].listreceivedbyaddress(minconf=0, include_immature_coinbase=True),
{"address": address},
{}, True)
self.log.info("listreceivedbylabel does not include invalidated block when minconf is 0 when including immature coinbase")
assert_array_result(self.nodes[0].listreceivedbylabel(minconf=0, include_immature_coinbase=True),
{"label": label},
{}, True)
if __name__ == '__main__':
ReceivedByTest().main()