Skip to content

Commit

Permalink
merge bitcoin#15780: add cachable amounts for caching credit/debit va…
Browse files Browse the repository at this point in the history
…lues
  • Loading branch information
kwvg committed Dec 8, 2021
1 parent 469d665 commit 6472a7e
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 142 deletions.
29 changes: 25 additions & 4 deletions src/script/ismine.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,43 @@
#include <script/standard.h>

#include <stdint.h>
#include <bitset>

class CKeyStore;
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_NO = 0,
ISMINE_WATCH_ONLY = 1 << 0,
ISMINE_SPENDABLE = 1 << 1,
ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE,
ISMINE_ENUM_ELEMENTS,
};
/** used for bitflags of isminetype */
typedef uint8_t isminefilter;

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;
CAmount m_value[ISMINE_ENUM_ELEMENTS];
inline void Reset()
{
m_cached.reset();
}
void Set(isminefilter filter, CAmount value)
{
m_cached.set(filter);
m_value[filter] = value;
}
};

#endif // BITCOIN_SCRIPT_ISMINE_H
5 changes: 2 additions & 3 deletions src/wallet/test/coinselector_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa
std::unique_ptr<CWalletTx> wtx = MakeUnique<CWalletTx>(&testWallet, MakeTransactionRef(std::move(tx)));
if (fIsFromMe)
{
wtx->fDebitCached = true;
wtx->nDebitCached = 1;
wtx->m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1);
}
COutput output(wtx.get(), nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
vCoins.push_back(output);
Expand Down Expand Up @@ -116,7 +115,7 @@ inline std::vector<OutputGroup>& GroupCoins(const std::vector<COutput>& coins)
{
static std::vector<OutputGroup> static_groups;
static_groups.clear();
for (auto& coin : coins) static_groups.emplace_back(coin.GetInputCoin(), coin.nDepth, coin.tx->fDebitCached && coin.tx->nDebitCached == 1 /* HACK: we can't figure out the is_me flag so we use the conditions defined above; perhaps set safe to false for !fIsFromMe in add_coin() */, 0, 0);
for (auto& coin : coins) static_groups.emplace_back(coin.GetInputCoin(), coin.nDepth, coin.tx->m_amounts[CWalletTx::DEBIT].m_cached[ISMINE_SPENDABLE] && coin.tx->m_amounts[CWalletTx::DEBIT].m_value[ISMINE_SPENDABLE] == 1 /* HACK: we can't figure out the is_me flag so we use the conditions defined above; perhaps set safe to false for !fIsFromMe in add_coin() */, 0, 0);
return static_groups;
}

Expand Down
118 changes: 38 additions & 80 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2312,33 +2312,26 @@ std::set<uint256> CWalletTx::GetConflicts() const
return result;
}

CAmount 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];
}

CAmount CWalletTx::GetDebit(const isminefilter& filter) const
{
if (tx->vin.empty())
return 0;

CAmount debit = 0;
if(filter & ISMINE_SPENDABLE)
{
if (fDebitCached)
debit += nDebitCached;
else
{
nDebitCached = pwallet->GetDebit(*tx, ISMINE_SPENDABLE);
fDebitCached = true;
debit += nDebitCached;
}
if (filter & ISMINE_SPENDABLE) {
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 += nWatchDebitCached;
}
if (filter & ISMINE_WATCH_ONLY) {
debit += GetCachableAmount(DEBIT, ISMINE_WATCH_ONLY);
}
return debit;
}
Expand All @@ -2350,40 +2343,20 @@ CAmount CWalletTx::GetCredit(interfaces::Chain::Lock& locked_chain, const ismine
return 0;

CAmount credit = 0;
if (filter & ISMINE_SPENDABLE)
{
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;
}
if (filter & ISMINE_WATCH_ONLY) {
credit += GetCachableAmount(CREDIT, ISMINE_WATCH_ONLY);
}
return credit;
}

CAmount 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 0;
Expand All @@ -2394,23 +2367,15 @@ CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, boo
if (pwallet == nullptr)
return 0;

// 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 0;

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

CAmount nCredit = 0;
Expand All @@ -2426,22 +2391,17 @@ CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, boo
}
}

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

return nCredit;
}

CAmount 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 0;
Expand All @@ -2458,8 +2418,8 @@ CAmount CWalletTx::GetAnonymizedCredit(interfaces::Chain::Lock& locked_chain, co
if (IsCoinBase() || GetDepthInMainChain(locked_chain) < 0)
return 0;

if (coinControl == nullptr && fAnonymizedCreditCached)
return nAnonymizedCreditCached;
if (coinControl == nullptr)
return GetCachableAmount(ANON_CREDIT, ISMINE_SPENDABLE, coinControl != nullptr);

CAmount nCredit = 0;
uint256 hashTx = GetHash();
Expand All @@ -2482,8 +2442,7 @@ CAmount CWalletTx::GetAnonymizedCredit(interfaces::Chain::Lock& locked_chain, co
}

if (coinControl == nullptr) {
nAnonymizedCreditCached = nCredit;
fAnonymizedCreditCached = true;
m_amounts[ANON_CREDIT].Set(ISMINE_SPENDABLE, nCredit);
}

return nCredit;
Expand All @@ -2507,10 +2466,11 @@ CAmount CWalletTx::GetDenominatedCredit(interfaces::Chain::Lock& locked_chain, b
if (unconfirmed != isUnconfirmed) return 0;

if (fUseCache) {
if(unconfirmed && fDenomUnconfCreditCached)
return nDenomUnconfCreditCached;
else if (!unconfirmed && fDenomConfCreditCached)
return nDenomConfCreditCached;
if(!unconfirmed) {
return GetCachableAmount(DENOM_CREDIT, ISMINE_SPENDABLE, !fUseCache);
} else {
return GetCachableAmount(DENOM_UCREDIT, ISMINE_SPENDABLE, !fUseCache);
}
}

CAmount nCredit = 0;
Expand All @@ -2526,12 +2486,10 @@ CAmount CWalletTx::GetDenominatedCredit(interfaces::Chain::Lock& locked_chain, b
throw std::runtime_error(std::string(__func__) + ": value out of range");
}

if (unconfirmed) {
nDenomUnconfCreditCached = nCredit;
fDenomUnconfCreditCached = true;
if (!unconfirmed) {
m_amounts[DENOM_CREDIT].Set(ISMINE_SPENDABLE, nCredit);
} else {
nDenomConfCreditCached = nCredit;
fDenomConfCreditCached = true;
m_amounts[DENOM_UCREDIT].Set(ISMINE_SPENDABLE, nCredit);
}
return nCredit;
}
Expand Down
65 changes: 10 additions & 55 deletions src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,30 +351,11 @@ 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 fAnonymizedCreditCached;
mutable bool fDenomUnconfCreditCached;
mutable bool fDenomConfCreditCached;
mutable bool fWatchDebitCached;
mutable bool fWatchCreditCached;
mutable bool fImmatureWatchCreditCached;
mutable bool fAvailableWatchCreditCached;
enum AmountType { DEBIT, CREDIT, IMMATURE_CREDIT, AVAILABLE_CREDIT, ANON_CREDIT, DENOM_UCREDIT, DENOM_CREDIT, AMOUNTTYPE_ENUM_ELEMENTS };
CAmount GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate = false) const;
mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS];
mutable bool fChangeCached;
mutable bool fInMempool;
mutable CAmount nDebitCached;
mutable CAmount nCreditCached;
mutable CAmount nImmatureCreditCached;
mutable CAmount nAvailableCreditCached;
mutable CAmount nAnonymizedCreditCached;
mutable CAmount nDenomUnconfCreditCached;
mutable CAmount nDenomConfCreditCached;
mutable CAmount nWatchDebitCached;
mutable CAmount nWatchCreditCached;
mutable CAmount nImmatureWatchCreditCached;
mutable CAmount nAvailableWatchCreditCached;
mutable CAmount nChangeCached;

CWalletTx(const CWallet* pwalletIn, CTransactionRef arg) : CMerkleTx(std::move(arg))
Expand All @@ -391,30 +372,8 @@ class CWalletTx : public CMerkleTx
nTimeReceived = 0;
nTimeSmart = 0;
fFromMe = false;
fDebitCached = false;
fCreditCached = false;
fImmatureCreditCached = false;
fAvailableCreditCached = false;
fAnonymizedCreditCached = false;
fDenomUnconfCreditCached = false;
fDenomConfCreditCached = false;
fWatchDebitCached = false;
fWatchCreditCached = false;
fImmatureWatchCreditCached = false;
fAvailableWatchCreditCached = false;
fChangeCached = false;
fInMempool = false;
nDebitCached = 0;
nCreditCached = 0;
nImmatureCreditCached = 0;
nAvailableCreditCached = 0;
nAnonymizedCreditCached = 0;
nDenomUnconfCreditCached = 0;
nDenomConfCreditCached = 0;
nWatchDebitCached = 0;
nWatchCreditCached = 0;
nAvailableWatchCreditCached = 0;
nImmatureWatchCreditCached = 0;
nChangeCached = 0;
nOrderPos = -1;
}
Expand Down Expand Up @@ -458,17 +417,13 @@ class CWalletTx : public CMerkleTx
//! make sure balances are recalculated
void MarkDirty()
{
fCreditCached = false;
fAvailableCreditCached = false;
fImmatureCreditCached = false;
fAnonymizedCreditCached = false;
fDenomUnconfCreditCached = false;
fDenomConfCreditCached = false;
fWatchDebitCached = false;
fWatchCreditCached = false;
fAvailableWatchCreditCached = false;
fImmatureWatchCreditCached = false;
fDebitCached = false;
m_amounts[DEBIT].Reset();
m_amounts[CREDIT].Reset();
m_amounts[ANON_CREDIT].Reset();
m_amounts[DENOM_CREDIT].Reset();
m_amounts[DENOM_UCREDIT].Reset();
m_amounts[IMMATURE_CREDIT].Reset();
m_amounts[AVAILABLE_CREDIT].Reset();
fChangeCached = false;
}

Expand Down

0 comments on commit 6472a7e

Please sign in to comment.