aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell Yanofsky <russ@yanofsky.org>2017-01-23 09:58:42 -0500
committerRussell Yanofsky <russ@yanofsky.org>2017-04-26 06:36:38 -0400
commit82b7dc373afff277d3083d09e68c6b69778f312f (patch)
tree0359a2ebea49e644a60ef76f2e65c37bb6da3a28
parentbd9ec0ef1ea73209ba9e491e4b7847c895ca7a2f (diff)
downloadbitcoin-82b7dc373afff277d3083d09e68c6b69778f312f.tar.xz
[wallet] Add GetLegacyBalance method to simplify getbalance RPC
This adds a simpler new implementation of getbalance logic along with asserts to confirm it behaves identically to the old logic. The old logic is removed in the next commit.
-rw-r--r--src/wallet/rpcwallet.cpp10
-rw-r--r--src/wallet/wallet.cpp58
-rw-r--r--src/wallet/wallet.h3
3 files changed, 71 insertions, 0 deletions
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index f8006a6255..98efb8bf6d 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -729,6 +729,8 @@ UniValue getbalance(const JSONRPCRequest& request)
if (request.params.size() == 0)
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();
@@ -737,6 +739,8 @@ UniValue getbalance(const JSONRPCRequest& request)
if(request.params[2].get_bool())
filter = filter | ISMINE_WATCH_ONLY;
+ CAmount legacyBalance = pwallet->GetLegacyBalance(filter, nMinDepth, account);
+
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
@@ -764,6 +768,7 @@ UniValue getbalance(const JSONRPCRequest& request)
nBalance -= s.amount;
nBalance -= allFee;
}
+ assert(nBalance == legacyBalance);
return ValueFromAmount(nBalance);
}
@@ -771,6 +776,7 @@ UniValue getbalance(const JSONRPCRequest& request)
CAmount nBalance = pwallet->GetAccountBalance(strAccount, nMinDepth, filter);
+ assert(nBalance == legacyBalance);
return ValueFromAmount(nBalance);
}
@@ -902,6 +908,8 @@ UniValue sendfrom(const JSONRPCRequest& request)
// Check funds
CAmount nBalance = pwallet->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE);
+ CAmount legacyBalance = pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &strAccount);
+ assert(nBalance == legacyBalance);
if (nAmount > nBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
@@ -1011,6 +1019,8 @@ UniValue sendmany(const JSONRPCRequest& request)
// Check funds
CAmount nBalance = pwallet->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE);
+ CAmount legacyBalance = pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &strAccount);
+ assert(nBalance == legacyBalance);
if (totalAmount > nBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index b63171c4b6..cd94eefb20 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1975,6 +1975,49 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const
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.
+CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth, const std::string* account) const
+{
+ LOCK2(cs_main, cs_wallet);
+
+ CAmount 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.
+ CAmount debit = wtx.GetDebit(filter);
+ const bool outgoing = debit > 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
{
vCoins.clear();
@@ -2911,6 +2954,21 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
return CWalletDB(*dbw).EraseName(CBitcoinAddress(address).ToString());
}
+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;
+}
+
bool CWallet::SetDefaultKey(const CPubKey &vchPubKey)
{
if (!CWalletDB(*dbw).WriteDefaultKey(vchPubKey))
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index a52ae99ed1..342715b478 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -918,6 +918,7 @@ public:
CAmount GetWatchOnlyBalance() const;
CAmount GetUnconfirmedWatchOnlyBalance() const;
CAmount GetImmatureWatchOnlyBalance() const;
+ CAmount GetLegacyBalance(const isminefilter& filter, int minDepth, const std::string* account) const;
/**
* Insert additional inputs into the transaction by
@@ -1004,6 +1005,8 @@ public:
bool DelAddressBook(const CTxDestination& address);
+ const std::string& GetAccountName(const CScript& scriptPubKey) const;
+
void Inventory(const uint256 &hash) override
{
{