diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 23d929daed..192feb9e93 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1686,9 +1686,14 @@ void ListTransactions(CWallet * const pwallet, const CWalletTx& wtx, const std:: bool fAllAccounts = (strAccount == std::string("*")); bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY); + bool list_sent = fAllAccounts; + + if (IsDeprecatedRPCEnabled("accounts")) { + list_sent |= strAccount == strSentAccount; + } + // Sent - if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) - { + if (list_sent) { for (const COutputEntry& s : listSent) { UniValue entry(UniValue::VOBJ); @@ -1781,12 +1786,14 @@ UniValue listtransactions(const JSONRPCRequest& request) std::string help_text {}; if (!IsDeprecatedRPCEnabled("accounts")) { - help_text = "listtransactions ( dummy count skip include_watchonly)\n" - "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n" + help_text = "listtransactions (label count skip include_watchonly)\n" + "\nIf a label name is provided, this will return only incoming transactions paying to addresses with the specified label.\n" + "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n" "Note that the \"account\" argument and \"otheraccount\" return value have been removed in V0.17. To use this RPC with an \"account\" argument, restart\n" "dashd with -deprecatedrpc=accounts\n" "\nArguments:\n" - "1. \"dummy\" (string, optional) If set, should be \"*\" for backwards compatibility.\n" + "1. \"label\" (string, optional) If set, should be a valid label name to return only incoming transactions\n" + " with the specified label, or \"*\" to disable filtering and return all transactions.\n" "2. count (numeric, optional, default=10) The number of transactions to return\n" "3. skip (numeric, optional, default=0) The number of transactions to skip\n" "4. include_watchonly (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')\n" @@ -1903,8 +1910,8 @@ UniValue listtransactions(const JSONRPCRequest& request) std::string strAccount = "*"; if (!request.params[0].isNull()) { strAccount = request.params[0].get_str(); - if (!IsDeprecatedRPCEnabled("accounts") && strAccount != "*") { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"*\""); + if (!IsDeprecatedRPCEnabled("accounts") && strAccount.empty()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Label argument must be a valid label name or \"*\"."); } } int nCount = 10; @@ -4135,7 +4142,7 @@ static const CRPCCommand commands[] = { "wallet", "listlockunspent", &listlockunspent, {} }, { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","addlocked","include_empty","include_watchonly","address_filter"} }, { "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} }, - { "wallet", "listtransactions", &listtransactions, {"account|dummy","count","skip","include_watchonly"} }, + { "wallet", "listtransactions", &listtransactions, {"account|label|dummy","count","skip","include_watchonly"} }, { "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, { "wallet", "listwallets", &listwallets, {} }, { "wallet", "loadwallet", &loadwallet, {"filename"} }, diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py index adb54c1bcb..7f9b93bd80 100755 --- a/test/functional/wallet_import_rescan.py +++ b/test/functional/wallet_import_rescan.py @@ -47,11 +47,11 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")): if self.call == Call.single: if self.data == Data.address: - response = self.try_rpc(self.node.importaddress, address=self.address["address"], rescan=rescan) + response = self.try_rpc(self.node.importaddress, address=self.address["address"], label=self.label, rescan=rescan) elif self.data == Data.pub: - response = self.try_rpc(self.node.importpubkey, pubkey=self.address["pubkey"], rescan=rescan) + response = self.try_rpc(self.node.importpubkey, pubkey=self.address["pubkey"], label=self.label, rescan=rescan) elif self.data == Data.priv: - response = self.try_rpc(self.node.importprivkey, privkey=self.key, rescan=rescan) + response = self.try_rpc(self.node.importprivkey, privkey=self.key, label=self.label, rescan=rescan) assert_equal(response, None) elif self.call == Call.multi: @@ -62,18 +62,32 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")): "timestamp": timestamp + TIMESTAMP_WINDOW + (1 if self.rescan == Rescan.late_timestamp else 0), "pubkeys": [self.address["pubkey"]] if self.data == Data.pub else [], "keys": [self.key] if self.data == Data.priv else [], + "label": self.label, "watchonly": self.data != Data.priv }], {"rescan": self.rescan in (Rescan.yes, Rescan.late_timestamp)}) assert_equal(response, [{"success": True}]) def check(self, txid=None, amount=None, confirmations=None): - """Verify that listreceivedbyaddress returns expected values.""" + """Verify that listtransactions/listreceivedbyaddress return expected values.""" + + txs = self.node.listtransactions(label=self.label, count=10000, skip=0, include_watchonly=True) + assert_equal(len(txs), self.expected_txs) addresses = self.node.listreceivedbyaddress(minconf=0, include_watchonly=True, address_filter=self.address['address']) if self.expected_txs: assert_equal(len(addresses[0]["txids"]), self.expected_txs) if txid is not None: + tx, = [tx for tx in txs if tx["txid"] == txid] + assert_equal(tx["label"], self.label) + assert_equal(tx["address"], self.address["address"]) + assert_equal(tx["amount"], amount) + assert_equal(tx["category"], "receive") + assert_equal(tx["label"], self.label) + assert_equal(tx["txid"], txid) + assert_equal(tx["confirmations"], confirmations) + assert_equal("trusted" not in tx, True) + address, = [ad for ad in addresses if txid in ad["txids"]] assert_equal(address["address"], self.address["address"]) assert_equal(address["amount"], self.expected_balance) @@ -126,7 +140,8 @@ class ImportRescanTest(BitcoinTestFramework): # Create one transaction on node 0 with a unique amount for # each possible type of wallet import RPC. for i, variant in enumerate(IMPORT_VARIANTS): - variant.address = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress()) + variant.label = "label {} {}".format(i, variant) + variant.address = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress(variant.label)) variant.key = self.nodes[1].dumpprivkey(variant.address["address"]) variant.initial_amount = 10 - (i + 1) / 4.0 variant.initial_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.initial_amount) diff --git a/test/functional/wallet_listtransactions.py b/test/functional/wallet_listtransactions.py index eface06a48..3cdc9b44c5 100755 --- a/test/functional/wallet_listtransactions.py +++ b/test/functional/wallet_listtransactions.py @@ -87,9 +87,10 @@ class ListTransactionsTest(BitcoinTestFramework): txid = self.nodes[1].sendtoaddress(multisig["address"], 0.1) self.nodes[1].generate(1) self.sync_all() - assert not [tx for tx in self.nodes[0].listtransactions(dummy="*", count=100, skip=0, include_watchonly=False) if "label" in tx and tx["label"] == "watchonly"] - txs = [tx for tx in self.nodes[0].listtransactions(dummy="*", count=100, skip=0, include_watchonly=True) if "label" in tx and tx['label'] == 'watchonly'] - assert_array_result(txs, {"category": "receive", "amount": Decimal("0.1")}, {"txid": txid}) + assert len(self.nodes[0].listtransactions(label="watchonly", count=100, skip=0, include_watchonly=False)) == 0 + assert_array_result(self.nodes[0].listtransactions(label="watchonly", count=100, skip=0, include_watchonly=True), + {"category": "receive", "amount": Decimal("0.1")}, + {"txid": txid, "label": "watchonly"}) if __name__ == '__main__': ListTransactionsTest().main()