diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -911,6 +911,10 @@ return ValueFromAmount(pwallet->GetBalance()); } + const std::string *account = request.params[0].get_str() != "*" + ? &request.params[0].get_str() + : nullptr; + int nMinDepth = 1; if (request.params.size() > 1) { nMinDepth = request.params[1].get_int(); @@ -921,49 +925,8 @@ filter = filter | ISMINE_WATCH_ONLY; } - if (request.params[0].get_str() == "*") { - // Calculate total balance in a very different way from GetBalance(). - // The biggest difference is that GetBalance() sums up all unspent - // TxOuts paying to the wallet, while this sums up both spent and - // unspent TxOuts paying to the wallet, and then subtracts the values of - // TxIns spending from the wallet. This also has fewer restrictions on - // which unconfirmed transactions are considered trusted. - Amount nBalance(0); - for (const std::pair<uint256, CWalletTx> &pairWtx : - pwallet->mapWallet) { - const CWalletTx &wtx = pairWtx.second; - CValidationState state; - if (!ContextualCheckTransactionForCurrentBlock(config, wtx, - state) || - wtx.GetBlocksToMaturity() > 0 || - wtx.GetDepthInMainChain() < 0) { - continue; - } - - Amount allFee; - std::string strSentAccount; - std::list<COutputEntry> listReceived; - std::list<COutputEntry> listSent; - wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, - filter); - if (wtx.GetDepthInMainChain() >= nMinDepth) { - for (const COutputEntry &r : listReceived) { - nBalance += r.amount; - } - } - for (const COutputEntry &s : listSent) { - nBalance -= s.amount; - } - nBalance -= allFee; - } - return ValueFromAmount(nBalance); - } - - std::string strAccount = AccountFromValue(request.params[0]); - - Amount nBalance = pwallet->GetAccountBalance(strAccount, nMinDepth, filter); - - return ValueFromAmount(nBalance); + return ValueFromAmount( + pwallet->GetLegacyBalance(filter, nMinDepth, account)); } static UniValue getunconfirmedbalance(const Config &config, @@ -1147,7 +1110,7 @@ // Check funds Amount nBalance = - pwallet->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); + pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &strAccount); if (nAmount > nBalance) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); @@ -1307,7 +1270,7 @@ // Check funds Amount nBalance = - pwallet->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); + pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &strAccount); if (totalAmount > nBalance) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -436,10 +436,6 @@ std::string &strSentAccount, const isminefilter &filter) const; - void GetAccountAmounts(const std::string &strAccount, Amount &nReceived, - Amount &nSent, Amount &nFee, - const isminefilter &filter) const; - bool IsFromMe(const isminefilter &filter) const { return (GetDebit(filter) > Amount(0)); } @@ -899,6 +895,8 @@ Amount GetWatchOnlyBalance() const; Amount GetUnconfirmedWatchOnlyBalance() const; Amount GetImmatureWatchOnlyBalance() const; + Amount GetLegacyBalance(const isminefilter &filter, int minDepth, + const std::string *account) const; /** * Insert additional inputs into the transaction by calling @@ -979,10 +977,6 @@ std::set<std::set<CTxDestination>> GetAddressGroupings(); std::map<CTxDestination, Amount> GetAddressBalances(); - Amount GetAccountBalance(const std::string &strAccount, int nMinDepth, - const isminefilter &filter); - Amount GetAccountBalance(CWalletDB &walletdb, const std::string &strAccount, - int nMinDepth, const isminefilter &filter); std::set<CTxDestination> GetAccountAddresses(const std::string &strAccount) const; @@ -1016,6 +1010,8 @@ bool DelAddressBook(const CTxDestination &address); + const std::string &GetAccountName(const CScript &scriptPubKey) const; + void Inventory(const uint256 &hash) override { LOCK(cs_wallet); std::map<uint256, int>::iterator mi = mapRequestCount.find(hash); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1696,40 +1696,6 @@ } } -void CWalletTx::GetAccountAmounts(const std::string &strAccount, - Amount &nReceived, Amount &nSent, - Amount &nFee, - const isminefilter &filter) const { - nReceived = nSent = nFee = Amount(0); - - Amount allFee; - std::string strSentAccount; - std::list<COutputEntry> listReceived; - std::list<COutputEntry> listSent; - GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); - - if (strAccount == strSentAccount) { - for (const COutputEntry &s : listSent) { - nSent += s.amount; - } - nFee = allFee; - } - - LOCK(pwallet->cs_wallet); - for (const COutputEntry &r : listReceived) { - if (pwallet->mapAddressBook.count(r.destination)) { - std::map<CTxDestination, CAddressBookData>::const_iterator mi = - pwallet->mapAddressBook.find(r.destination); - if (mi != pwallet->mapAddressBook.end() && - (*mi).second.name == strAccount) { - nReceived += r.amount; - } - } else if (strAccount.empty()) { - nReceived += r.amount; - } - } -} - /** * Scan the block chain (starting in pindexStart) for transactions from or to * us. If fUpdate is true, found transactions that already exist in the wallet @@ -2251,6 +2217,52 @@ return nTotal; } +// Calculate total balance in a different way from GetBalance. The biggest +// difference is that GetBalance sums up all unspent TxOuts paying to the +// wallet, while this sums up both spent and unspent TxOuts paying to the +// wallet, and then subtracts the values of TxIns spending from the wallet. This +// also has fewer restrictions on which unconfirmed transactions are considered +// trusted. +Amount CWallet::GetLegacyBalance(const isminefilter &filter, int minDepth, + const std::string *account) const { + LOCK2(cs_main, cs_wallet); + + Amount balance(0); + for (const auto &entry : mapWallet) { + const CWalletTx &wtx = entry.second; + const int depth = wtx.GetDepthInMainChain(); + if (depth < 0 || !CheckFinalTx(*wtx.tx) || + wtx.GetBlocksToMaturity() > 0) { + continue; + } + + // Loop through tx outputs and add incoming payments. For outgoing txs, + // treat change outputs specially, as part of the amount debited. + Amount debit = wtx.GetDebit(filter); + const bool outgoing = debit > Amount(0); + for (const CTxOut &out : wtx.tx->vout) { + if (outgoing && IsChange(out)) { + debit -= out.nValue; + } else if (IsMine(out) & filter && depth >= minDepth && + (!account || + *account == GetAccountName(out.scriptPubKey))) { + balance += out.nValue; + } + } + + // For outgoing txs, subtract amount debited. + if (outgoing && (!account || *account == wtx.strFromAccount)) { + balance -= debit; + } + } + + if (account) { + balance += CWalletDB(*dbw).GetAccountCreditDebit(*account); + } + + return balance; +} + void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const CCoinControl *coinControl, bool fIncludeZeroValue) const { @@ -3298,6 +3310,21 @@ return CWalletDB(*dbw).EraseName(address); } +const std::string &CWallet::GetAccountName(const CScript &scriptPubKey) const { + CTxDestination address; + if (ExtractDestination(scriptPubKey, address) && + !scriptPubKey.IsUnspendable()) { + auto mi = mapAddressBook.find(address); + if (mi != mapAddressBook.end()) { + return mi->second.name; + } + } + // A scriptPubKey that doesn't have an entry in the address book is + // associated with the default account (""). + const static std::string DEFAULT_ACCOUNT_NAME; + return DEFAULT_ACCOUNT_NAME; +} + /** * Mark old keypool keys as used, and generate all new keys. */ @@ -3677,42 +3704,6 @@ return ret; } -Amount CWallet::GetAccountBalance(const std::string &strAccount, int nMinDepth, - const isminefilter &filter) { - CWalletDB walletdb(*dbw); - return GetAccountBalance(walletdb, strAccount, nMinDepth, filter); -} - -Amount CWallet::GetAccountBalance(CWalletDB &walletdb, - const std::string &strAccount, int nMinDepth, - const isminefilter &filter) { - Amount nBalance(0); - - // Tally wallet transactions. - for (std::map<uint256, CWalletTx>::iterator it = mapWallet.begin(); - it != mapWallet.end(); ++it) { - const CWalletTx &wtx = (*it).second; - if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || - wtx.GetDepthInMainChain() < 0) { - continue; - } - - Amount nReceived, nSent, nFee; - wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter); - - if (nReceived != Amount(0) && wtx.GetDepthInMainChain() >= nMinDepth) { - nBalance += nReceived; - } - - nBalance -= nSent + nFee; - } - - // Tally internal accounting entries. - nBalance += walletdb.GetAccountCreditDebit(strAccount); - - return nBalance; -} - std::set<CTxDestination> CWallet::GetAccountAddresses(const std::string &strAccount) const { LOCK(cs_wallet);