-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat: instantsend cache heights and reduce cs_main contention #6953
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -145,25 +145,32 @@ std::variant<uint256, CTransactionRef, std::monostate> 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 | ||
| if (found_transaction && !hashBlock.IsNull()) { | ||
| pindexMined = WITH_LOCK(::cs_main, return m_chainstate.m_blockman.LookupBlockIndex(hashBlock)); | ||
|
|
||
| std::optional<int> 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); | ||
| 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 (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", | ||
| __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.has_value()) { | ||
| db.WriteInstantSendLockMined(hash, *minedHeight); | ||
| } | ||
|
Comment on lines
150
to
174
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix clang-format issue. The pipeline indicates clang-format differences at lines 393-395. Please run clang-format to resolve the formatting issue. 🧰 Tools🪛 GitHub Actions: Clang Diff Format Check[error] 393-395: Clang format differences found. Apply clang-format changes to src/instantsend/instantsend.cpp. Exit code 1 due to unformatted code. 🤖 Prompt for AI Agents |
||
| } else { | ||
| // put it in a separate pending map and try again later | ||
|
|
@@ -251,6 +258,8 @@ void CInstantSendManager::BlockConnected(const std::shared_ptr<const CBlock>& pb | |
| return; | ||
| } | ||
|
|
||
| CacheTipHeight(pindex); | ||
|
|
||
| if (m_mn_sync.IsBlockchainSynced()) { | ||
| const bool has_chainlock = clhandler.HasChainLock(pindex->nHeight, pindex->GetBlockHash()); | ||
| for (const auto& tx : pblock->vtx) { | ||
|
|
@@ -278,6 +287,13 @@ void CInstantSendManager::BlockConnected(const std::shared_ptr<const CBlock>& pb | |
| void CInstantSendManager::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, | ||
| const CBlockIndex* pindexDisconnected) | ||
| { | ||
| { | ||
| LOCK(cs_height_cache); | ||
| m_cached_block_heights.erase(pindexDisconnected->GetBlockHash()); | ||
| } | ||
|
|
||
| CacheTipHeight(pindexDisconnected->pprev); | ||
|
|
||
| db.RemoveBlockInstantSendLocks(pblock, pindexDisconnected); | ||
| } | ||
|
|
||
|
|
@@ -417,6 +433,8 @@ void CInstantSendManager::NotifyChainLock(const CBlockIndex* pindexChainLock) | |
|
|
||
| void CInstantSendManager::UpdatedBlockTip(const CBlockIndex* pindexNew) | ||
| { | ||
| CacheTipHeight(pindexNew); | ||
|
|
||
| bool fDIP0008Active = pindexNew->pprev && pindexNew->pprev->nHeight >= Params().GetConsensus().DIP0008Height; | ||
|
|
||
| if (AreChainLocksEnabled(spork_manager) && fDIP0008Active) { | ||
|
|
@@ -597,7 +615,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 +721,64 @@ size_t CInstantSendManager::GetInstantSendLockCount() const | |
| return db.GetInstantSendLockCount(); | ||
| } | ||
|
|
||
| void CInstantSendManager::CacheBlockHeightInternal(const CBlockIndex* const block_index) const | ||
| { | ||
| AssertLockHeld(cs_height_cache); | ||
| m_cached_block_heights.insert(block_index->GetBlockHash(), block_index->nHeight); | ||
| } | ||
|
|
||
| void CInstantSendManager::CacheBlockHeight(const CBlockIndex* const block_index) const | ||
| { | ||
| LOCK(cs_height_cache); | ||
| CacheBlockHeightInternal(block_index); | ||
| } | ||
|
|
||
| std::optional<int> CInstantSendManager::GetBlockHeight(const uint256& hash) const | ||
| { | ||
| if (hash.IsNull()) { | ||
| return std::nullopt; | ||
| } | ||
| { | ||
| 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); | ||
| 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 | ||
| { | ||
| { | ||
| 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()); | ||
|
|
||
| CacheTipHeight(tip); | ||
| return tip ? tip->nHeight : -1; | ||
| } | ||
|
|
||
| bool CInstantSendManager::IsInstantSendEnabled() const | ||
| { | ||
| return !fReindex && !fImporting && spork_manager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,11 +17,13 @@ | |
| #include <unordered_lru_cache.h> | ||
|
|
||
| #include <atomic> | ||
| #include <unordered_map> | ||
| #include <optional> | ||
| #include <unordered_set> | ||
| #include <variant> | ||
| #include <vector> | ||
|
|
||
| #include <saltedhasher.h> | ||
|
|
||
| class CBlockIndex; | ||
| class CChainState; | ||
| class CDataStream; | ||
|
|
@@ -91,6 +93,14 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent | |
| mutable Mutex cs_timingsTxSeen; | ||
| Uint256HashMap<int64_t> timingsTxSeen GUARDED_BY(cs_timingsTxSeen); | ||
|
|
||
| mutable Mutex cs_height_cache; | ||
| static constexpr size_t MAX_BLOCK_HEIGHT_CACHE{16384}; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. isn't 16k a bit too much? rotation quorums are not alive that much; also it's very unlikely situation that tx will exist that long before it will be mined. 16k is roughly 28 days |
||
| mutable unordered_lru_cache<uint256, int, StaticSaltedHasher, MAX_BLOCK_HEIGHT_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 CBlockIndex* const block_index) 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<uint256, CTransactionRef, std::monostate> 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<const CBlock>& pblock, const CBlockIndex* pindex) | ||
| EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks, !cs_pendingRetry, !cs_timingsTxSeen); | ||
| void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexDisconnected); | ||
| EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks, !cs_pendingRetry, !cs_timingsTxSeen, !cs_height_cache); | ||
| void BlockDisconnected(const std::shared_ptr<const CBlock>& 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,20 @@ 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 CBlockIndex* const block_index) const EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); | ||
| std::optional<int> 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; | ||
| /** | ||
| * If true, MN should sign all transactions, if false, MN should not sign | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: use snake_case for local variables for new code; it should be
mined_heightdoc/developer-nodes.md: