diff --git a/src/wallet/rpc/coins.cpp b/src/wallet/rpc/coins.cpp index 51fd86088526..7f7f391e5ce5 100644 --- a/src/wallet/rpc/coins.cpp +++ b/src/wallet/rpc/coins.cpp @@ -162,6 +162,51 @@ RPCHelpMan getreceivedbylabel() }; } +RPCHelpMan listaddressbalances() +{ + return RPCHelpMan{"listaddressbalances", + "\nLists addresses of this wallet and their balances\n", + { + {"minamount", RPCArg::Type::NUM, RPCArg::Default{0}, "Minimum balance in " + CURRENCY_UNIT + " an address should have to be shown in the list"}, + }, + RPCResult{ + RPCResult::Type::OBJ_DYN, "addresses", "Balances of addresses", + { + {RPCResult::Type::STR_AMOUNT, "address", "the amount in " + CURRENCY_UNIT}, + } + }, + RPCExamples{ + HelpExampleCli("listaddressbalances", "") + + HelpExampleCli("listaddressbalances", "10") + + HelpExampleRpc("listaddressbalances", "") + + HelpExampleRpc("listaddressbalances", "10") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + const std::shared_ptr pwallet = GetWalletForJSONRPCRequest(request); + if (!pwallet) return UniValue::VNULL; + + LOCK(pwallet->cs_wallet); + + CAmount nMinAmount = 0; + if (!request.params[0].isNull()) + nMinAmount = AmountFromValue(request.params[0]); + + if (nMinAmount < 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); + + UniValue jsonBalances(UniValue::VOBJ); + std::map balances = GetAddressBalances(*pwallet); + for (auto& balance : balances) + if (balance.second >= nMinAmount) + jsonBalances.pushKV(EncodeDestination(balance.first), ValueFromAmount(balance.second)); + + return jsonBalances; +}, + }; +} + + RPCHelpMan getbalance() { return RPCHelpMan{"getbalance", diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp index c48a0d41b0f5..a90712a3ac1e 100644 --- a/src/wallet/rpc/wallet.cpp +++ b/src/wallet/rpc/wallet.cpp @@ -42,51 +42,6 @@ bool HaveKey(const SigningProvider& wallet, const CKey& key) return wallet.HaveKey(key.GetPubKey().GetID()) || wallet.HaveKey(key2.GetPubKey().GetID()); } -static RPCHelpMan listaddressbalances() -{ - return RPCHelpMan{"listaddressbalances", - "\nLists addresses of this wallet and their balances\n", - { - {"minamount", RPCArg::Type::NUM, RPCArg::Default{0}, "Minimum balance in " + CURRENCY_UNIT + " an address should have to be shown in the list"}, - }, - RPCResult{ - RPCResult::Type::ARR, "", "", - { - {RPCResult::Type::STR_AMOUNT, "amount", "The Dash address and the amount in " + CURRENCY_UNIT}, - } - }, - RPCExamples{ - HelpExampleCli("listaddressbalances", "") - + HelpExampleCli("listaddressbalances", "10") - + HelpExampleRpc("listaddressbalances", "") - + HelpExampleRpc("listaddressbalances", "10") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue -{ - const std::shared_ptr pwallet = GetWalletForJSONRPCRequest(request); - if (!pwallet) return UniValue::VNULL; - - LOCK(pwallet->cs_wallet); - - CAmount nMinAmount = 0; - if (!request.params[0].isNull()) - nMinAmount = AmountFromValue(request.params[0]); - - if (nMinAmount < 0) - throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); - - UniValue jsonBalances(UniValue::VOBJ); - std::map balances = GetAddressBalances(*pwallet); - for (auto& balance : balances) - if (balance.second >= nMinAmount) - jsonBalances.pushKV(EncodeDestination(balance.first), ValueFromAmount(balance.second)); - - return jsonBalances; -}, - }; -} - - static RPCHelpMan setcoinjoinrounds() { return RPCHelpMan{"setcoinjoinrounds", @@ -1138,6 +1093,7 @@ RPCHelpMan importelectrumwallet(); // coins RPCHelpMan getreceivedbyaddress(); RPCHelpMan getreceivedbylabel(); +RPCHelpMan listaddressbalances(); RPCHelpMan getbalance(); RPCHelpMan getunconfirmedbalance(); RPCHelpMan lockunspent(); diff --git a/test/functional/create_cache.py b/test/functional/create_cache.py index 1108a8e3544b..2179bb7815a7 100755 --- a/test/functional/create_cache.py +++ b/test/functional/create_cache.py @@ -16,6 +16,7 @@ class CreateCache(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 0 + self.uses_wallet = True def setup_network(self): pass diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index de0e4ca70501..45cdf8c9e142 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -144,6 +144,7 @@ def __init__(self): self._requires_wallet = False # Disable ThreadOpenConnections by default, so that adding entries to # addrman will not result in automatic connections to them. + self.uses_wallet = False self.disable_autoconnect = True self.set_test_params() assert self.wallet_names is None or len(self.wallet_names) <= self.num_nodes @@ -577,6 +578,7 @@ def get_bin_from_version(version, bin_name, bin_default): use_valgrind=self.options.valgrind, descriptors=self.options.descriptors, v2transport=self.options.v2transport, + uses_wallet=self.uses_wallet, ) self.nodes.append(test_node_i) if not test_node_i.version_is_at_least(160000): @@ -955,7 +957,7 @@ def _initialize_chain(self): cache_node_dir, chain=self.chain, extra_conf=["bind=127.0.0.1"], - extra_args=['-disablewallet', f"-mocktime={TIME_GENESIS_BLOCK}"], + extra_args=[f"-mocktime={TIME_GENESIS_BLOCK}"], extra_args_from_options=self.extra_args_from_options, rpchost=None, timewait=self.rpc_timeout, @@ -966,6 +968,7 @@ def _initialize_chain(self): coverage_dir=None, cwd=self.options.tmpdir, descriptors=self.options.descriptors, + uses_wallet=self.uses_wallet, )) self.start_node(CACHE_NODE_ID) cache_node = self.nodes[CACHE_NODE_ID] diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 52f785c14ce5..1d4c2c817255 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -69,7 +69,7 @@ class TestNode(): To make things easier for the test writer, any unrecognised messages will be dispatched to the RPC connection.""" - def __init__(self, i, datadir, extra_args_from_options, *, chain, rpchost, timewait, timeout_factor, bitcoind, bitcoin_cli, mocktime, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False, use_valgrind=False, version=None, descriptors=False, v2transport=False): + def __init__(self, i, datadir, extra_args_from_options, *, chain, rpchost, timewait, timeout_factor, bitcoind, bitcoin_cli, mocktime, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False, use_valgrind=False, version=None, descriptors=False, v2transport=False, uses_wallet=False): """ Kwargs: start_perf (bool): If True, begin profiling the node with `perf` as soon as @@ -114,7 +114,7 @@ def __init__(self, i, datadir, extra_args_from_options, *, chain, rpchost, timew if self.mocktime != 0: self.args.append(f"-mocktime={mocktime}") - if self.descriptors is None: + if uses_wallet is not None and not uses_wallet and self.descriptors is None: self.args.append("-disablewallet") # Use valgrind, expect for previous release binaries diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 90c8fbe8e49e..3dcd7035142c 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -926,6 +926,8 @@ def _get_uncovered_rpc_commands(self): covered_cmds = set({'generate'}) # TODO: implement functional tests for voteraw covered_cmds.add('voteraw') + # TODO: implement functional tests for importelectrumwallet + covered_cmds.add('importelectrumwallet') # TODO: implement functional tests for getmerkleblocks covered_cmds.add('getmerkleblocks') diff --git a/test/functional/wallet_balance.py b/test/functional/wallet_balance.py index b039f147aa3b..5454a43406c9 100755 --- a/test/functional/wallet_balance.py +++ b/test/functional/wallet_balance.py @@ -109,6 +109,15 @@ def run_test(self): assert_equal(self.nodes[0].getbalance("*", 1, True), 500) assert_equal(self.nodes[1].getbalance(minconf=0, include_watchonly=True), 500) + self.log.info("Test listaddressbalances") + address_balances = {} + address_balances['yYdShjQSptFKitYLksFEUSwHe4hnbar5rf'] = Decimal('500.00000000') + if not self.options.descriptors: + address_balances['yVg3NBUHNEhgDceqwVUjsZHreC5PBHnUo9'] = Decimal('500.00000000') + assert_equal(address_balances, self.nodes[0].listaddressbalances()) + assert_equal(address_balances, self.nodes[0].listaddressbalances(500)) + assert_equal({}, self.nodes[0].listaddressbalances(501)) + # Send 490 BTC from 0 to 1 and 960 BTC from 1 to 0. txs = create_transactions(self.nodes[0], self.nodes[1].getnewaddress(), 490 , [Decimal('0.01')]) self.nodes[0].sendrawtransaction(txs[0]['hex']) diff --git a/test/functional/wallet_transactiontime_rescan.py b/test/functional/wallet_transactiontime_rescan.py index 268e623cd90d..e7c57fb774ea 100755 --- a/test/functional/wallet_transactiontime_rescan.py +++ b/test/functional/wallet_transactiontime_rescan.py @@ -136,6 +136,10 @@ def run_test(self): restorewo_wallet.importaddress(wo2, rescan=False) restorewo_wallet.importaddress(wo3, rescan=False) + self.log.info('Testing abortrescan when no rescan is in progress') + assert_equal(restorewo_wallet.getwalletinfo()['scanning'], False) + assert_equal(restorewo_wallet.abortrescan(), False) + # check user has 0 balance and no transactions assert_equal(restorewo_wallet.getbalance(), 0) assert_equal(len(restorewo_wallet.listtransactions()), 0)