Merge #13566: Fix get balance

702ae1e21a [RPC] [wallet] allow getbalance to use min_conf and watch_only without accounts. (John Newbery)
cf15761f6d [wallet] GetBalance can take a min_depth argument. (John Newbery)
0f3d6e9ab7 [wallet] factor out GetAvailableWatchOnlyBalance() (John Newbery)
7110c830f8 [wallet] deduplicate GetAvailableCredit logic (John Newbery)
ef7bc8893c [wallet] Factor out GetWatchOnlyBalance() (John Newbery)
4279da4785 [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
This commit is contained in:
Pieter Wuille 2018-07-13 19:40:31 -07:00 committed by xdustinface
parent b84b7da8c3
commit 341b5a9714
5 changed files with 80 additions and 92 deletions

View File

@ -382,7 +382,7 @@ public:
result.anonymized_balance = m_wallet.GetAnonymizedBalance();
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();
}

View File

@ -868,8 +868,9 @@ UniValue getbalance(const JSONRPCRequest& request)
return NullUniValue;
}
if (request.fHelp || (request.params.size() > 4 && IsDeprecatedRPCEnabled("accounts")) || (request.params.size() != 0 && !IsDeprecatedRPCEnabled("accounts")))
if (request.fHelp || (request.params.size() > 4))
throw std::runtime_error(
(IsDeprecatedRPCEnabled("accounts") ? std::string(
"getbalance ( \"account\" minconf addlocked 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"
@ -881,9 +882,18 @@ UniValue getbalance(const JSONRPCRequest& request)
"1. \"account\" (string, optional) DEPRECATED. This argument will be removed in V0.18. \n"
" To use this deprecated argument, start dashd with -deprecatedrpc=accounts.\n"
" The selected account, or \"*\" for entire wallet. It may be the default account using \"\".\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 dashd with -deprecatedrpc=accounts. Only include transactions confirmed at least this many times.\n"
"3. addlocked (bool, optional, default=false) Whether to include the value of transactions locked via InstantSend in the wallet's balance.\n"
"4. 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 dashd 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 addlocked 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. addlocked (bool, optional, default=false) Whether to include the value of transactions locked via InstantSend in the wallet's balance.\n"
"4. 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"
@ -901,42 +911,41 @@ 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& addlocked = request.params[2];
const UniValue& include_watchonly = request.params[3];
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();
}
const UniValue& addlocked = request.params[2];
bool fAddLocked = false;
if (!addlocked.isNull()) {
fAddLocked = addlocked.get_bool();
}
isminefilter filter = ISMINE_SPENDABLE;
if (!request.params[3].isNull() && request.params[3].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();
bool fAddLocked = false;
if (!addlocked.isNull())
fAddLocked = addlocked.get_bool();
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, fAddLocked));
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, fAddLocked));
}
}
return ValueFromAmount(pwallet->GetBalance());
return ValueFromAmount(pwallet->GetBalance(filter, min_depth));
}
UniValue getunconfirmedbalance(const JSONRPCRequest &request)
@ -4107,7 +4116,7 @@ static const CRPCCommand commands[] =
{ "wallet", "dumpwallet", &dumpwallet, {"filename"} },
{ "wallet", "encryptwallet", &encryptwallet, {"passphrase"} },
{ "wallet", "getaddressinfo", &getaddressinfo, {"address"} },
{ "wallet", "getbalance", &getbalance, {"account","minconf","addlocked","include_watchonly"} },
{ "wallet", "getbalance", &getbalance, {"account|dummy","minconf","addlocked","include_watchonly"} },
{ "wallet", "getnewaddress", &getnewaddress, {"label|account"} },
{ "wallet", "getrawchangeaddress", &getrawchangeaddress, {} },
{ "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf","addlocked"} },

View File

@ -2333,7 +2333,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;
@ -2342,8 +2342,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();
@ -2352,14 +2364,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;
}
@ -2377,35 +2391,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::GetAnonymizedCredit(const CCoinControl* coinControl) const
{
if (!pwallet)
@ -2627,14 +2612,15 @@ std::unordered_set<const CWalletTx*, WalletTxHasher> CWallet::GetSpendableTXs()
return ret;
}
CAmount CWallet::GetBalance() const
CAmount CWallet::GetBalance(const isminefilter& filter, const int min_depth) const
{
CAmount nTotal = 0;
{
LOCK2(cs_main, cs_wallet);
for (auto pcoin : GetSpendableTXs()) {
if (pcoin->IsTrusted())
nTotal += pcoin->GetAvailableCredit();
if (pcoin->IsTrusted() && pcoin->GetDepthInMainChain() >= min_depth) {
nTotal += pcoin->GetAvailableCredit(true, filter);
}
}
}
@ -2764,20 +2750,6 @@ CAmount CWallet::GetImmatureBalance() const
return nTotal;
}
CAmount CWallet::GetWatchOnlyBalance() const
{
CAmount nTotal = 0;
{
LOCK2(cs_main, cs_wallet);
for (auto pcoin : GetSpendableTXs()) {
if (pcoin->IsTrusted())
nTotal += pcoin->GetAvailableWatchOnlyCredit();
}
}
return nTotal;
}
CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
{
CAmount nTotal = 0;
@ -2785,7 +2757,7 @@ CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
LOCK2(cs_main, cs_wallet);
for (auto pcoin : GetSpendableTXs()) {
if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && !pcoin->IsLockedByInstantSend() && pcoin->InMempool())
nTotal += pcoin->GetAvailableWatchOnlyCredit();
nTotal += pcoin->GetAvailableCredit(true, ISMINE_WATCH_ONLY);
}
}
return nTotal;

View File

@ -474,9 +474,8 @@ public:
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;
CAmount GetAnonymizedCredit(const CCoinControl* coinControl = nullptr) const;
@ -1047,10 +1046,9 @@ public:
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 bool fAddLocked) const;

View File

@ -52,6 +52,15 @@ class WalletTest(BitcoinTestFramework):
assert_equal(self.nodes[1].getbalance(), 500)
assert_equal(self.nodes[2].getbalance(), 0)
# Check getbalance with different arguments
assert_equal(self.nodes[0].getbalance("*"), 500)
assert_equal(self.nodes[0].getbalance("*", 1), 500)
assert_equal(self.nodes[0].getbalance("*", 1, True), 500)
assert_equal(self.nodes[0].getbalance(minconf=1), 500)
# 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)