Skip to content

Commit ad552a5

Browse files
committed
Merge #13566: Fix get balance
702ae1e [RPC] [wallet] allow getbalance to use min_conf and watch_only without accounts. (John Newbery) cf15761 [wallet] GetBalance can take a min_depth argument. (John Newbery) 0f3d6e9 [wallet] factor out GetAvailableWatchOnlyBalance() (John Newbery) 7110c83 [wallet] deduplicate GetAvailableCredit logic (John Newbery) ef7bc88 [wallet] Factor out GetWatchOnlyBalance() (John Newbery) 4279da4 [wallet] GetBalance can take an isminefilter filter. (John Newbery) Pull request description: #12953 inadvertently removed the functionality to call `getbalance "*" <int> <bool>` to get the wallet's balance with either minconfs or include_watchonly. This restores that functionality (when `-deprecatedrpc=accounts`), and also makes it possible to call ``getbalance minconf=<int> include_watchonly=<bool>` when accounts are not being used. Tree-SHA512: 67e84de9291ed6d34b23c626f4dc5988ba0ae6c99708d02b87dd3aaad3f4b6baa6202a66cc2dadd30dd993a39de8036ee920fcaa8cbb1c5dfe606e6fac183344
2 parents 90b1c7e + 702ae1e commit ad552a5

File tree

5 files changed

+73
-89
lines changed

5 files changed

+73
-89
lines changed

src/interfaces/wallet.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ class WalletImpl : public Wallet
338338
result.immature_balance = m_wallet.GetImmatureBalance();
339339
result.have_watch_only = m_wallet.HaveWatchOnly();
340340
if (result.have_watch_only) {
341-
result.watch_only_balance = m_wallet.GetWatchOnlyBalance();
341+
result.watch_only_balance = m_wallet.GetBalance(ISMINE_WATCH_ONLY);
342342
result.unconfirmed_watch_only_balance = m_wallet.GetUnconfirmedWatchOnlyBalance();
343343
result.immature_watch_only_balance = m_wallet.GetImmatureWatchOnlyBalance();
344344
}

src/wallet/rpcwallet.cpp

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -852,8 +852,9 @@ static UniValue getbalance(const JSONRPCRequest& request)
852852
return NullUniValue;
853853
}
854854

