Skip to content

Commit

Permalink
Merge #13566: Fix get balance
Browse files Browse the repository at this point in the history
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
  • Loading branch information
sipa committed Jul 14, 2018
2 parents 90b1c7e + 702ae1e commit ad552a5
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 89 deletions.
2 changes: 1 addition & 1 deletion src/interfaces/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ class WalletImpl : public Wallet
result.immature_balance = m_wallet.GetImmatureBalance();
result.have_watch_only = m_wallet.HaveWatchOnly();
if (result.have_watch_only) {
result.watch_only_balance = m_wallet.GetWatchOnlyBalance();
result.watch_only_balance = m_wallet.GetBalance(ISMINE_WATCH_ONLY);
result.unconfirmed_watch_only_balance = m_wallet.GetUnconfirmedWatchOnlyBalance();
result.immature_watch_only_balance = m_wallet.GetImmatureWatchOnlyBalance();
}
Expand Down
65 changes: 36 additions & 29 deletions src/wallet/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -852,8 +852,9 @@ static UniValue getbalance(const JSONRPCRequest& request)
return NullUniValue;
}

if (request.fHelp || (request.params.size() > 3 && IsDeprecatedRPCEnabled("accounts")) || (request.params.size() != 0 && !IsDeprecatedRPCEnabled("accounts")))
if (request.fHelp || (request.params.size() > 3 ))
throw std::runtime_error(
(IsDeprecatedRPCEnabled("accounts") ? std::string(
"getbalance ( \"account\" minconf include_watchonly )\n"
"\nIf account is not specified, returns the server's total available balance.\n"
"The available balance is what the wallet considers currently spendable, and is\n"
Expand All @@ -875,8 +876,17 @@ static UniValue getbalance(const JSONRPCRequest& request)
" balances. In general, account balance calculation is not considered\n"
" reliable and has resulted in confusing outcomes, so it is recommended to\n"
" avoid passing this argument.\n"
"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"
"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"
"2. minconf (numeric, optional) Only include transactions confirmed at least this many times. \n"
" The default is 1 if an account is provided or 0 if no account is provided\n")
: std::string(
"getbalance ( \"(dummy)\" minconf include_watchonly )\n"
"\nReturns the total available balance.\n"
"The available balance is what the wallet considers currently spendable, and is\n"
"thus affected by options which limit spendability such as -spendzeroconfchange.\n"
"\nArguments:\n"
"1. (dummy) (string, optional) Remains for backward compatibility. Must be excluded or set to \"*\".\n"
"2. minconf (numeric, optional, default=0) Only include transactions confirmed at least this many times.\n")) +
"3. include_watchonly (bool, optional, default=false) Also include balance in watch-only addresses (see 'importaddress')\n"
"\nResult:\n"
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n"
"\nExamples:\n"
Expand All @@ -894,38 +904,35 @@ static UniValue getbalance(const JSONRPCRequest& request)

LOCK2(cs_main, pwallet->cs_wallet);

if (IsDeprecatedRPCEnabled("accounts")) {
const UniValue& account_value = request.params[0];
const UniValue& minconf = request.params[1];
const UniValue& include_watchonly = request.params[2];
const UniValue& account_value = request.params[0];

if (account_value.isNull()) {
if (!minconf.isNull()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"getbalance minconf option is only currently supported if an account is specified");
}
if (!include_watchonly.isNull()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"getbalance include_watchonly option is only currently supported if an account is specified");
}
return ValueFromAmount(pwallet->GetBalance());
}
int min_depth = 0;
if (IsDeprecatedRPCEnabled("accounts") && !account_value.isNull()) {
// Default min_depth to 1 when an account is provided.
min_depth = 1;
}
if (!request.params[1].isNull()) {
min_depth = request.params[1].get_int();
}

isminefilter filter = ISMINE_SPENDABLE;
if (!request.params[2].isNull() && request.params[2].get_bool()) {
filter = filter | ISMINE_WATCH_ONLY;
}

