From 0e1f11421b814a3ea3dd8315a49a960f0b6da0d4 Mon Sep 17 00:00:00 2001 From: pasta Date: Mon, 10 Nov 2025 14:13:21 -0600 Subject: [PATCH 1/3] instantsend: cache heights and reduce cs_main contention - Add unordered_lru_cache for block heights in CInstantSendManager - Provide GetTipHeight() (non-optional) and GetBlockHeight() helpers - Use cache in InstantSendSigner::CheckCanLock - Cache-first for cycleHash height in ISLOCK message path and batch verify - Cache-first mined-height for HasChainLock and WriteInstantSendLockMined Only InstantSend files are included in this commit. --- src/instantsend/instantsend.cpp | 98 ++++++++++++++++++++++++++--- src/instantsend/instantsend.h | 32 +++++++--- src/instantsend/net_instantsend.cpp | 26 ++++---- src/instantsend/signing.cpp | 28 ++++++--- src/instantsend/signing.h | 4 ++ 5 files changed, 151 insertions(+), 37 deletions(-) diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index 0415a19d5377..da43d5396366 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -145,25 +145,34 @@ std::variant CInstantSendManager::Proc uint256 hashBlock{}; const auto tx = GetTransaction(nullptr, &mempool, islock->txid, Params().GetConsensus(), hashBlock); - const CBlockIndex* pindexMined{nullptr}; const bool found_transaction{tx != nullptr}; // we ignore failure here as we must be able to propagate the lock even if we don't have the TX locally + int minedHeight{-1}; if (found_transaction && !hashBlock.IsNull()) { - pindexMined = WITH_LOCK(::cs_main, return m_chainstate.m_blockman.LookupBlockIndex(hashBlock)); - + if (auto h = GetBlockHeight(hashBlock)) { + minedHeight = *h; + } else { + const CBlockIndex* pindexMined = WITH_LOCK(::cs_main, return m_chainstate.m_blockman.LookupBlockIndex(hashBlock)); + if (pindexMined != nullptr) { + CacheBlockHeight(pindexMined->GetBlockHash(), pindexMined->nHeight); + minedHeight = pindexMined->nHeight; + } + } // Let's see if the TX that was locked by this islock is already mined in a ChainLocked block. If yes, // we can simply ignore the islock, as the ChainLock implies locking of all TXs in that chain - if (pindexMined != nullptr && clhandler.HasChainLock(pindexMined->nHeight, pindexMined->GetBlockHash())) { - LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- txlock=%s, islock=%s: dropping islock as it already got a ChainLock in block %s, peer=%d\n", __func__, - islock->txid.ToString(), hash.ToString(), hashBlock.ToString(), from); + if (clhandler.HasChainLock(minedHeight, hashBlock)) { + LogPrint(BCLog::INSTANTSEND, /* Continued */ + "CInstantSendManager::%s -- txlock=%s, islock=%s: dropping islock as it already got a " + "ChainLock in block %s, peer=%d\n", + __func__, islock->txid.ToString(), hash.ToString(), hashBlock.ToString(), from); return std::monostate{}; } } if (found_transaction) { db.WriteNewInstantSendLock(hash, islock); - if (pindexMined) { - db.WriteInstantSendLockMined(hash, pindexMined->nHeight); + if (minedHeight >= 0) { + db.WriteInstantSendLockMined(hash, minedHeight); } } else { // put it in a separate pending map and try again later @@ -251,6 +260,12 @@ void CInstantSendManager::BlockConnected(const std::shared_ptr& pb return; } + { + LOCK(cs_height_cache); + CacheBlockHeightInternal(pindex->GetBlockHash(), pindex->nHeight); + m_cached_tip_height = pindex->nHeight; + } + if (m_mn_sync.IsBlockchainSynced()) { const bool has_chainlock = clhandler.HasChainLock(pindex->nHeight, pindex->GetBlockHash()); for (const auto& tx : pblock->vtx) { @@ -278,6 +293,16 @@ void CInstantSendManager::BlockConnected(const std::shared_ptr& pb void CInstantSendManager::BlockDisconnected(const std::shared_ptr& pblock, const CBlockIndex* pindexDisconnected) { + { + LOCK(cs_height_cache); + m_cached_block_heights.erase(pindexDisconnected->GetBlockHash()); + const CBlockIndex* const new_tip = pindexDisconnected->pprev; + m_cached_tip_height = new_tip ? new_tip->nHeight : -1; + if (new_tip) { + CacheBlockHeightInternal(new_tip->GetBlockHash(), new_tip->nHeight); + } + } + db.RemoveBlockInstantSendLocks(pblock, pindexDisconnected); } @@ -417,6 +442,12 @@ void CInstantSendManager::NotifyChainLock(const CBlockIndex* pindexChainLock) void CInstantSendManager::UpdatedBlockTip(const CBlockIndex* pindexNew) { + { + LOCK(cs_height_cache); + CacheBlockHeightInternal(pindexNew->GetBlockHash(), pindexNew->nHeight); + m_cached_tip_height = pindexNew->nHeight; + } + bool fDIP0008Active = pindexNew->pprev && pindexNew->pprev->nHeight >= Params().GetConsensus().DIP0008Height; if (AreChainLocksEnabled(spork_manager) && fDIP0008Active) { @@ -597,7 +628,7 @@ void CInstantSendManager::RemoveConflictingLock(const uint256& islockHash, const { LogPrintf("CInstantSendManager::%s -- txid=%s, islock=%s: Removing ISLOCK and its chained children\n", __func__, islock.txid.ToString(), islockHash.ToString()); - int tipHeight = WITH_LOCK(::cs_main, return m_chainstate.m_chain.Height()); + const int tipHeight = GetTipHeight(); auto removedIslocks = db.RemoveChainedInstantSendLocks(islockHash, islock.txid, tipHeight); for (const auto& h : removedIslocks) { @@ -703,6 +734,55 @@ size_t CInstantSendManager::GetInstantSendLockCount() const return db.GetInstantSendLockCount(); } +void CInstantSendManager::CacheBlockHeightInternal(const uint256& hash, int height) const +{ + AssertLockHeld(cs_height_cache); + m_cached_block_heights.insert(hash, height); +} + +void CInstantSendManager::CacheBlockHeight(const uint256& hash, int height) const +{ + LOCK(cs_height_cache); + CacheBlockHeightInternal(hash, height); +} + +std::optional CInstantSendManager::GetBlockHeight(const uint256& hash) const +{ + { + LOCK(cs_height_cache); + int cached_height{0}; + if (m_cached_block_heights.get(hash, cached_height)) return cached_height; + } + + const CBlockIndex* pindex = WITH_LOCK(::cs_main, return m_chainstate.m_blockman.LookupBlockIndex(hash)); + if (pindex == nullptr) { + return std::nullopt; + } + + CacheBlockHeight(pindex->GetBlockHash(), pindex->nHeight); + return pindex->nHeight; +} + +int CInstantSendManager::GetTipHeight() const +{ + { + LOCK(cs_height_cache); + if (m_cached_tip_height >= 0) { + return m_cached_tip_height; + } + } + + const CBlockIndex* tip = WITH_LOCK(::cs_main, return m_chainstate.m_chain.Tip()); + assert(tip != nullptr); + + { + LOCK(cs_height_cache); + CacheBlockHeightInternal(tip->GetBlockHash(), tip->nHeight); + m_cached_tip_height = tip->nHeight; + return m_cached_tip_height; + } +} + bool CInstantSendManager::IsInstantSendEnabled() const { return !fReindex && !fImporting && spork_manager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED); diff --git a/src/instantsend/instantsend.h b/src/instantsend/instantsend.h index 30e8ba4a8ebf..8709637f25f4 100644 --- a/src/instantsend/instantsend.h +++ b/src/instantsend/instantsend.h @@ -17,11 +17,13 @@ #include #include -#include +#include #include #include #include +#include + class CBlockIndex; class CChainState; class CDataStream; @@ -91,6 +93,14 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent mutable Mutex cs_timingsTxSeen; Uint256HashMap timingsTxSeen GUARDED_BY(cs_timingsTxSeen); + mutable Mutex cs_height_cache; + static constexpr size_t MAX_BLOCK_HEIGHT_CACHE{16384}; + mutable unordered_lru_cache m_cached_block_heights + GUARDED_BY(cs_height_cache); + mutable int m_cached_tip_height GUARDED_BY(cs_height_cache){-1}; + + void CacheBlockHeightInternal(const uint256& hash, int height) const EXCLUSIVE_LOCKS_REQUIRED(cs_height_cache); + public: CInstantSendManager() = delete; CInstantSendManager(const CInstantSendManager&) = delete; @@ -122,7 +132,7 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent void RemoveMempoolConflictsForLock(const uint256& hash, const instantsend::InstantSendLock& islock) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); void ResolveBlockConflicts(const uint256& islockHash, const instantsend::InstantSendLock& islock) - EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks, !cs_pendingRetry); + EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks, !cs_pendingRetry, !cs_height_cache); void HandleFullyConfirmedBlock(const CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); @@ -142,14 +152,15 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent CSigningManager& Sigman() { return sigman; } [[nodiscard]] std::variant ProcessInstantSendLock( NodeId from, const uint256& hash, const instantsend::InstantSendLockPtr& islock) - EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks, !cs_pendingRetry); + EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks, !cs_pendingRetry, !cs_height_cache); void TransactionAddedToMempool(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks, !cs_pendingRetry, !cs_timingsTxSeen); - void TransactionRemovedFromMempool(const CTransactionRef& tx); + void TransactionRemovedFromMempool(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); void BlockConnected(const std::shared_ptr& pblock, const CBlockIndex* pindex) - EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks, !cs_pendingRetry, !cs_timingsTxSeen); - void BlockDisconnected(const std::shared_ptr& pblock, const CBlockIndex* pindexDisconnected); + EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks, !cs_pendingRetry, !cs_timingsTxSeen, !cs_height_cache); + void BlockDisconnected(const std::shared_ptr& pblock, const CBlockIndex* pindexDisconnected) + EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); bool AlreadyHave(const CInv& inv) const EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); bool GetInstantSendLockByHash(const uint256& hash, instantsend::InstantSendLock& ret) const @@ -159,14 +170,19 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent void NotifyChainLock(const CBlockIndex* pindexChainLock) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); void UpdatedBlockTip(const CBlockIndex* pindexNew) - EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); + EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry, !cs_height_cache); - void RemoveConflictingLock(const uint256& islockHash, const instantsend::InstantSendLock& islock); + void RemoveConflictingLock(const uint256& islockHash, const instantsend::InstantSendLock& islock) + EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); void TryEmplacePendingLock(const uint256& hash, const NodeId id, const instantsend::InstantSendLockPtr& islock) override EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); size_t GetInstantSendLockCount() const; + void CacheBlockHeight(const uint256& hash, int height) const EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); + std::optional GetBlockHeight(const uint256& hash) const override EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); + int GetTipHeight() const override EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); + bool IsInstantSendEnabled() const override; /** * If true, MN should sign all transactions, if false, MN should not sign diff --git a/src/instantsend/net_instantsend.cpp b/src/instantsend/net_instantsend.cpp index f4eeb5300ce5..0e4b4b38caf5 100644 --- a/src/instantsend/net_instantsend.cpp +++ b/src/instantsend/net_instantsend.cpp @@ -34,18 +34,18 @@ void NetInstantSend::ProcessMessage(CNode& pfrom, const std::string& msg_type, C return; } - int block_height = [this, islock] { + auto cycleHeightOpt = m_is_manager.GetBlockHeight(islock->cycleHash); + if (!cycleHeightOpt) { const auto blockIndex = WITH_LOCK(::cs_main, return m_chainstate.m_blockman.LookupBlockIndex(islock->cycleHash)); if (blockIndex == nullptr) { - return -1; + // Maybe we don't have the block yet or maybe some peer spams invalid values for cycleHash + m_peer_manager->PeerMisbehaving(pfrom.GetId(), 1); + return; } - return blockIndex->nHeight; - }(); - if (block_height < 0) { - // Maybe we don't have the block yet or maybe some peer spams invalid values for cycleHash - m_peer_manager->PeerMisbehaving(pfrom.GetId(), 1); - return; + m_is_manager.CacheBlockHeight(blockIndex); + cycleHeightOpt = blockIndex->nHeight; } + const int block_height = *cycleHeightOpt; // Deterministic islocks MUST use rotation based llmq auto llmqType = Params().GetConsensus().llmqTypeDIP0024InstantSend; @@ -125,16 +125,18 @@ Uint256HashSet NetInstantSend::ProcessPendingInstantSendLocks( continue; } - const auto blockIndex = WITH_LOCK(::cs_main, return m_chainstate.m_blockman.LookupBlockIndex(islock->cycleHash)); - if (blockIndex == nullptr) { + auto cycleHeightOpt = m_is_manager.GetBlockHeight(islock->cycleHash); + if (!cycleHeightOpt) { batchVerifier.badSources.emplace(nodeId); continue; } int nSignHeight{-1}; const auto dkgInterval = llmq_params.dkgInterval; - if (blockIndex->nHeight + dkgInterval < m_chainstate.m_chain.Height()) { - nSignHeight = blockIndex->nHeight + dkgInterval - 1; + const int tipHeight = m_is_manager.GetTipHeight(); + const int cycleHeight = *cycleHeightOpt; + if (cycleHeight + dkgInterval < tipHeight) { + nSignHeight = cycleHeight + dkgInterval - 1; } // For RegTest non-rotating quorum cycleHash has directly quorum hash auto quorum = llmq_params.useRotation ? llmq::SelectQuorumForSigning(llmq_params, m_chainstate.m_chain, m_qman, diff --git a/src/instantsend/signing.cpp b/src/instantsend/signing.cpp index 789c0135f968..984968d9c53d 100644 --- a/src/instantsend/signing.cpp +++ b/src/instantsend/signing.cpp @@ -204,16 +204,28 @@ bool InstantSendSigner::CheckCanLock(const COutPoint& outpoint, bool printDebug, return false; } - const CBlockIndex* pindexMined; - int nTxAge; - { - LOCK(::cs_main); - pindexMined = m_chainstate.m_blockman.LookupBlockIndex(hashBlock); - nTxAge = m_chainstate.m_chain.Height() - pindexMined->nHeight + 1; + const auto blockHeight = m_isman.GetBlockHeight(hashBlock); + if (!blockHeight) { + if (printDebug) { + LogPrint(BCLog::INSTANTSEND, "%s -- txid=%s: failed to determine mined height for parent TX %s\n", __func__, + txHash.ToString(), outpoint.hash.ToString()); + } + return false; + } + + const int tipHeight = m_isman.GetTipHeight(); + + if (tipHeight < *blockHeight) { + if (printDebug) { + LogPrint(BCLog::INSTANTSEND, "%s -- txid=%s: cached tip height %d is below block height %d for parent TX %s\n", + __func__, txHash.ToString(), tipHeight, *blockHeight, outpoint.hash.ToString()); + } + return false; } - if (nTxAge < nInstantSendConfirmationsRequired && - !m_clhandler.HasChainLock(pindexMined->nHeight, pindexMined->GetBlockHash())) { + const int nTxAge = tipHeight - *blockHeight + 1; + + if (nTxAge < nInstantSendConfirmationsRequired && !m_clhandler.HasChainLock(*blockHeight, hashBlock)) { if (printDebug) { LogPrint(BCLog::INSTANTSEND, "%s -- txid=%s: outpoint %s too new and not ChainLocked. nTxAge=%d, nInstantSendConfirmationsRequired=%d\n", __func__, txHash.ToString(), outpoint.ToStringShort(), nTxAge, nInstantSendConfirmationsRequired); diff --git a/src/instantsend/signing.h b/src/instantsend/signing.h index f62ea90c4a18..cf878e8c21e9 100644 --- a/src/instantsend/signing.h +++ b/src/instantsend/signing.h @@ -8,6 +8,8 @@ #include #include +#include + class CMasternodeSync; class CSporkManager; class CTxMemPool; @@ -34,6 +36,8 @@ class InstantSendSignerParent virtual bool IsLocked(const uint256& txHash) const = 0; virtual InstantSendLockPtr GetConflictingLock(const CTransaction& tx) const = 0; virtual void TryEmplacePendingLock(const uint256& hash, const NodeId id, const InstantSendLockPtr& islock) = 0; + virtual std::optional GetBlockHeight(const uint256& hash) const = 0; + virtual int GetTipHeight() const = 0; }; class InstantSendSigner final : public llmq::CRecoveredSigsListener From 493d077e8717cdb0f8aeed701a1feb444f64b1dc Mon Sep 17 00:00:00 2001 From: pasta Date: Thu, 13 Nov 2025 14:05:33 -0600 Subject: [PATCH 2/3] refactor: simplify optional usage --- src/instantsend/instantsend.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index da43d5396366..22561afc8c2f 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -147,11 +147,9 @@ std::variant CInstantSendManager::Proc const auto tx = GetTransaction(nullptr, &mempool, islock->txid, Params().GetConsensus(), hashBlock); const bool found_transaction{tx != nullptr}; // we ignore failure here as we must be able to propagate the lock even if we don't have the TX locally - int minedHeight{-1}; - if (found_transaction && !hashBlock.IsNull()) { - if (auto h = GetBlockHeight(hashBlock)) { - minedHeight = *h; - } else { + std::optional minedHeight = GetBlockHeight(hashBlock); + if (found_transaction) { + if (!minedHeight.has_value()) { const CBlockIndex* pindexMined = WITH_LOCK(::cs_main, return m_chainstate.m_blockman.LookupBlockIndex(hashBlock)); if (pindexMined != nullptr) { CacheBlockHeight(pindexMined->GetBlockHash(), pindexMined->nHeight); @@ -160,7 +158,7 @@ std::variant CInstantSendManager::Proc } // Let's see if the TX that was locked by this islock is already mined in a ChainLocked block. If yes, // we can simply ignore the islock, as the ChainLock implies locking of all TXs in that chain - if (clhandler.HasChainLock(minedHeight, hashBlock)) { + if (minedHeight.has_value() && clhandler.HasChainLock(*minedHeight, hashBlock)) { LogPrint(BCLog::INSTANTSEND, /* Continued */ "CInstantSendManager::%s -- txlock=%s, islock=%s: dropping islock as it already got a " "ChainLock in block %s, peer=%d\n", @@ -171,8 +169,8 @@ std::variant CInstantSendManager::Proc if (found_transaction) { db.WriteNewInstantSendLock(hash, islock); - if (minedHeight >= 0) { - db.WriteInstantSendLockMined(hash, minedHeight); + if (minedHeight.has_value()) { + db.WriteInstantSendLockMined(hash, *minedHeight); } } else { // put it in a separate pending map and try again later @@ -748,6 +746,9 @@ void CInstantSendManager::CacheBlockHeight(const uint256& hash, int height) cons std::optional CInstantSendManager::GetBlockHeight(const uint256& hash) const { + if (hash.IsNull()) { + return std::nullopt; + } { LOCK(cs_height_cache); int cached_height{0}; From e918729c2c109c2a1281d6ae3447521322ec8512 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Fri, 14 Nov 2025 14:44:00 +0300 Subject: [PATCH 3/3] refactor: add CacheTipHeight, improve cache API safety Add CacheTipHeight() helper to eliminate code duplication when updating tip height cache. Refactor cache methods to accept CBlockIndex* instead of hash/height pairs for type safety. Handle nullptr cases properly. --- src/instantsend/instantsend.cpp | 51 +++++++++++++++------------------ src/instantsend/instantsend.h | 5 ++-- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index 22561afc8c2f..68766bf0c71f 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -152,7 +152,7 @@ std::variant CInstantSendManager::Proc if (!minedHeight.has_value()) { const CBlockIndex* pindexMined = WITH_LOCK(::cs_main, return m_chainstate.m_blockman.LookupBlockIndex(hashBlock)); if (pindexMined != nullptr) { - CacheBlockHeight(pindexMined->GetBlockHash(), pindexMined->nHeight); + CacheBlockHeight(pindexMined); minedHeight = pindexMined->nHeight; } } @@ -258,11 +258,7 @@ void CInstantSendManager::BlockConnected(const std::shared_ptr& pb return; } - { - LOCK(cs_height_cache); - CacheBlockHeightInternal(pindex->GetBlockHash(), pindex->nHeight); - m_cached_tip_height = pindex->nHeight; - } + CacheTipHeight(pindex); if (m_mn_sync.IsBlockchainSynced()) { const bool has_chainlock = clhandler.HasChainLock(pindex->nHeight, pindex->GetBlockHash()); @@ -294,13 +290,10 @@ void CInstantSendManager::BlockDisconnected(const std::shared_ptr& { LOCK(cs_height_cache); m_cached_block_heights.erase(pindexDisconnected->GetBlockHash()); - const CBlockIndex* const new_tip = pindexDisconnected->pprev; - m_cached_tip_height = new_tip ? new_tip->nHeight : -1; - if (new_tip) { - CacheBlockHeightInternal(new_tip->GetBlockHash(), new_tip->nHeight); - } } + CacheTipHeight(pindexDisconnected->pprev); + db.RemoveBlockInstantSendLocks(pblock, pindexDisconnected); } @@ -440,11 +433,7 @@ void CInstantSendManager::NotifyChainLock(const CBlockIndex* pindexChainLock) void CInstantSendManager::UpdatedBlockTip(const CBlockIndex* pindexNew) { - { - LOCK(cs_height_cache); - CacheBlockHeightInternal(pindexNew->GetBlockHash(), pindexNew->nHeight); - m_cached_tip_height = pindexNew->nHeight; - } + CacheTipHeight(pindexNew); bool fDIP0008Active = pindexNew->pprev && pindexNew->pprev->nHeight >= Params().GetConsensus().DIP0008Height; @@ -732,16 +721,16 @@ size_t CInstantSendManager::GetInstantSendLockCount() const return db.GetInstantSendLockCount(); } -void CInstantSendManager::CacheBlockHeightInternal(const uint256& hash, int height) const +void CInstantSendManager::CacheBlockHeightInternal(const CBlockIndex* const block_index) const { AssertLockHeld(cs_height_cache); - m_cached_block_heights.insert(hash, height); + m_cached_block_heights.insert(block_index->GetBlockHash(), block_index->nHeight); } -void CInstantSendManager::CacheBlockHeight(const uint256& hash, int height) const +void CInstantSendManager::CacheBlockHeight(const CBlockIndex* const block_index) const { LOCK(cs_height_cache); - CacheBlockHeightInternal(hash, height); + CacheBlockHeightInternal(block_index); } std::optional CInstantSendManager::GetBlockHeight(const uint256& hash) const @@ -760,10 +749,21 @@ std::optional CInstantSendManager::GetBlockHeight(const uint256& hash) cons return std::nullopt; } - CacheBlockHeight(pindex->GetBlockHash(), pindex->nHeight); + CacheBlockHeight(pindex); return pindex->nHeight; } +void CInstantSendManager::CacheTipHeight(const CBlockIndex* const tip) const +{ + LOCK(cs_height_cache); + if (tip) { + CacheBlockHeightInternal(tip); + m_cached_tip_height = tip->nHeight; + } else { + m_cached_tip_height = -1; + } +} + int CInstantSendManager::GetTipHeight() const { { @@ -774,14 +774,9 @@ int CInstantSendManager::GetTipHeight() const } const CBlockIndex* tip = WITH_LOCK(::cs_main, return m_chainstate.m_chain.Tip()); - assert(tip != nullptr); - { - LOCK(cs_height_cache); - CacheBlockHeightInternal(tip->GetBlockHash(), tip->nHeight); - m_cached_tip_height = tip->nHeight; - return m_cached_tip_height; - } + CacheTipHeight(tip); + return tip ? tip->nHeight : -1; } bool CInstantSendManager::IsInstantSendEnabled() const diff --git a/src/instantsend/instantsend.h b/src/instantsend/instantsend.h index 8709637f25f4..9b93198e85f4 100644 --- a/src/instantsend/instantsend.h +++ b/src/instantsend/instantsend.h @@ -99,7 +99,7 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent GUARDED_BY(cs_height_cache); mutable int m_cached_tip_height GUARDED_BY(cs_height_cache){-1}; - void CacheBlockHeightInternal(const uint256& hash, int height) const EXCLUSIVE_LOCKS_REQUIRED(cs_height_cache); + void CacheBlockHeightInternal(const CBlockIndex* const block_index) const EXCLUSIVE_LOCKS_REQUIRED(cs_height_cache); public: CInstantSendManager() = delete; @@ -179,8 +179,9 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent size_t GetInstantSendLockCount() const; - void CacheBlockHeight(const uint256& hash, int height) const EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); + void CacheBlockHeight(const CBlockIndex* const block_index) const EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); std::optional GetBlockHeight(const uint256& hash) const override EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); + void CacheTipHeight(const CBlockIndex* const tip) const EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); int GetTipHeight() const override EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); bool IsInstantSendEnabled() const override;