855-
if (request.fHelp || (request.params.size() > 3 && IsDeprecatedRPCEnabled("accounts")) || (request.params.size() != 0 && !IsDeprecatedRPCEnabled("accounts")))
855+
if (request.fHelp || (request.params.size() > 3 ))
856856
throw std::runtime_error(
857+
(IsDeprecatedRPCEnabled("accounts") ? std::string(
857858
"getbalance ( \"account\" minconf include_watchonly )\n"
858859
"\nIf account is not specified, returns the server's total available balance.\n"
859860
"The available balance is what the wallet considers currently spendable, and is\n"
@@ -875,8 +876,17 @@ static UniValue getbalance(const JSONRPCRequest& request)
875876
" balances. In general, account balance calculation is not considered\n"
876877
" reliable and has resulted in confusing outcomes, so it is recommended to\n"
877878
" avoid passing this argument.\n"
878-
"2. minconf (numeric, optional, default=1) DEPRECATED. Only valid when an account is specified. This argument will be removed in V0.18. To use this deprecated argument, start bitcoind with -deprecatedrpc=accounts. Only include transactions confirmed at least this many times.\n"
879-
"3. include_watchonly (bool, optional, default=false) DEPRECATED. Only valid when an account is specified. This argument will be removed in V0.18. To use this deprecated argument, start bitcoind with -deprecatedrpc=accounts. Also include balance in watch-only addresses (see 'importaddress')\n"
879+
"2. minconf (numeric, optional) Only include transactions confirmed at least this many times. \n"
880+
" The default is 1 if an account is provided or 0 if no account is provided\n")
881+
: std::string(
882+
"getbalance ( \"(dummy)\" minconf include_watchonly )\n"
883+
"\nReturns the total available balance.\n"
884+
"The available balance is what the wallet considers currently spendable, and is\n"
885+
"thus affected by options which limit spendability such as -spendzeroconfchange.\n"
886+
"\nArguments:\n"
887+
"1. (dummy) (string, optional) Remains for backward compatibility. Must be excluded or set to \"*\".\n"
888+
"2. minconf (numeric, optional, default=0) Only include transactions confirmed at least this many times.\n")) +
889+
"3. include_watchonly (bool, optional, default=false) Also include balance in watch-only addresses (see 'importaddress')\n"
880890
"\nResult:\n"
881891
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n"
882892
"\nExamples:\n"
@@ -894,38 +904,35 @@ static UniValue getbalance(const JSONRPCRequest& request)
894904

895905
LOCK2(cs_main, pwallet->cs_wallet);
896906

897-
if (IsDeprecatedRPCEnabled("accounts")) {
898-
const UniValue& account_value = request.params[0];
899-
const UniValue& minconf = request.params[1];
900-
const UniValue& include_watchonly = request.params[2];
907+
const UniValue& account_value = request.params[0];
901908

902-
if (account_value.isNull()) {
903-
if (!minconf.isNull()) {
904-
throw JSONRPCError(RPC_INVALID_PARAMETER,
905-
"getbalance minconf option is only currently supported if an account is specified");
906-
}
907-
if (!include_watchonly.isNull()) {
908-
throw JSONRPCError(RPC_INVALID_PARAMETER,
909-
"getbalance include_watchonly option is only currently supported if an account is specified");
910-
}
911-
return ValueFromAmount(pwallet->GetBalance());
912-
}
909+
int min_depth = 0;
910+
if (IsDeprecatedRPCEnabled("accounts") && !account_value.isNull()) {
911+
// Default min_depth to 1 when an account is provided.
912+
min_depth = 1;
913+
}
914+
if (!request.params[1].isNull()) {
915+
min_depth = request.params[1].get_int();
916+
}
917+
918+
isminefilter filter = ISMINE_SPENDABLE;
919+
if (!request.params[2].isNull() && request.params[2].get_bool()) {
920+
filter = filter | ISMINE_WATCH_ONLY;
921+
}
922+
923+
if (!account_value.isNull()) {
913924

914925
const std::string& account_param = account_value.get_str();
915926
const std::string* account = account_param != "*" ? &account_param : nullptr;
916927

917-
int nMinDepth = 1;
918-
if (!minconf.isNull())
919-
nMinDepth = minconf.get_int();
920-
isminefilter filter = ISMINE_SPENDABLE;
921-
if(!include_watchonly.isNull())
922-
if(include_watchonly.get_bool())
923-
filter = filter | ISMINE_WATCH_ONLY;
924-
925-
return ValueFromAmount(pwallet->GetLegacyBalance(filter, nMinDepth, account));
928+
if (!IsDeprecatedRPCEnabled("accounts") && account_param != "*") {
929+
throw JSONRPCError(RPC_METHOD_DEPRECATED, "dummy first argument must be excluded or set to \"*\".");
930+
} else if (IsDeprecatedRPCEnabled("accounts")) {
931+
return ValueFromAmount(pwallet->GetLegacyBalance(filter, min_depth, account));
932+
}
926933
}
927934

928-
return ValueFromAmount(pwallet->GetBalance());
935+
return ValueFromAmount(pwallet->GetBalance(filter, min_depth));
929936
}
930937

931938
static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
@@ -4421,7 +4428,7 @@ static const CRPCCommand commands[] =
44214428
{ "wallet", "dumpwallet", &dumpwallet, {"filename"} },
44224429
{ "wallet", "encryptwallet", &encryptwallet, {"passphrase"} },
44234430
{ "wallet", "getaddressinfo", &getaddressinfo, {"address"} },
4424-
{ "wallet", "getbalance", &getbalance, {"account","minconf","include_watchonly"} },
4431+
{ "wallet", "getbalance", &getbalance, {"account|dummy","minconf","include_watchonly"} },
44254432
{ "wallet", "getnewaddress", &getnewaddress, {"label|account","address_type"} },
44264433
{ "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} },
44274434
{ "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} },

src/wallet/wallet.cpp

Lines changed: 25 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1929,7 +1929,7 @@ CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
19291929
return 0;
19301930
}
19311931

1932-
CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
1932+
CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter) const
19331933
{
19341934
if (pwallet == nullptr)
19351935
return 0;
@@ -1938,8 +1938,20 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
19381938
if (IsCoinBase() && GetBlocksToMaturity() > 0)
19391939
return 0;
19401940

1941-
if (fUseCache && fAvailableCreditCached)
1942-
return nAvailableCreditCached;
1941+
CAmount* cache = nullptr;
1942+
bool* cache_used = nullptr;
1943+
1944+
if (filter == ISMINE_SPENDABLE) {
1945+
cache = &nAvailableCreditCached;
1946+
cache_used = &fAvailableCreditCached;
1947+
} else if (filter == ISMINE_WATCH_ONLY) {
1948+
cache = &nAvailableWatchCreditCached;
1949+
cache_used = &fAvailableWatchCreditCached;
1950+
}
1951+
1952+
if (fUseCache && cache_used && *cache_used) {
1953+
return *cache;
1954+
}
19431955

19441956
CAmount nCredit = 0;
19451957
uint256 hashTx = GetHash();
@@ -1948,14 +1960,16 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
19481960
if (!pwallet->IsSpent(hashTx, i))
19491961
{
19501962
const CTxOut &txout = tx->vout[i];
1951-
nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE);
1963+
nCredit += pwallet->GetCredit(txout, filter);
19521964
if (!MoneyRange(nCredit))
19531965
throw std::runtime_error(std::string(__func__) + " : value out of range");
19541966
}
19551967
}
19561968