if (!account_value.isNull()) {

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

int nMinDepth = 1;
if (!minconf.isNull())
nMinDepth = minconf.get_int();
isminefilter filter = ISMINE_SPENDABLE;
if(!include_watchonly.isNull())
if(include_watchonly.get_bool())
filter = filter | ISMINE_WATCH_ONLY;

return ValueFromAmount(pwallet->GetLegacyBalance(filter, nMinDepth, account));
if (!IsDeprecatedRPCEnabled("accounts") && account_param != "*") {
throw JSONRPCError(RPC_METHOD_DEPRECATED, "dummy first argument must be excluded or set to \"*\".");
} else if (IsDeprecatedRPCEnabled("accounts")) {
return ValueFromAmount(pwallet->GetLegacyBalance(filter, min_depth, account));
}
}

return ValueFromAmount(pwallet->GetBalance());
return ValueFromAmount(pwallet->GetBalance(filter, min_depth));
}

static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
Expand Down Expand Up @@ -4421,7 +4428,7 @@ static const CRPCCommand commands[] =
{ "wallet", "dumpwallet", &dumpwallet, {"filename"} },
{ "wallet", "encryptwallet", &encryptwallet, {"passphrase"} },
{ "wallet", "getaddressinfo", &getaddressinfo, {"address"} },
{ "wallet", "getbalance", &getbalance, {"account","minconf","include_watchonly"} },
{ "wallet", "getbalance", &getbalance, {"account|dummy","minconf","include_watchonly"} },
{ "wallet", "getnewaddress", &getnewaddress, {"label|account","address_type"} },
{ "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} },
{ "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} },
Expand Down
80 changes: 25 additions & 55 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1929,7 +1929,7 @@ CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
return 0;
}

CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter) const
{
if (pwallet == nullptr)
return 0;
Expand All @@ -1938,8 +1938,20 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
if (IsCoinBase() && GetBlocksToMaturity() > 0)
return 0;

if (fUseCache && fAvailableCreditCached)
return nAvailableCreditCached;
CAmount* cache = nullptr;
bool* cache_used = nullptr;

if (filter == ISMINE_SPENDABLE) {
cache = &nAvailableCreditCached;
cache_used = &fAvailableCreditCached;
} else if (filter == ISMINE_WATCH_ONLY) {
cache = &nAvailableWatchCreditCached;
cache_used = &fAvailableWatchCreditCached;
}

if (fUseCache && cache_used && *cache_used) {
return *cache;
}

CAmount nCredit = 0;
uint256 hashTx = GetHash();
Expand All @@ -1948,14 +1960,16 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
if (!pwallet->IsSpent(hashTx, i))
{
const CTxOut &txout = tx->vout[i];
nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE);
nCredit += pwallet->GetCredit(txout, filter);
if (!MoneyRange(nCredit))
throw std::runtime_error(std::string(__func__) + " : value out of range");
}
}

nAvailableCreditCached = nCredit;
fAvailableCreditCached = true;
if (cache) {
*cache = nCredit;
*cache_used = true;
}
return nCredit;
}

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

CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool fUseCache) const
{
if (pwallet == nullptr)
return 0;

// Must wait until coinbase is safely deep enough in the chain before valuing it
if (IsCoinBase() && GetBlocksToMaturity() > 0)
return 0;

if (fUseCache && fAvailableWatchCreditCached)
return nAvailableWatchCreditCached;

CAmount nCredit = 0;
for (unsigned int i = 0; i < tx->vout.size(); i++)
{
if (!pwallet->IsSpent(GetHash(), i))
{
const CTxOut &txout = tx->vout[i];
nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY);
if (!MoneyRange(nCredit))
throw std::runtime_error(std::string(__func__) + ": value out of range");
}
}

nAvailableWatchCreditCached = nCredit;
fAvailableWatchCreditCached = true;
return nCredit;
}

