Skip to content

Commit

Permalink
[backport#15780] wallet: add cachable amounts for caching credit/debi…
Browse files Browse the repository at this point in the history
…t values

Summary:
bitcoin/bitcoin@c9e6e7e

---

Backport of Core [[bitcoin/bitcoin#15780 | PR15780]]

Test Plan:
  ninja check-all

Reviewers: #bitcoin_abc, nakihito

Reviewed By: nakihito

Differential Revision: https://reviews.bitcoinabc.org/D6347
  • Loading branch information
kallewoof authored and majcosta committed Jun 4, 2020
1 parent 6dd46aa commit 7171690
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 105 deletions.
22 changes: 19 additions & 3 deletions src/script/ismine.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <script/standard.h>

#include <bitset>
#include <cstdint>

class CKeyStore;
Expand All @@ -16,9 +17,10 @@ class CScript;
/** IsMine() return codes */
enum isminetype {
ISMINE_NO = 0,
ISMINE_WATCH_ONLY = 1,
ISMINE_SPENDABLE = 2,
ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE
ISMINE_WATCH_ONLY = 1 << 0,
ISMINE_SPENDABLE = 1 << 1,
ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE,
ISMINE_ENUM_ELEMENTS,
};

/** used for bitflags of isminetype */
Expand All @@ -36,4 +38,18 @@ isminetype IsMine(const CKeyStore &keystore, const CScript &scriptPubKey,
isminetype IsMine(const CKeyStore &keystore, const CScript &scriptPubKey);
isminetype IsMine(const CKeyStore &keystore, const CTxDestination &dest);

/**
* Cachable amount subdivided into watchonly and spendable parts.
*/
struct CachableAmount {
// NO and ALL are never (supposed to be) cached
std::bitset<ISMINE_ENUM_ELEMENTS> m_cached;
Amount m_value[ISMINE_ENUM_ELEMENTS];
inline void Reset() { m_cached.reset(); }
void Set(isminefilter filter, Amount value) {
m_cached.set(filter);
m_value[filter] = value;
}
};

#endif // BITCOIN_SCRIPT_ISMINE_H
7 changes: 4 additions & 3 deletions src/wallet/test/coinselector_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ static void add_coin(CWallet &wallet, const Amount nValue, int nAge = 6 * 24,
auto wtx =
std::make_unique<CWalletTx>(&wallet, MakeTransactionRef(std::move(tx)));
if (fIsFromMe) {
wtx->fDebitCached = true;
wtx->nDebitCached = SATOSHI;
wtx->m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, SATOSHI);
}
COutput output(wtx.get(), nInput, nAge, true /* spendable */,
true /* solvable */, true /* safe */);
Expand Down Expand Up @@ -127,7 +126,9 @@ inline std::vector<OutputGroup> &GroupCoins(const std::vector<COutput> &coins) {
// HACK: we can't figure out the is_me flag so we use the conditions
// defined below; perhaps set safe to false for !fIsFromMe in add_coin()
const bool is_me =
coin.tx->fDebitCached && coin.tx->nDebitCached == SATOSHI;
coin.tx->m_amounts[CWalletTx::DEBIT].m_cached[ISMINE_SPENDABLE] &&
coin.tx->m_amounts[CWalletTx::DEBIT].m_value[ISMINE_SPENDABLE] ==
SATOSHI;
static_groups.emplace_back(coin.GetInputCoin(), coin.nDepth, is_me, 0,
0);
}
Expand Down
87 changes: 28 additions & 59 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2115,30 +2115,27 @@ std::set<TxId> CWalletTx::GetConflicts() const {
return result;
}

Amount CWalletTx::GetCachableAmount(AmountType type, const isminefilter &filter,
bool recalculate) const {
auto &amount = m_amounts[type];
if (recalculate || !amount.m_cached[filter]) {
amount.Set(filter, type == DEBIT ? pwallet->GetDebit(*tx, filter)
: pwallet->GetCredit(*tx, filter));
}
return amount.m_value[filter];
}

Amount CWalletTx::GetDebit(const isminefilter &filter) const {
if (tx->vin.empty()) {
return Amount::zero();
}

Amount debit = Amount::zero();
if (filter & ISMINE_SPENDABLE) {
if (fDebitCached) {
debit += nDebitCached;
} else {
nDebitCached = pwallet->GetDebit(*tx, ISMINE_SPENDABLE);
fDebitCached = true;
debit += nDebitCached;
}
debit += GetCachableAmount(DEBIT, ISMINE_SPENDABLE);
}

if (filter & ISMINE_WATCH_ONLY) {
if (fWatchDebitCached) {
debit += nWatchDebitCached;
} else {
nWatchDebitCached = pwallet->GetDebit(*tx, ISMINE_WATCH_ONLY);
fWatchDebitCached = true;
debit += Amount(nWatchDebitCached);
}
debit += GetCachableAmount(DEBIT, ISMINE_WATCH_ONLY);
}

return debit;
Expand All @@ -2155,23 +2152,11 @@ Amount CWalletTx::GetCredit(interfaces::Chain::Lock &locked_chain,
Amount credit = Amount::zero();
if (filter & ISMINE_SPENDABLE) {
// GetBalance can assume transactions in mapWallet won't change.
if (fCreditCached) {
credit += nCreditCached;
} else {
nCreditCached = pwallet->GetCredit(*tx, ISMINE_SPENDABLE);
fCreditCached = true;
credit += nCreditCached;
}
credit += GetCachableAmount(CREDIT, ISMINE_SPENDABLE);
}

if (filter & ISMINE_WATCH_ONLY) {
if (fWatchCreditCached) {
credit += nWatchCreditCached;
} else {
nWatchCreditCached = pwallet->GetCredit(*tx, ISMINE_WATCH_ONLY);
fWatchCreditCached = true;
credit += nWatchCreditCached;
}
credit += GetCachableAmount(CREDIT, ISMINE_WATCH_ONLY);
}

return credit;
Expand All @@ -2180,13 +2165,7 @@ Amount CWalletTx::GetCredit(interfaces::Chain::Lock &locked_chain,
Amount CWalletTx::GetImmatureCredit(interfaces::Chain::Lock &locked_chain,
bool fUseCache) const {
if (IsImmatureCoinBase(locked_chain) && IsInMainChain(locked_chain)) {
if (fUseCache && fImmatureCreditCached) {
return nImmatureCreditCached;
}

nImmatureCreditCached = pwallet->GetCredit(*tx, ISMINE_SPENDABLE);
fImmatureCreditCached = true;
return nImmatureCreditCached;
return GetCachableAmount(IMMATURE_CREDIT, ISMINE_SPENDABLE, !fUseCache);
}

return Amount::zero();
Expand All @@ -2199,25 +2178,20 @@ Amount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock &locked_chain,
return Amount::zero();
}

// Avoid caching ismine for NO or ALL cases (could remove this check and
// simplify in the future).
bool allow_cache =
filter == ISMINE_SPENDABLE || filter == ISMINE_WATCH_ONLY;

// Must wait until coinbase is safely deep enough in the chain before
// valuing it.
if (IsImmatureCoinBase(locked_chain)) {
return Amount::zero();
}

Amount *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;
if (fUseCache && allow_cache &&
m_amounts[AVAILABLE_CREDIT].m_cached[filter]) {
return m_amounts[AVAILABLE_CREDIT].m_value[filter];
}

Amount nCredit = Amount::zero();
Expand All @@ -2233,24 +2207,19 @@ Amount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock &locked_chain,
}
}

if (cache) {
*cache = nCredit;
*cache_used = true;
if (allow_cache) {
m_amounts[AVAILABLE_CREDIT].Set(filter, nCredit);
}

return nCredit;
}

Amount
CWalletTx::GetImmatureWatchOnlyCredit(interfaces::Chain::Lock &locked_chain,
const bool fUseCache) const {
if (IsImmatureCoinBase(locked_chain) && IsInMainChain(locked_chain)) {
if (fUseCache && fImmatureWatchCreditCached) {
return nImmatureWatchCreditCached;
}

nImmatureWatchCreditCached = pwallet->GetCredit(*tx, ISMINE_WATCH_ONLY);
fImmatureWatchCreditCached = true;
return nImmatureWatchCreditCached;
return GetCachableAmount(IMMATURE_CREDIT, ISMINE_WATCH_ONLY,
!fUseCache);
}

return Amount::zero();
Expand Down
54 changes: 14 additions & 40 deletions src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -388,24 +388,18 @@ class CWalletTx : public CMerkleTx {
std::multimap<int64_t, CWalletTx *>::const_iterator m_it_wtxOrdered;

// memory only
mutable bool fDebitCached;
mutable bool fCreditCached;
mutable bool fImmatureCreditCached;
mutable bool fAvailableCreditCached;
mutable bool fWatchDebitCached;
mutable bool fWatchCreditCached;
mutable bool fImmatureWatchCreditCached;
mutable bool fAvailableWatchCreditCached;
enum AmountType {
DEBIT,
CREDIT,
IMMATURE_CREDIT,
AVAILABLE_CREDIT,
AMOUNTTYPE_ENUM_ELEMENTS
};
Amount GetCachableAmount(AmountType type, const isminefilter &filter,
bool recalculate = false) const;
mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS];
mutable bool fChangeCached;
mutable bool fInMempool;
mutable Amount nDebitCached;
mutable Amount nCreditCached;
mutable Amount nImmatureCreditCached;
mutable Amount nAvailableCreditCached;
mutable Amount nWatchDebitCached;
mutable Amount nWatchCreditCached;
mutable Amount nImmatureWatchCreditCached;
mutable Amount nAvailableWatchCreditCached;
mutable Amount nChangeCached;

CWalletTx(const CWallet *pwalletIn, CTransactionRef arg)
Expand All @@ -421,24 +415,8 @@ class CWalletTx : public CMerkleTx {
nTimeReceived = 0;
nTimeSmart = 0;
fFromMe = false;
fDebitCached = false;
fCreditCached = false;
fImmatureCreditCached = false;
fAvailableCreditCached = false;
fWatchDebitCached = false;
fWatchCreditCached = false;
fImmatureWatchCreditCached = false;
fAvailableWatchCreditCached = false;
fChangeCached = false;
fInMempool = false;
nDebitCached = Amount::zero();
nCreditCached = Amount::zero();
nImmatureCreditCached = Amount::zero();
nAvailableCreditCached = Amount::zero();
nWatchDebitCached = Amount::zero();
nWatchCreditCached = Amount::zero();
nAvailableWatchCreditCached = Amount::zero();
nImmatureWatchCreditCached = Amount::zero();
nChangeCached = Amount::zero();
nOrderPos = -1;
}
Expand Down Expand Up @@ -483,14 +461,10 @@ class CWalletTx : public CMerkleTx {

//! make sure balances are recalculated
void MarkDirty() {
fCreditCached = false;
fAvailableCreditCached = false;
fImmatureCreditCached = false;
fWatchDebitCached = false;
fWatchCreditCached = false;
fAvailableWatchCreditCached = false;
fImmatureWatchCreditCached = false;
fDebitCached = false;
m_amounts[DEBIT].Reset();
m_amounts[CREDIT].Reset();
m_amounts[IMMATURE_CREDIT].Reset();
m_amounts[AVAILABLE_CREDIT].Reset();
fChangeCached = false;
}

Expand Down

0 comments on commit 7171690

Please sign in to comment.