1957-
nAvailableCreditCached = nCredit;
1958-
fAvailableCreditCached = true;
1969+
if (cache) {
1970+
*cache = nCredit;
1971+
*cache_used = true;
1972+
}
19591973
return nCredit;
19601974
}
19611975

@@ -1973,35 +1987,6 @@ CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool fUseCache) const
19731987
return 0;
19741988
}
19751989

1976-
CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool fUseCache) const
1977-
{
1978-
if (pwallet == nullptr)
1979-
return 0;
1980-
1981-
// Must wait until coinbase is safely deep enough in the chain before valuing it
1982-
if (IsCoinBase() && GetBlocksToMaturity() > 0)
1983-
return 0;
1984-
1985-
if (fUseCache && fAvailableWatchCreditCached)
1986-
return nAvailableWatchCreditCached;
1987-
1988-
CAmount nCredit = 0;
1989-
for (unsigned int i = 0; i < tx->vout.size(); i++)
1990-
{
1991-
if (!pwallet->IsSpent(GetHash(), i))
1992-
{
1993-
const CTxOut &txout = tx->vout[i];
1994-
nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY);
1995-
if (!MoneyRange(nCredit))
1996-
throw std::runtime_error(std::string(__func__) + ": value out of range");
1997-
}
1998-
}
1999-
2000-
nAvailableWatchCreditCached = nCredit;
2001-
fAvailableWatchCreditCached = true;
2002-
return nCredit;
2003-
}
2004-
20051990
CAmount CWalletTx::GetChange() const
20061991
{
20071992
if (fChangeCached)
@@ -2115,16 +2100,17 @@ void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman
21152100
*/
21162101

21172102

2118-
CAmount CWallet::GetBalance() const
2103+
CAmount CWallet::GetBalance(const isminefilter& filter, const int min_depth) const
21192104
{
21202105
CAmount nTotal = 0;
21212106
{
21222107
LOCK2(cs_main, cs_wallet);
21232108
for (const auto& entry : mapWallet)
21242109
{
21252110
const CWalletTx* pcoin = &entry.second;
2126-
if (pcoin->IsTrusted())
2127-
nTotal += pcoin->GetAvailableCredit();
2111+
if (pcoin->IsTrusted() && pcoin->GetDepthInMainChain() >= min_depth) {
2112+
nTotal += pcoin->GetAvailableCredit(true, filter);
2113+
}
21282114
}
21292115
}
21302116

@@ -2160,22 +2146,6 @@ CAmount CWallet::GetImmatureBalance() const
21602146
return nTotal;
21612147
}
21622148

2163-
CAmount CWallet::GetWatchOnlyBalance() const
2164-
{
2165-
CAmount nTotal = 0;
2166-
{
2167-
LOCK2(cs_main, cs_wallet);
2168-
for (const auto& entry : mapWallet)
2169-
{
2170-
const CWalletTx* pcoin = &entry.second;
2171-
if (pcoin->IsTrusted())
2172-
nTotal += pcoin->GetAvailableWatchOnlyCredit();
2173-
}
2174-
}
2175-
2176-
return nTotal;
2177-
}
2178-
21792149
CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
21802150
{
21812151
CAmount nTotal = 0;
@@ -2185,7 +2155,7 @@ CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
21852155
{
21862156
const CWalletTx* pcoin = &entry.second;
21872157
if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool())
2188-
nTotal += pcoin->GetAvailableWatchOnlyCredit();
2158+
nTotal += pcoin->GetAvailableCredit(true, ISMINE_WATCH_ONLY);
21892159
}
21902160
}
21912161
return nTotal;