CAmount CWalletTx::GetChange() const
{
if (fChangeCached)
Expand Down Expand Up @@ -2115,16 +2100,17 @@ void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman
*/


CAmount CWallet::GetBalance() const
CAmount CWallet::GetBalance(const isminefilter& filter, const int min_depth) const
{
CAmount nTotal = 0;
{
LOCK2(cs_main, cs_wallet);
for (const auto& entry : mapWallet)
{
const CWalletTx* pcoin = &entry.second;
if (pcoin->IsTrusted())
nTotal += pcoin->GetAvailableCredit();
if (pcoin->IsTrusted() && pcoin->GetDepthInMainChain() >= min_depth) {
nTotal += pcoin->GetAvailableCredit(true, filter);
}
}
}

Expand Down Expand Up @@ -2160,22 +2146,6 @@ CAmount CWallet::GetImmatureBalance() const
return nTotal;
}

CAmount CWallet::GetWatchOnlyBalance() const
{
CAmount nTotal = 0;
{
LOCK2(cs_main, cs_wallet);
for (const auto& entry : mapWallet)
{
const CWalletTx* pcoin = &entry.second;
if (pcoin->IsTrusted())
nTotal += pcoin->GetAvailableWatchOnlyCredit();
}
}

return nTotal;
}

CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
{
CAmount nTotal = 0;
Expand All @@ -2185,7 +2155,7 @@ CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
{
const CWalletTx* pcoin = &entry.second;
if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool())
nTotal += pcoin->GetAvailableWatchOnlyCredit();
nTotal += pcoin->GetAvailableCredit(true, ISMINE_WATCH_ONLY);
}
}
return nTotal;
Expand Down
6 changes: 2 additions & 4 deletions src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -459,9 +459,8 @@ class CWalletTx : public CMerkleTx
CAmount GetDebit(const isminefilter& filter) const;
CAmount GetCredit(const isminefilter& filter) const;
CAmount GetImmatureCredit(bool fUseCache=true) const;
CAmount GetAvailableCredit(bool fUseCache=true) const;
CAmount GetAvailableCredit(bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const;
CAmount GetImmatureWatchOnlyCredit(const bool fUseCache=true) const;
CAmount GetAvailableWatchOnlyCredit(const bool fUseCache=true) const;
CAmount GetChange() const;

// Get the marginal bytes if spending the specified output from this transaction
Expand Down Expand Up @@ -941,10 +940,9 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface
void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override;
// ResendWalletTransactionsBefore may only be called if fBroadcastTransactions!
std::vector<uint256> ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman);
CAmount GetBalance() const;
CAmount GetBalance(const isminefilter& filter=ISMINE_SPENDABLE, const int min_depth=0) const;
CAmount GetUnconfirmedBalance() const;
CAmount GetImmatureBalance() const;
CAmount GetWatchOnlyBalance() const;
CAmount GetUnconfirmedWatchOnlyBalance() const;
CAmount GetImmatureWatchOnlyBalance() const;
CAmount GetLegacyBalance(const isminefilter& filter, int minDepth, const std::string* account) const;
Expand Down
9 changes: 9 additions & 0 deletions test/functional/wallet_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ def run_test(self):
assert_equal(self.nodes[1].getbalance(), 50)
assert_equal(self.nodes[2].getbalance(), 0)

# Check getbalance with different arguments
assert_equal(self.nodes[0].getbalance("*"), 50)
assert_equal(self.nodes[0].getbalance("*", 1), 50)
assert_equal(self.nodes[0].getbalance("*", 1, True), 50)
assert_equal(self.nodes[0].getbalance(minconf=1), 50)

# first argument of getbalance must be excluded or set to "*"
assert_raises_rpc_error(-32, "dummy first argument must be excluded or set to \"*\"", self.nodes[0].getbalance, "")

# Check that only first and second nodes have UTXOs
utxos = self.nodes[0].listunspent()
assert_equal(len(utxos), 1)
Expand Down

0 comments on commit ad552a5

Please sign in to comment.