diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2931,6 +2931,9 @@ "mempool)\n" " \"immature\": xxx (numeric) balance from " "immature coinbase outputs\n" + " \"used\": xxx (numeric) (only present if " + "avoid_reuse is set) balance from coins sent to addresses that " + "were previously spent from (potentially privacy violating)\n" " },\n" " \"watchonly\": { (object) watchonly " "balances (not present if wallet does not watch anything)\n" @@ -2968,6 +2971,17 @@ balances_mine.pushKV("untrusted_pending", ValueFromAmount(bal.m_mine_untrusted_pending)); balances_mine.pushKV("immature", ValueFromAmount(bal.m_mine_immature)); + if (wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) { + // If the AVOID_REUSE flag is set, bal has been set to just the + // un-reused address balance. Get the total balance, and then + // subtract bal to get the reused address balance. + const auto full_bal = wallet.GetBalance(0, false); + balances_mine.pushKV( + "used", ValueFromAmount(full_bal.m_mine_trusted + + full_bal.m_mine_untrusted_pending - + bal.m_mine_trusted - + bal.m_mine_untrusted_pending)); + } balances.pushKV("mine", balances_mine); } if (wallet.HaveWatchOnly()) { diff --git a/test/functional/wallet_avoidreuse.py b/test/functional/wallet_avoidreuse.py --- a/test/functional/wallet_avoidreuse.py +++ b/test/functional/wallet_avoidreuse.py @@ -16,9 +16,11 @@ def assert_approx(v, vexp, vspan=0.00001): if v < vexp - vspan: - raise AssertionError("{} < [{}..{}]".format(str(v), str(vexp - vspan), str(vexp + vspan))) + raise AssertionError("{} < [{}..{}]".format( + str(v), str(vexp - vspan), str(vexp + vspan))) if v > vexp + vspan: - raise AssertionError("{} > [{}..{}]".format(str(v), str(vexp - vspan), str(vexp + vspan))) + raise AssertionError("{} > [{}..{}]".format( + str(v), str(vexp - vspan), str(vexp + vspan))) def reset_balance(node, discardaddr): @@ -74,6 +76,13 @@ assert_approx(stats["reused"]["sum"], reused_sum, 0.001) +def assert_balances(node, mine): + '''Make assertions about a node's getbalances output''' + got = node.getbalances()["mine"] + for k, v in mine.items(): + assert_approx(got[k], v, 0.001) + + class AvoidReuseTest(BitcoinTestFramework): def set_test_params(self): @@ -171,6 +180,11 @@ total_sum=10, reused_supported=True, reused_count=0) + # getbalances should show no used, 10 BCH trusted + assert_balances(self.nodes[1], mine={"used": 0, "trusted": 10}) + # node 0 should not show a used entry, as it does not enable + # avoid_reuse + assert("used" not in self.nodes[0].getbalances()["mine"]) self.nodes[1].sendtoaddress(retaddr, 5) self.nodes[0].generate(1) @@ -183,6 +197,8 @@ total_sum=5, reused_supported=True, reused_count=0) + # getbalances should show no used, 5 BCH trusted + assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5}) self.nodes[0].sendtoaddress(fundaddr, 10) self.nodes[0].generate(1) @@ -196,6 +212,8 @@ total_sum=15, reused_count=1, reused_sum=10) + # getbalances should show 10 used, 5 BCH trusted + assert_balances(self.nodes[1], mine={"used": 10, "trusted": 5}) self.nodes[1].sendtoaddress( address=retaddr, amount=10, avoid_reuse=False) @@ -206,6 +224,8 @@ total_count=1, total_sum=5, reused_count=0) + # getbalances should show no used, 5 BCH trusted + assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5}) # node 1 should now have about 5 BCH left (for both cases) assert_approx(self.nodes[1].getbalance(), 5, 0.001) @@ -235,6 +255,8 @@ total_sum=10, reused_supported=True, reused_count=0) + # getbalances should show no used, 10 BCH trusted + assert_balances(self.nodes[1], mine={"used": 0, "trusted": 10}) self.nodes[1].sendtoaddress(retaddr, 5) self.nodes[0].generate(1) @@ -247,6 +269,8 @@ total_sum=5, reused_supported=True, reused_count=0) + # getbalances should show no used, 5 BCH trusted + assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5}) self.nodes[0].sendtoaddress(fundaddr, 10) self.nodes[0].generate(1) @@ -260,6 +284,8 @@ total_sum=15, reused_count=1, reused_sum=10) + # getbalances should show 10 used, 5 BCH trusted + assert_balances(self.nodes[1], mine={"used": 10, "trusted": 5}) # node 1 should now have a balance of 5 (no dirty) or 15 (including # dirty) @@ -279,6 +305,8 @@ total_sum=11, reused_count=1, reused_sum=10) + # getbalances should show 10 used, 1 BCH trusted + assert_balances(self.nodes[1], mine={"used": 10, "trusted": 1}) # node 1 should now have about 1 BCH left (no dirty) and 11 (including # dirty)