[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.
pull/365/head
Russell Yanofsky 8 years ago
parent bd9ec0ef1e
commit 82b7dc373a

@ -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");

@ -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))

@ -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
{
{

Loading…
Cancel
Save