diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp index 42ccfae6ef7..07500cc088e 100644 --- a/src/wallet/interfaces.cpp +++ b/src/wallet/interfaces.cpp @@ -76,8 +76,8 @@ WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx) wallet.IsMine(result.txout_address.back()) : ISMINE_NO); } - result.credit = CachedTxGetCredit(wallet, wtx, ISMINE_ALL); - result.debit = CachedTxGetDebit(wallet, wtx, ISMINE_ALL); + result.credit = CachedTxGetCredit(wallet, wtx, ISMINE_ALL, /*avoid_reuse=*/true); + result.debit = CachedTxGetDebit(wallet, wtx, ISMINE_ALL, /*avoid_reuse=*/true); result.change = CachedTxGetChange(wallet, wtx); result.time = wtx.GetTxTime(); result.value_map = wtx.mapValue; diff --git a/src/wallet/receive.cpp b/src/wallet/receive.cpp index 4c9ffc499d9..46e52b3f505 100644 --- a/src/wallet/receive.cpp +++ b/src/wallet/receive.cpp @@ -97,17 +97,17 @@ CAmount TxGetChange(const CWallet& wallet, const CTransaction& tx) return nChange; } -static CAmount GetCachableAmount(const CWallet& wallet, const CWalletTx& wtx, CWalletTx::AmountType type, const isminefilter& filter) +static CAmount GetCachableAmount(const CWallet& wallet, const CWalletTx& wtx, CWalletTx::AmountType type, const isminefilter& filter, bool avoid_reuse) { auto& amount = wtx.m_amounts[type]; - if (!amount.m_cached[filter]) { - amount.Set(filter, type == CWalletTx::DEBIT ? wallet.GetDebit(*wtx.tx, filter) : TxGetCredit(wallet, *wtx.tx, filter)); + if (!amount.IsCached(avoid_reuse)) { + amount.Set(avoid_reuse, type == CWalletTx::DEBIT ? wallet.GetDebit(*wtx.tx, filter) : TxGetCredit(wallet, *wtx.tx, filter)); wtx.m_is_cache_empty = false; } - return amount.m_value[filter]; + return amount.Get(avoid_reuse); } -CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter) +CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter, bool avoid_reuse) { AssertLockHeld(wallet.cs_wallet); @@ -119,12 +119,12 @@ CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, const ism const isminefilter get_amount_filter{filter & ISMINE_ALL}; if (get_amount_filter) { // GetBalance can assume transactions in mapWallet won't change - credit += GetCachableAmount(wallet, wtx, CWalletTx::CREDIT, get_amount_filter); + credit += GetCachableAmount(wallet, wtx, CWalletTx::CREDIT, get_amount_filter, avoid_reuse); } return credit; } -CAmount CachedTxGetDebit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter) +CAmount CachedTxGetDebit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter, bool avoid_reuse) { if (wtx.tx->vin.empty()) return 0; @@ -132,7 +132,7 @@ CAmount CachedTxGetDebit(const CWallet& wallet, const CWalletTx& wtx, const ismi CAmount debit = 0; const isminefilter get_amount_filter{filter & ISMINE_ALL}; if (get_amount_filter) { - debit += GetCachableAmount(wallet, wtx, CWalletTx::DEBIT, get_amount_filter); + debit += GetCachableAmount(wallet, wtx, CWalletTx::DEBIT, get_amount_filter, avoid_reuse); } return debit; } @@ -156,7 +156,7 @@ void CachedTxGetAmounts(const CWallet& wallet, const CWalletTx& wtx, listSent.clear(); // Compute fee: - CAmount nDebit = CachedTxGetDebit(wallet, wtx, filter); + CAmount nDebit = CachedTxGetDebit(wallet, wtx, filter, /*avoid_reuse=*/false); if (nDebit > 0) // debit>0 means we signed/sent this transaction { CAmount nValueOut = wtx.tx->GetValueOut(); @@ -205,7 +205,7 @@ void CachedTxGetAmounts(const CWallet& wallet, const CWalletTx& wtx, bool CachedTxIsFromMe(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter) { - return (CachedTxGetDebit(wallet, wtx, filter) > 0); + return (CachedTxGetDebit(wallet, wtx, filter, /*avoid_reuse=*/false) > 0); } // NOLINTNEXTLINE(misc-no-recursion) diff --git a/src/wallet/receive.h b/src/wallet/receive.h index f129c195322..271c1a96226 100644 --- a/src/wallet/receive.h +++ b/src/wallet/receive.h @@ -25,10 +25,10 @@ bool OutputIsChange(const CWallet& wallet, const CTxOut& txout) EXCLUSIVE_LOCKS_ CAmount OutputGetChange(const CWallet& wallet, const CTxOut& txout) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); CAmount TxGetChange(const CWallet& wallet, const CTransaction& tx); -CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter) +CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter, bool avoid_reuse) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); //! filter decides which addresses will count towards the debit -CAmount CachedTxGetDebit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter); +CAmount CachedTxGetDebit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter, bool avoid_reuse); CAmount CachedTxGetChange(const CWallet& wallet, const CWalletTx& wtx); struct COutputEntry { diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp index b58d3dad516..8d2c7fb2135 100644 --- a/src/wallet/rpc/transactions.cpp +++ b/src/wallet/rpc/transactions.cpp @@ -741,8 +741,8 @@ RPCHelpMan gettransaction() } const CWalletTx& wtx = it->second; - CAmount nCredit = CachedTxGetCredit(*pwallet, wtx, filter); - CAmount nDebit = CachedTxGetDebit(*pwallet, wtx, filter); + CAmount nCredit = CachedTxGetCredit(*pwallet, wtx, filter, /*avoid_reuse=*/false); + CAmount nDebit = CachedTxGetDebit(*pwallet, wtx, filter, /*avoid_reuse=*/false); CAmount nNet = nCredit - nDebit; CAmount nFee = (CachedTxIsFromMe(*pwallet, wtx, filter) ? wtx.tx->GetValueOut() - nDebit : 0); diff --git a/src/wallet/transaction.h b/src/wallet/transaction.h index 83c3c660135..ec4fb99ed5f 100644 --- a/src/wallet/transaction.h +++ b/src/wallet/transaction.h @@ -128,21 +128,38 @@ std::string TxStateString(const T& state) } /** - * Cachable amount subdivided into watchonly and spendable parts. + * Cachable amount subdivided into avoid reuse and all balances */ struct CachableAmount { - // NO and ALL are never (supposed to be) cached - std::bitset m_cached; - CAmount m_value[ISMINE_ENUM_ELEMENTS]; + std::optional m_avoid_reuse_value; + std::optional m_all_value; inline void Reset() { - m_cached.reset(); + m_avoid_reuse_value.reset(); + m_all_value.reset(); } - void Set(isminefilter filter, CAmount value) + void Set(bool avoid_reuse, CAmount value) { - m_cached.set(filter); - m_value[filter] = value; + if (avoid_reuse) { + m_avoid_reuse_value = value; + } else { + m_all_value = value; + } + } + CAmount Get(bool avoid_reuse) + { + if (avoid_reuse) { + Assert(m_avoid_reuse_value.has_value()); + return m_avoid_reuse_value.value(); + } + Assert(m_all_value.has_value()); + return m_all_value.value(); + } + bool IsCached(bool avoid_reuse) + { + if (avoid_reuse) return m_avoid_reuse_value.has_value(); + return m_all_value.has_value(); } }; diff --git a/src/wallet/types.h b/src/wallet/types.h index 8d1202dd619..066a286a6e2 100644 --- a/src/wallet/types.h +++ b/src/wallet/types.h @@ -26,23 +26,18 @@ namespace wallet { * For LegacyScriptPubKeyMan, * ISMINE_NO: the scriptPubKey is not in the wallet; * ISMINE_SPENDABLE: the scriptPubKey corresponds to an address owned by the wallet user (can spend with the private key); - * ISMINE_USED: the scriptPubKey corresponds to a used address owned by the wallet user; * ISMINE_ALL: all ISMINE flags except for USED; - * ISMINE_ALL_USED: all ISMINE flags including USED; * ISMINE_ENUM_ELEMENTS: the number of isminetype enum elements. * * For DescriptorScriptPubKeyMan and future ScriptPubKeyMan, * ISMINE_NO: the scriptPubKey is not in the wallet; * ISMINE_SPENDABLE: the scriptPubKey matches a scriptPubKey in the wallet. - * ISMINE_USED: the scriptPubKey corresponds to a used address owned by the wallet user. * */ enum isminetype : unsigned int { ISMINE_NO = 0, ISMINE_SPENDABLE = 1 << 1, - ISMINE_USED = 1 << 2, ISMINE_ALL = ISMINE_SPENDABLE, - ISMINE_ALL_USED = ISMINE_ALL | ISMINE_USED, ISMINE_ENUM_ELEMENTS, }; /** used for bitflags of isminetype */