src/wallet/wallet.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -459,9 +459,8 @@ class CWalletTx : public CMerkleTx
459459
CAmount GetDebit(const isminefilter& filter) const;
460460
CAmount GetCredit(const isminefilter& filter) const;
461461
CAmount GetImmatureCredit(bool fUseCache=true) const;
462-
CAmount GetAvailableCredit(bool fUseCache=true) const;
462+
CAmount GetAvailableCredit(bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const;
463463
CAmount GetImmatureWatchOnlyCredit(const bool fUseCache=true) const;
464-
CAmount GetAvailableWatchOnlyCredit(const bool fUseCache=true) const;
465464
CAmount GetChange() const;
466465

467466
// Get the marginal bytes if spending the specified output from this transaction
@@ -941,10 +940,9 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface
941940
void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override;
942941
// ResendWalletTransactionsBefore may only be called if fBroadcastTransactions!
943942
std::vector<uint256> ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman);
944-
CAmount GetBalance() const;
943+
CAmount GetBalance(const isminefilter& filter=ISMINE_SPENDABLE, const int min_depth=0) const;
945944
CAmount GetUnconfirmedBalance() const;
946945
CAmount GetImmatureBalance() const;
947-
CAmount GetWatchOnlyBalance() const;
948946
CAmount GetUnconfirmedWatchOnlyBalance() const;
949947
CAmount GetImmatureWatchOnlyBalance() const;
950948
CAmount GetLegacyBalance(const isminefilter& filter, int minDepth, const std::string* account) const;

test/functional/wallet_basic.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ def run_test(self):
6464
assert_equal(self.nodes[1].getbalance(), 50)
6565
assert_equal(self.nodes[2].getbalance(), 0)
6666

67+
# Check getbalance with different arguments
68+
assert_equal(self.nodes[0].getbalance("*"), 50)
69+
assert_equal(self.nodes[0].getbalance("*", 1), 50)
70+
assert_equal(self.nodes[0].getbalance("*", 1, True), 50)
71+
assert_equal(self.nodes[0].getbalance(minconf=1), 50)
72+
73+
# first argument of getbalance must be excluded or set to "*"
74+
assert_raises_rpc_error(-32, "dummy first argument must be excluded or set to \"*\"", self.nodes[0].getbalance, "")
75+
6776
# Check that only first and second nodes have UTXOs
6877
utxos = self.nodes[0].listunspent()
6978
assert_equal(len(utxos), 1)

0 commit comments

Comments
 (0)