diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 02de3cceed274..811fa5db9f4a5 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -723,6 +723,7 @@ DBErrors CWallet::ReorderTransactions() } walletdb.WriteOrderPosNext(nOrderPosNext); + MarkBalancesDirty(); return DB_LOAD_OK; } @@ -812,11 +813,10 @@ bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bFo void CWallet::MarkDirty() { - { - LOCK(cs_wallet); - for (std::pair& item : mapWallet) - item.second.MarkDirty(); - } + LOCK(cs_wallet); + MarkBalancesDirty(); + for (std::pair& item : mapWallet) + item.second.MarkDirty(); } bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash) @@ -843,6 +843,7 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash) success = false; } + MarkBalancesDirty(); NotifyTransactionChanged(this, originalHash, CT_UPDATED); return success; @@ -907,6 +908,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) // Break debit/credit balance caches: wtx.MarkDirty(); + MarkBalancesDirty(); // Notify UI of new or updated transaction NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); @@ -1053,6 +1055,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx) } } + MarkBalancesDirty(); return true; } @@ -1113,6 +1116,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) } } } + MarkBalancesDirty(); } void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pindex, int posInBlock) { @@ -1152,6 +1156,7 @@ void CWallet::BlockConnected(const std::shared_ptr& pblock, const for (size_t i = 0; i < pblock->vtx.size(); i++) { SyncTransaction(pblock->vtx[i], pindex, i); } + MarkBalancesDirty(); } void CWallet::BlockDisconnected(const std::shared_ptr& pblock) { @@ -1160,6 +1165,7 @@ void CWallet::BlockDisconnected(const std::shared_ptr& pblock) { for (const CTransactionRef& ptx : pblock->vtx) { SyncTransaction(ptx); } + MarkBalancesDirty(); } @@ -1856,9 +1862,23 @@ void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman * @{ */ +void CWallet::MarkBalancesDirty() +{ + fBalanceDirty = true; + fUnconfirmedBalanceDirty = true; + fImmatureBalanceDirty = true; + fWatchOnlyBalanceDirty = true; + fUnconfirmedWatchOnlyBalanceDirty = true; + fImmatureWatchOnlyBalanceDirty = true; + BalancesChanged(); //emit signal +} CAmount CWallet::GetBalance() const { + if (!fBalanceDirty) { + return nBalanceCache; + } + CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); @@ -1868,13 +1888,18 @@ CAmount CWallet::GetBalance() const if (pcoin->IsTrusted()) nTotal += pcoin->GetAvailableCredit(); } + nBalanceCache = nTotal; + fBalanceDirty = false; } - - return nTotal; + return nBalanceCache; } CAmount CWallet::GetUnconfirmedBalance() const { + if (!fUnconfirmedBalanceDirty) { + return nUnconfirmedBalanceCache; + } + CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); @@ -1884,12 +1909,18 @@ CAmount CWallet::GetUnconfirmedBalance() const if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool()) nTotal += pcoin->GetAvailableCredit(); } + nUnconfirmedBalanceCache = nTotal; + fUnconfirmedBalanceDirty = false; } - return nTotal; + return nUnconfirmedBalanceCache; } CAmount CWallet::GetImmatureBalance() const { + if (!fImmatureBalanceDirty) { + return nImmatureBalanceCache; + } + CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); @@ -1898,12 +1929,18 @@ CAmount CWallet::GetImmatureBalance() const const CWalletTx* pcoin = &(*it).second; nTotal += pcoin->GetImmatureCredit(); } + nImmatureBalanceCache = nTotal; + fImmatureBalanceDirty = false; } - return nTotal; + return nImmatureBalanceCache; } CAmount CWallet::GetWatchOnlyBalance() const { + if (!fWatchOnlyBalanceDirty) { + return nWatchOnlyBalanceCache; + } + CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); @@ -1913,13 +1950,18 @@ CAmount CWallet::GetWatchOnlyBalance() const if (pcoin->IsTrusted()) nTotal += pcoin->GetAvailableWatchOnlyCredit(); } + nWatchOnlyBalanceCache = nTotal; + fWatchOnlyBalanceDirty = false; } - - return nTotal; + return nWatchOnlyBalanceCache; } CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const { + if (!fUnconfirmedWatchOnlyBalanceDirty) { + return nUnconfirmedWatchOnlyBalanceCache; + } + CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); @@ -1929,12 +1971,18 @@ CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool()) nTotal += pcoin->GetAvailableWatchOnlyCredit(); } + nUnconfirmedWatchOnlyBalanceCache = nTotal; + fUnconfirmedWatchOnlyBalanceDirty = false; } - return nTotal; + return nUnconfirmedWatchOnlyBalanceCache; } CAmount CWallet::GetImmatureWatchOnlyBalance() const { + if (!fImmatureWatchOnlyBalanceDirty) { + return nImmatureWatchOnlyBalanceCache; + } + CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); @@ -1943,8 +1991,10 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const const CWalletTx* pcoin = &(*it).second; nTotal += pcoin->GetImmatureWatchOnlyCredit(); } + nImmatureWatchOnlyBalanceCache = nTotal; + fImmatureWatchOnlyBalanceDirty = false; } - return nTotal; + return nImmatureWatchOnlyBalanceCache; } // Calculate total balance in a different way from GetBalance. The biggest diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 6c6eb69180ae6..572e87124d72c 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -791,6 +791,12 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface nRelockTime = 0; fAbortRescan = false; fScanningWallet = false; + fBalanceDirty = true; + fUnconfirmedBalanceDirty = true; + fImmatureBalanceDirty = true; + fWatchOnlyBalanceDirty = true; + fUnconfirmedWatchOnlyBalanceDirty = true; + fImmatureWatchOnlyBalanceDirty = true; } std::map mapWallet; @@ -922,6 +928,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface void ReacceptWalletTransactions(); void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override; std::vector ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman); + + void MarkBalancesDirty(); CAmount GetBalance() const; CAmount GetUnconfirmedBalance() const; CAmount GetImmatureBalance() const; @@ -931,6 +939,22 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface CAmount GetLegacyBalance(const isminefilter& filter, int minDepth, const std::string* account) const; CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const; + mutable std::atomic nBalanceCache; + mutable std::atomic nUnconfirmedBalanceCache; + mutable std::atomic nImmatureBalanceCache; + mutable std::atomic nWatchOnlyBalanceCache; + mutable std::atomic nUnconfirmedWatchOnlyBalanceCache; + mutable std::atomic nImmatureWatchOnlyBalanceCache; + mutable std::atomic fBalanceDirty; + mutable std::atomic fUnconfirmedBalanceDirty; + mutable std::atomic fImmatureBalanceDirty; + mutable std::atomic fWatchOnlyBalanceDirty; + mutable std::atomic fUnconfirmedWatchOnlyBalanceDirty; + mutable std::atomic fImmatureWatchOnlyBalanceDirty; + + /** Notification for balance changes */ + boost::signals2::signal BalancesChanged; + /** * Insert additional inputs into the transaction by * calling CreateTransaction();