From c0cdfd04fa7c8c7269de4ba6df83dad0e6a304fb Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 29 Jan 2026 02:25:05 +0700 Subject: [PATCH 01/15] refactor: move ProcessInstantSendLock to NetInstantSend --- src/init.cpp | 2 +- src/instantsend/instantsend.cpp | 89 ++++++----------------------- src/instantsend/instantsend.h | 18 ++++-- src/instantsend/net_instantsend.cpp | 72 ++++++++++++++++++++++- src/instantsend/net_instantsend.h | 13 ++++- 5 files changed, 115 insertions(+), 79 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index fdb3e5af3998..44077fcaa606 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2207,7 +2207,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // ********************************************************* Step 7d: Setup other Dash services - node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.llmq_ctx->isman, *node.llmq_ctx->qman, chainman.ActiveChainstate())); + node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.llmq_ctx->isman, *node.llmq_ctx->qman, chainman.ActiveChainstate(), *node.mempool)); node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.llmq_ctx->sigman, node.active_ctx ? node.active_ctx->shareman.get() : nullptr, *node.sporkman)); if (node.active_ctx) { diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index 0726da6911e3..92f4b2a7bc0f 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -14,17 +14,9 @@ #include #include #include -#include - -// Forward declaration to break dependency over node/transaction.h -namespace node { -CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, - const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock); -} // namespace node using node::fImporting; using node::fReindex; -using node::GetTransaction; namespace llmq { namespace { @@ -118,22 +110,15 @@ instantsend::PendingState CInstantSendManager::FetchPendingLocks() return ret; } -std::variant CInstantSendManager::ProcessInstantSendLock( - NodeId from, const uint256& hash, const instantsend::InstantSendLockPtr& islock) +bool CInstantSendManager::PreVerifyIsLock(const uint256& hash, const instantsend::InstantSendLockPtr& islock, NodeId from) const { - LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- txid=%s, islock=%s: processing islock, peer=%d\n", - __func__, islock->txid.ToString(), hash.ToString(), from); - - if (auto signer = m_signer.load(std::memory_order_acquire); signer) { - signer->ClearLockFromQueue(islock); - } if (db.KnownInstantSendLock(hash)) { - return std::monostate{}; + return false; } if (const auto sameTxIsLock = db.GetInstantSendLockByTxid(islock->txid)) { // can happen, nothing to do - return std::monostate{}; + return false; } for (const auto& in : islock->inputs) { const auto sameOutpointIsLock = db.GetInstantSendLockByInput(in); @@ -142,62 +127,23 @@ std::variant CInstantSendManager::Proc islock->txid.ToString(), hash.ToString(), in.ToStringShort(), ::SerializeHash(*sameOutpointIsLock).ToString(), from); } } + return true; +} - uint256 hashBlock{}; - 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 - 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); - 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 (minedHeight.has_value() && m_chainlocks.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 (minedHeight.has_value()) { - db.WriteInstantSendLockMined(hash, *minedHeight); - } - } else { - // put it in a separate pending map and try again later - LOCK(cs_pendingLocks); - pendingNoTxInstantSendLocks.try_emplace(hash, instantsend::PendingISLockFromPeer{from, islock}); - } - - // This will also add children TXs to pendingRetryTxs - RemoveNonLockedTx(islock->txid, true); - // We don't need the recovered sigs for the inputs anymore. This prevents unnecessary propagation of these sigs. - // We only need the ISLOCK from now on to detect conflicts - TruncateRecoveredSigsForInputs(*islock); - ResolveBlockConflicts(hash, *islock); - - if (found_transaction) { - RemoveMempoolConflictsForLock(hash, *islock); - LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- notify about lock %s for tx %s\n", __func__, - hash.ToString(), tx->GetHash().ToString()); - GetMainSignals().NotifyTransactionLock(tx, islock); - // bump mempool counter to make sure newly locked txes are picked up by getblocktemplate - mempool.AddTransactionsUpdated(1); +void CInstantSendManager::WriteNewISLock(const uint256& hash, const instantsend::InstantSendLockPtr& islock, + std::optional minedHeight) +{ + db.WriteNewInstantSendLock(hash, islock); + if (minedHeight.has_value()) { + db.WriteInstantSendLockMined(hash, *minedHeight); } +} - if (found_transaction) { - return tx; - } - return islock->txid; +void CInstantSendManager::AddPendingISLock(const uint256& hash, const instantsend::InstantSendLockPtr& islock, NodeId from) +{ + // put it in a separate pending map and try again later + LOCK(cs_pendingLocks); + pendingNoTxInstantSendLocks.try_emplace(hash, instantsend::PendingISLockFromPeer{from, islock}); } void CInstantSendManager::TransactionAddedToMempool(const CTransactionRef& tx) @@ -811,4 +757,5 @@ bool CInstantSendManager::RejectConflictingBlocks() const } return true; } + } // namespace llmq diff --git a/src/instantsend/instantsend.h b/src/instantsend/instantsend.h index dde3739d6030..e11f8ff61b58 100644 --- a/src/instantsend/instantsend.h +++ b/src/instantsend/instantsend.h @@ -130,17 +130,23 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent private: void AddNonLockedTx(const CTransactionRef& tx, const CBlockIndex* pindexMined) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks, !cs_timingsTxSeen); + +public: void RemoveNonLockedTx(const uint256& txid, bool retryChildren) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); + +private: void RemoveConflictedTx(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); - void TruncateRecoveredSigsForInputs(const instantsend::InstantSendLock& islock); +public: + void TruncateRecoveredSigsForInputs(const instantsend::InstantSendLock& islock); 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, !cs_height_cache); +private: void HandleFullyConfirmedBlock(const CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); @@ -157,9 +163,13 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent [[nodiscard]] std::vector PrepareTxToRetry() EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); 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, !cs_height_cache); + const chainlock::Chainlocks& Chainlocks() { return m_chainlocks; } + + void WriteNewISLock(const uint256& hash, const instantsend::InstantSendLockPtr& islock, std::optional minedHeight); + void AddPendingISLock(const uint256& hash, const instantsend::InstantSendLockPtr& islock, NodeId from) + EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); + + bool PreVerifyIsLock(const uint256& hash, const instantsend::InstantSendLockPtr& islock, NodeId from) const; void TransactionAddedToMempool(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks, !cs_pendingRetry, !cs_timingsTxSeen); diff --git a/src/instantsend/net_instantsend.cpp b/src/instantsend/net_instantsend.cpp index 64fcfd0fd8ee..3779e9159e77 100644 --- a/src/instantsend/net_instantsend.cpp +++ b/src/instantsend/net_instantsend.cpp @@ -5,19 +5,27 @@ #include #include -#include +#include #include #include #include #include #include #include +#include #include #include #include #include +// Forward declaration to break dependency over node/transaction.h +namespace node { +CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, + const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock); +} // namespace node + +using node::GetTransaction; namespace { constexpr int BATCH_VERIFIER_SOURCE_THRESHOLD{8}; constexpr int INVALID_ISLOCK_MISBEHAVIOR_SCORE{100}; @@ -311,6 +319,68 @@ void NetInstantSend::ProcessPendingISLocks(std::vector NetInstantSend::ProcessInstantSendLock( + NodeId from, const uint256& hash, const instantsend::InstantSendLockPtr& islock) +{ + LogPrint(BCLog::INSTANTSEND, "NetSigning::%s -- txid=%s, islock=%s: processing islock, peer=%d\n", __func__, + islock->txid.ToString(), hash.ToString(), from); + + if (auto signer = m_is_manager.Signer(); signer) { + signer->ClearLockFromQueue(islock); + } + if (!m_is_manager.PreVerifyIsLock(hash, islock, from)) return std::monostate{}; + + uint256 hashBlock{}; + auto tx = GetTransaction(nullptr, &m_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 + std::optional minedHeight = m_is_manager.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) { + m_is_manager.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 (minedHeight.has_value() && m_is_manager.Chainlocks().HasChainLock(*minedHeight, hashBlock)) { + LogPrint(BCLog::INSTANTSEND, /* Continued */ + "NetSigning::%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{}; + } + m_is_manager.WriteNewISLock(hash, islock, minedHeight); + } else { + m_is_manager.AddPendingISLock(hash, islock, from); + } + + + // This will also add children TXs to pendingRetryTxs + m_is_manager.RemoveNonLockedTx(islock->txid, true); + // We don't need the recovered sigs for the inputs anymore. This prevents unnecessary propagation of these sigs. + // We only need the ISLOCK from now on to detect conflicts + m_is_manager.TruncateRecoveredSigsForInputs(*islock); + m_is_manager.ResolveBlockConflicts(hash, *islock); + + if (found_transaction) { + m_is_manager.RemoveMempoolConflictsForLock(hash, *islock); + LogPrint(BCLog::INSTANTSEND, "NetSigning::%s -- notify about lock %s for tx %s\n", __func__, hash.ToString(), + tx->GetHash().ToString()); + GetMainSignals().NotifyTransactionLock(tx, islock); + // bump mempool counter to make sure newly locked txes are picked up by getblocktemplate + m_mempool.AddTransactionsUpdated(1); + } + + if (found_transaction) { + return tx; + } + return islock->txid; +} + void NetInstantSend::WorkThreadMain() { while (!workInterrupt) { diff --git a/src/instantsend/net_instantsend.h b/src/instantsend/net_instantsend.h index 5601cbb5f1c2..f90331cacbb3 100644 --- a/src/instantsend/net_instantsend.h +++ b/src/instantsend/net_instantsend.h @@ -13,13 +13,18 @@ #include #include #include +#include #include namespace Consensus { struct LLMQParams; } // namespace Consensus + +class CTxMemPool; + namespace instantsend { struct InstantSendLock; +using InstantSendLockPtr = std::shared_ptr; struct PendingISLockEntry; } // namespace instantsend namespace llmq { @@ -31,11 +36,12 @@ class NetInstantSend final : public NetHandler { public: NetInstantSend(PeerManagerInternal* peer_manager, llmq::CInstantSendManager& is_manager, llmq::CQuorumManager& qman, - CChainState& chainstate) : + CChainState& chainstate, CTxMemPool& mempool) : NetHandler(peer_manager), m_is_manager{is_manager}, m_qman(qman), - m_chainstate{chainstate} + m_chainstate{chainstate}, + m_mempool{mempool} { workInterrupt.reset(); } @@ -63,6 +69,8 @@ class NetInstantSend final : public NetHandler const std::vector& pend); void ProcessPendingISLocks(std::vector&& locks_to_process); + std::variant ProcessInstantSendLock( + NodeId from, const uint256& hash, const instantsend::InstantSendLockPtr& islock); Uint256HashSet ProcessPendingInstantSendLocks( const Consensus::LLMQParams& llmq_params, int signOffset, bool ban, @@ -70,6 +78,7 @@ class NetInstantSend final : public NetHandler llmq::CInstantSendManager& m_is_manager; llmq::CQuorumManager& m_qman; const CChainState& m_chainstate; + CTxMemPool& m_mempool; std::thread workThread; CThreadInterrupt workInterrupt; From b954b5c9cd07bf289aa013144c9372ea3b9c22a8 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 29 Jan 2026 02:33:32 +0700 Subject: [PATCH 02/15] refactor: simplify ProcessInstantSendLock by using PeerManager on place instead of returning std::variant --- src/dsnotificationinterface.cpp | 1 - src/init.cpp | 2 +- src/instantsend/instantsend.cpp | 72 ++++++++----------------- src/instantsend/instantsend.h | 22 +++----- src/instantsend/net_instantsend.cpp | 81 ++++++++++++++++++++++------- src/instantsend/net_instantsend.h | 17 +++--- 6 files changed, 101 insertions(+), 94 deletions(-) diff --git a/src/dsnotificationinterface.cpp b/src/dsnotificationinterface.cpp index f2566970adc3..b899b4caccc7 100644 --- a/src/dsnotificationinterface.cpp +++ b/src/dsnotificationinterface.cpp @@ -85,7 +85,6 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con void CDSNotificationInterface::TransactionAddedToMempool(const CTransactionRef& ptx, int64_t nAcceptTime, uint64_t mempool_sequence) { - Assert(m_llmq_ctx)->isman->TransactionAddedToMempool(ptx); m_dstxman.TransactionAddedToMempool(ptx); } diff --git a/src/init.cpp b/src/init.cpp index 44077fcaa606..9d55a385b908 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2207,7 +2207,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // ********************************************************* Step 7d: Setup other Dash services - node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.llmq_ctx->isman, *node.llmq_ctx->qman, chainman.ActiveChainstate(), *node.mempool)); + node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.llmq_ctx->isman, *node.llmq_ctx->qman, chainman.ActiveChainstate(), *node.mempool, *node.mn_sync)); node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.llmq_ctx->sigman, node.active_ctx ? node.active_ctx->shareman.get() : nullptr, *node.sporkman)); if (node.active_ctx) { diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index 92f4b2a7bc0f..a8b2d61fd272 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -146,41 +146,6 @@ void CInstantSendManager::AddPendingISLock(const uint256& hash, const instantsen pendingNoTxInstantSendLocks.try_emplace(hash, instantsend::PendingISLockFromPeer{from, islock}); } -void CInstantSendManager::TransactionAddedToMempool(const CTransactionRef& tx) -{ - if (!IsInstantSendEnabled() || !m_mn_sync.IsBlockchainSynced() || tx->vin.empty()) { - return; - } - - instantsend::InstantSendLockPtr islock{nullptr}; - { - LOCK(cs_pendingLocks); - auto it = pendingNoTxInstantSendLocks.begin(); - while (it != pendingNoTxInstantSendLocks.end()) { - if (it->second.islock->txid == tx->GetHash()) { - // we received an islock earlier - LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- txid=%s, islock=%s\n", __func__, - tx->GetHash().ToString(), it->first.ToString()); - islock = it->second.islock; - pendingInstantSendLocks.try_emplace(it->first, it->second); - pendingNoTxInstantSendLocks.erase(it); - break; - } - ++it; - } - } - - if (islock == nullptr) { - if (auto signer = m_signer.load(std::memory_order_acquire); signer) { - signer->ProcessTx(*tx, false, Params().GetConsensus()); - } - // TX is not locked, so make sure it is tracked - AddNonLockedTx(tx, nullptr); - } else { - RemoveMempoolConflictsForLock(::SerializeHash(*islock), *islock); - } -} - void CInstantSendManager::TransactionRemovedFromMempool(const CTransactionRef& tx) { if (tx->vin.empty()) { @@ -243,6 +208,26 @@ void CInstantSendManager::BlockDisconnected(const std::shared_ptr& db.RemoveBlockInstantSendLocks(pblock, pindexDisconnected); } +instantsend::InstantSendLockPtr CInstantSendManager::AttachISLockToTx(const CTransactionRef& tx) +{ + instantsend::InstantSendLockPtr ret_islock{nullptr}; + LOCK(cs_pendingLocks); + auto it = pendingNoTxInstantSendLocks.begin(); + while (it != pendingNoTxInstantSendLocks.end()) { + if (it->second.islock->txid == tx->GetHash()) { + // we received an islock earlier, let's put it back into pending and verify/lock + LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- txid=%s, islock=%s\n", __func__, + tx->GetHash().ToString(), it->first.ToString()); + ret_islock = it->second.islock; + pendingInstantSendLocks.try_emplace(it->first, it->second); + pendingNoTxInstantSendLocks.erase(it); + return ret_islock; + } + ++it; + } + return ret_islock; // not found, nullptr +} + void CInstantSendManager::AddNonLockedTx(const CTransactionRef& tx, const CBlockIndex* pindexMined) { { @@ -259,22 +244,7 @@ void CInstantSendManager::AddNonLockedTx(const CTransactionRef& tx, const CBlock } } } - { - LOCK(cs_pendingLocks); - auto it = pendingNoTxInstantSendLocks.begin(); - while (it != pendingNoTxInstantSendLocks.end()) { - if (it->second.islock->txid == tx->GetHash()) { - // we received an islock earlier, let's put it back into pending and verify/lock - LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- txid=%s, islock=%s\n", __func__, - tx->GetHash().ToString(), it->first.ToString()); - pendingInstantSendLocks.try_emplace(it->first, it->second); - pendingNoTxInstantSendLocks.erase(it); - break; - } - ++it; - } - } - + AttachISLockToTx(tx); if (ShouldReportISLockTiming()) { LOCK(cs_timingsTxSeen); // Only insert the time the first time we see the tx, as we sometimes try to resign diff --git a/src/instantsend/instantsend.h b/src/instantsend/instantsend.h index e11f8ff61b58..71a32f1ea1f3 100644 --- a/src/instantsend/instantsend.h +++ b/src/instantsend/instantsend.h @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -127,22 +126,17 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent instantsend::InstantSendSigner* Signer() const { return m_signer.load(std::memory_order_acquire); } -private: void AddNonLockedTx(const CTransactionRef& tx, const CBlockIndex* pindexMined) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks, !cs_timingsTxSeen); - -public: void RemoveNonLockedTx(const uint256& txid, bool retryChildren) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); -private: + instantsend::InstantSendLockPtr AttachISLockToTx(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); + void RemoveConflictedTx(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); -public: void TruncateRecoveredSigsForInputs(const instantsend::InstantSendLock& islock); - 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, !cs_height_cache); @@ -171,24 +165,22 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent bool PreVerifyIsLock(const uint256& hash, const instantsend::InstantSendLockPtr& islock, NodeId from) const; - void TransactionAddedToMempool(const CTransactionRef& tx) - EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks, !cs_pendingRetry, !cs_timingsTxSeen); + // -- CValidationInterface + void UpdatedBlockTip(const CBlockIndex* pindexNew) + EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry, !cs_height_cache); 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, !cs_height_cache); void BlockDisconnected(const std::shared_ptr& pblock, const CBlockIndex* pindexDisconnected) EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); + void NotifyChainLock(const CBlockIndex* pindexChainLock) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); + bool AlreadyHave(const CInv& inv) const EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); bool GetInstantSendLockByHash(const uint256& hash, instantsend::InstantSendLock& ret) const EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); instantsend::InstantSendLockPtr GetInstantSendLockByTxid(const uint256& txid) const; - void NotifyChainLock(const CBlockIndex* pindexChainLock) - EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); - void UpdatedBlockTip(const CBlockIndex* pindexNew) - EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry, !cs_height_cache); - 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 diff --git a/src/instantsend/net_instantsend.cpp b/src/instantsend/net_instantsend.cpp index 3779e9159e77..5aa3495a0079 100644 --- a/src/instantsend/net_instantsend.cpp +++ b/src/instantsend/net_instantsend.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -21,7 +22,7 @@ // Forward declaration to break dependency over node/transaction.h namespace node { -CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, +CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const m_mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock); } // namespace node @@ -180,16 +181,7 @@ Uint256HashSet NetInstantSend::ApplyVerificationResults( continue; } - CInv inv(MSG_ISDLOCK, hash); - auto ret = m_is_manager.ProcessInstantSendLock(nodeId, hash, islock); - if (std::holds_alternative(ret)) { - m_peer_manager->PeerRelayInvFiltered(inv, std::get(ret)); - m_peer_manager->PeerAskPeersForTransaction(islock->txid); - } else if (std::holds_alternative(ret)) { - m_peer_manager->PeerRelayInvFiltered(inv, *std::get(ret)); - } else { - assert(std::holds_alternative(ret)); - } + ProcessInstantSendLock(nodeId, hash, islock); // Pass a reconstructed recovered sig to the signing manager to avoid double-verification of the sig. auto it = data.recSigs.find(hash); @@ -290,7 +282,6 @@ Uint256HashSet NetInstantSend::ProcessPendingInstantSendLocks( return ApplyVerificationResults(llmq_params, ban, *batch, pend); } - void NetInstantSend::ProcessPendingISLocks(std::vector&& locks_to_process) { // TODO Investigate if leaving this is ok @@ -319,8 +310,7 @@ void NetInstantSend::ProcessPendingISLocks(std::vector NetInstantSend::ProcessInstantSendLock( - NodeId from, const uint256& hash, const instantsend::InstantSendLockPtr& islock) +void NetInstantSend::ProcessInstantSendLock(NodeId from, const uint256& hash, const instantsend::InstantSendLockPtr& islock) { LogPrint(BCLog::INSTANTSEND, "NetSigning::%s -- txid=%s, islock=%s: processing islock, peer=%d\n", __func__, islock->txid.ToString(), hash.ToString(), from); @@ -328,7 +318,7 @@ std::variant NetInstantSend::ProcessIn if (auto signer = m_is_manager.Signer(); signer) { signer->ClearLockFromQueue(islock); } - if (!m_is_manager.PreVerifyIsLock(hash, islock, from)) return std::monostate{}; + if (!m_is_manager.PreVerifyIsLock(hash, islock, from)) return; uint256 hashBlock{}; auto tx = GetTransaction(nullptr, &m_mempool, islock->txid, Params().GetConsensus(), hashBlock); @@ -351,7 +341,7 @@ std::variant NetInstantSend::ProcessIn "NetSigning::%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{}; + return; } m_is_manager.WriteNewISLock(hash, islock, minedHeight); } else { @@ -367,18 +357,21 @@ std::variant NetInstantSend::ProcessIn m_is_manager.ResolveBlockConflicts(hash, *islock); if (found_transaction) { - m_is_manager.RemoveMempoolConflictsForLock(hash, *islock); + RemoveMempoolConflictsForLock(hash, *islock); LogPrint(BCLog::INSTANTSEND, "NetSigning::%s -- notify about lock %s for tx %s\n", __func__, hash.ToString(), tx->GetHash().ToString()); GetMainSignals().NotifyTransactionLock(tx, islock); - // bump mempool counter to make sure newly locked txes are picked up by getblocktemplate + // bump m_mempool counter to make sure newly locked txes are picked up by getblocktemplate m_mempool.AddTransactionsUpdated(1); } + CInv inv(MSG_ISDLOCK, hash); if (found_transaction) { - return tx; + m_peer_manager->PeerRelayInvFiltered(inv, *tx); + } else { + m_peer_manager->PeerRelayInvFiltered(inv, islock->txid); + m_peer_manager->PeerAskPeersForTransaction(islock->txid); } - return islock->txid; } void NetInstantSend::WorkThreadMain() @@ -402,3 +395,51 @@ void NetInstantSend::WorkThreadMain() } } } + +void NetInstantSend::TransactionAddedToMempool(const CTransactionRef& tx, int64_t, uint64_t mempool_sequence) +{ + if (!m_is_manager.IsInstantSendEnabled() || !m_mn_sync.IsBlockchainSynced() || tx->vin.empty()) { + return; + } + + instantsend::InstantSendLockPtr islock = m_is_manager.AttachISLockToTx(tx); + if (islock == nullptr) { + if (auto signer = m_is_manager.Signer(); signer) { + signer->ProcessTx(*tx, false, Params().GetConsensus()); + } + // TX is not locked, so make sure it is tracked + m_is_manager.AddNonLockedTx(tx, nullptr); + } else { + RemoveMempoolConflictsForLock(::SerializeHash(*islock), *islock); + } +} + +void NetInstantSend::RemoveMempoolConflictsForLock(const uint256& hash, const instantsend::InstantSendLock& islock) +{ + Uint256HashMap toDelete; + + { + LOCK(m_mempool.cs); + + for (const auto& in : islock.inputs) { + auto it = m_mempool.mapNextTx.find(in); + if (it == m_mempool.mapNextTx.end()) { + continue; + } + if (it->second->GetHash() != islock.txid) { + toDelete.emplace(it->second->GetHash(), m_mempool.get(it->second->GetHash())); + + LogPrintf("%s -- txid=%s, mempool TX %s with input %s conflicts with islock=%s\n", __func__, + islock.txid.ToString(), it->second->GetHash().ToString(), in.ToStringShort(), hash.ToString()); + } + } + + for (const auto& p : toDelete) { + m_mempool.removeRecursive(*p.second, MemPoolRemovalReason::CONFLICT); + } + } + + for (const auto& p : toDelete) { + m_is_manager.RemoveConflictedTx(*p.second); + } +} diff --git a/src/instantsend/net_instantsend.h b/src/instantsend/net_instantsend.h index f90331cacbb3..bc9270ac4065 100644 --- a/src/instantsend/net_instantsend.h +++ b/src/instantsend/net_instantsend.h @@ -9,17 +9,18 @@ #include #include +#include #include #include #include -#include #include namespace Consensus { struct LLMQParams; } // namespace Consensus +class CMasternodeSync; class CTxMemPool; namespace instantsend { @@ -32,16 +33,17 @@ class CInstantSendManager; class CQuorumManager; } // namespace llmq -class NetInstantSend final : public NetHandler +class NetInstantSend final : public NetHandler, public CValidationInterface { public: NetInstantSend(PeerManagerInternal* peer_manager, llmq::CInstantSendManager& is_manager, llmq::CQuorumManager& qman, - CChainState& chainstate, CTxMemPool& mempool) : + CChainState& chainstate, CTxMemPool& mempool, const CMasternodeSync& mn_sync) : NetHandler(peer_manager), m_is_manager{is_manager}, m_qman(qman), m_chainstate{chainstate}, - m_mempool{mempool} + m_mempool{mempool}, + m_mn_sync{mn_sync} { workInterrupt.reset(); } @@ -53,6 +55,9 @@ class NetInstantSend final : public NetHandler void WorkThreadMain(); +protected: + void TransactionAddedToMempool(const CTransactionRef&, int64_t, uint64_t mempool_sequence) override; + private: struct BatchVerificationData; @@ -69,8 +74,7 @@ class NetInstantSend final : public NetHandler const std::vector& pend); void ProcessPendingISLocks(std::vector&& locks_to_process); - std::variant ProcessInstantSendLock( - NodeId from, const uint256& hash, const instantsend::InstantSendLockPtr& islock); + void ProcessInstantSendLock(NodeId from, const uint256& hash, const instantsend::InstantSendLockPtr& islock); Uint256HashSet ProcessPendingInstantSendLocks( const Consensus::LLMQParams& llmq_params, int signOffset, bool ban, @@ -79,6 +83,7 @@ class NetInstantSend final : public NetHandler llmq::CQuorumManager& m_qman; const CChainState& m_chainstate; CTxMemPool& m_mempool; + const CMasternodeSync& m_mn_sync; std::thread workThread; CThreadInterrupt workInterrupt; From 3d3b7af8fcda487ed4110e7b18a38b39094c843e Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 29 Jan 2026 14:06:21 +0700 Subject: [PATCH 03/15] refactor: move usages of mempool from CInstantSendManager to NetInstantSend --- src/instantsend/instantsend.cpp | 30 ------------------------------ src/instantsend/net_instantsend.h | 1 + 2 files changed, 1 insertion(+), 30 deletions(-) diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index a8b2d61fd272..e01613678132 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -408,36 +408,6 @@ void CInstantSendManager::HandleFullyConfirmedBlock(const CBlockIndex* pindex) } } -void CInstantSendManager::RemoveMempoolConflictsForLock(const uint256& hash, const instantsend::InstantSendLock& islock) -{ - Uint256HashMap toDelete; - - { - LOCK(mempool.cs); - - for (const auto& in : islock.inputs) { - auto it = mempool.mapNextTx.find(in); - if (it == mempool.mapNextTx.end()) { - continue; - } - if (it->second->GetHash() != islock.txid) { - toDelete.emplace(it->second->GetHash(), mempool.get(it->second->GetHash())); - - LogPrintf("CInstantSendManager::%s -- txid=%s, islock=%s: mempool TX %s with input %s conflicts with islock\n", __func__, - islock.txid.ToString(), hash.ToString(), it->second->GetHash().ToString(), in.ToStringShort()); - } - } - - for (const auto& p : toDelete) { - mempool.removeRecursive(*p.second, MemPoolRemovalReason::CONFLICT); - } - } - - for (const auto& p : toDelete) { - RemoveConflictedTx(*p.second); - } -} - void CInstantSendManager::ResolveBlockConflicts(const uint256& islockHash, const instantsend::InstantSendLock& islock) { // Lets first collect all non-locked TXs which conflict with the given ISLOCK diff --git a/src/instantsend/net_instantsend.h b/src/instantsend/net_instantsend.h index bc9270ac4065..71b9970d73d7 100644 --- a/src/instantsend/net_instantsend.h +++ b/src/instantsend/net_instantsend.h @@ -75,6 +75,7 @@ class NetInstantSend final : public NetHandler, public CValidationInterface void ProcessPendingISLocks(std::vector&& locks_to_process); void ProcessInstantSendLock(NodeId from, const uint256& hash, const instantsend::InstantSendLockPtr& islock); + void RemoveMempoolConflictsForLock(const uint256& hash, const instantsend::InstantSendLock& islock); Uint256HashSet ProcessPendingInstantSendLocks( const Consensus::LLMQParams& llmq_params, int signOffset, bool ban, From 2ce8c4c7ce11490644004f8fa4a600559cf4d6cb Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 29 Jan 2026 14:11:31 +0700 Subject: [PATCH 04/15] refactor: drop dependency of mempool from instantsend/instantsend --- src/instantsend/instantsend.cpp | 4 +--- src/instantsend/instantsend.h | 6 ++---- src/llmq/context.cpp | 7 +++---- src/llmq/context.h | 6 ++---- src/node/chainstate.cpp | 2 +- test/lint/lint-circular-dependencies.py | 2 +- 6 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index e01613678132..345bc95fb12e 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include using node::fImporting; @@ -41,14 +40,13 @@ Uint256HashSet GetIdsFromLockable(const std::vector& vec) } // anonymous namespace CInstantSendManager::CInstantSendManager(const chainlock::Chainlocks& chainlocks, CChainState& chainstate, - CSigningManager& _sigman, CSporkManager& sporkman, CTxMemPool& _mempool, + CSigningManager& _sigman, CSporkManager& sporkman, const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params) : db{db_params}, m_chainlocks{chainlocks}, m_chainstate{chainstate}, sigman{_sigman}, spork_manager{sporkman}, - mempool{_mempool}, m_mn_sync{mn_sync} { } diff --git a/src/instantsend/instantsend.h b/src/instantsend/instantsend.h index 71a32f1ea1f3..05810f140d3d 100644 --- a/src/instantsend/instantsend.h +++ b/src/instantsend/instantsend.h @@ -28,7 +28,6 @@ class CChainState; class CDataStream; class CMasternodeSync; class CSporkManager; -class CTxMemPool; namespace Consensus { struct LLMQParams; } // namespace Consensus @@ -70,7 +69,6 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent CChainState& m_chainstate; CSigningManager& sigman; CSporkManager& spork_manager; - CTxMemPool& mempool; const CMasternodeSync& m_mn_sync; std::atomic m_signer{nullptr}; @@ -112,8 +110,8 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent CInstantSendManager(const CInstantSendManager&) = delete; CInstantSendManager& operator=(const CInstantSendManager&) = delete; explicit CInstantSendManager(const chainlock::Chainlocks& chainlocks, CChainState& chainstate, - CSigningManager& _sigman, CSporkManager& sporkman, CTxMemPool& _mempool, - const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params); + CSigningManager& _sigman, CSporkManager& sporkman, const CMasternodeSync& mn_sync, + const util::DbWrapperParams& db_params); ~CInstantSendManager(); void ConnectSigner(gsl::not_null signer) diff --git a/src/llmq/context.cpp b/src/llmq/context.cpp index cc43675a29ef..112af2780a1d 100644 --- a/src/llmq/context.cpp +++ b/src/llmq/context.cpp @@ -13,9 +13,8 @@ #include LLMQContext::LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSporkManager& sporkman, - chainlock::Chainlocks& chainlocks, CTxMemPool& mempool, ChainstateManager& chainman, - const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params, int8_t bls_threads, - int64_t max_recsigs_age) : + chainlock::Chainlocks& chainlocks, ChainstateManager& chainman, const CMasternodeSync& mn_sync, + const util::DbWrapperParams& db_params, int8_t bls_threads, int64_t max_recsigs_age) : bls_worker{std::make_shared()}, qsnapman{std::make_unique(evo_db)}, quorum_block_processor{std::make_unique(chainman.ActiveChainstate(), dmnman, evo_db, @@ -24,7 +23,7 @@ LLMQContext::LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSpork chainman, db_params)}, sigman{std::make_unique(*qman, db_params, max_recsigs_age)}, isman{std::make_unique(chainlocks, chainman.ActiveChainstate(), *sigman, sporkman, - mempool, mn_sync, db_params)} + mn_sync, db_params)} { // Have to start it early to let VerifyDB check ChainLock signatures in coinbase bls_worker->Start(); diff --git a/src/llmq/context.h b/src/llmq/context.h index 677d5b6a76c9..0968d320e21a 100644 --- a/src/llmq/context.h +++ b/src/llmq/context.h @@ -15,7 +15,6 @@ class CDeterministicMNManager; class CEvoDB; class CMasternodeSync; class CSporkManager; -class CTxMemPool; class PeerManager; namespace chainlock { @@ -39,9 +38,8 @@ struct LLMQContext { LLMQContext(const LLMQContext&) = delete; LLMQContext& operator=(const LLMQContext&) = delete; explicit LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSporkManager& sporkman, - chainlock::Chainlocks& chainlocks, CTxMemPool& mempool, ChainstateManager& chainman, - const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params, int8_t bls_threads, - int64_t max_recsigs_age); + chainlock::Chainlocks& chainlocks, ChainstateManager& chainman, const CMasternodeSync& mn_sync, + const util::DbWrapperParams& db_params, int8_t bls_threads, int64_t max_recsigs_age); ~LLMQContext(); /** Guaranteed if LLMQContext is initialized then all members are valid too diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index aee964546334..60f763aaa39f 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -227,7 +227,7 @@ void DashChainstateSetup(ChainstateManager& chainman, dmnman = std::make_unique(evodb, mn_metaman); llmq_ctx.reset(); - llmq_ctx = std::make_unique(*dmnman, evodb, sporkman, chainlocks, *mempool, chainman, mn_sync, + llmq_ctx = std::make_unique(*dmnman, evodb, sporkman, chainlocks, chainman, mn_sync, util::DbWrapperParams{.path = data_dir, .memory = llmq_dbs_in_memory, .wipe = llmq_dbs_wipe}, bls_threads, max_recsigs_age); mempool->ConnectManagers(dmnman.get(), llmq_ctx->isman.get()); diff --git a/test/lint/lint-circular-dependencies.py b/test/lint/lint-circular-dependencies.py index db7472d9eaef..289c81da1b29 100755 --- a/test/lint/lint-circular-dependencies.py +++ b/test/lint/lint-circular-dependencies.py @@ -42,7 +42,7 @@ "evo/specialtxman -> validation -> evo/specialtxman", "governance/classes -> governance/object -> governance/governance -> governance/classes", "governance/governance -> governance/signing -> governance/object -> governance/governance", - "instantsend/instantsend -> txmempool -> instantsend/instantsend", + "instantsend/instantsend -> validation -> txmempool -> instantsend/instantsend", "llmq/blockprocessor -> llmq/utils -> llmq/snapshot -> llmq/blockprocessor", "llmq/commitment -> llmq/utils -> llmq/snapshot -> llmq/commitment", "llmq/dkgsessionhandler -> net_processing -> llmq/dkgsessionmgr -> llmq/dkgsessionhandler", From d7e167b6f625a89cfdf16486c9f1b64624f203eb Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 30 Jan 2026 01:14:18 +0700 Subject: [PATCH 05/15] refactor: move all notification handlers from CInstantSendManager to NetInstantSend --- src/dsnotificationinterface.cpp | 33 +++---------- src/dsnotificationinterface.h | 14 ++---- src/init.cpp | 2 +- src/instantsend/instantsend.cpp | 75 +++++------------------------ src/instantsend/instantsend.h | 16 ++---- src/instantsend/net_instantsend.cpp | 69 ++++++++++++++++++++++++++ src/instantsend/net_instantsend.h | 11 +++-- src/net_processing.cpp | 4 +- 8 files changed, 105 insertions(+), 119 deletions(-) diff --git a/src/dsnotificationinterface.cpp b/src/dsnotificationinterface.cpp index b899b4caccc7..271ce2daa752 100644 --- a/src/dsnotificationinterface.cpp +++ b/src/dsnotificationinterface.cpp @@ -5,34 +5,25 @@ #include -#include -#include - #include #include #include #include #include -#include -#include -#include -#include #include +#include +#include -CDSNotificationInterface::CDSNotificationInterface(CConnman& connman, - CDSTXManager& dstxman, - CMasternodeSync& mn_sync, - CGovernanceManager& govman, - const ChainstateManager& chainman, - const std::unique_ptr& dmnman, - const std::unique_ptr& llmq_ctx) : + +CDSNotificationInterface::CDSNotificationInterface(CConnman& connman, CDSTXManager& dstxman, CMasternodeSync& mn_sync, + CGovernanceManager& govman, const ChainstateManager& chainman, + const std::unique_ptr& dmnman) : m_connman{connman}, m_dstxman{dstxman}, m_mn_sync{mn_sync}, m_govman{govman}, m_chainman{chainman}, - m_dmnman{dmnman}, - m_llmq_ctx{llmq_ctx} + m_dmnman{dmnman} { } @@ -76,7 +67,6 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con m_dstxman.UpdatedBlockTip(pindexNew); } - m_llmq_ctx->isman->UpdatedBlockTip(pindexNew); if (m_govman.IsValid()) { m_govman.UpdatedBlockTip(pindexNew); } @@ -88,21 +78,13 @@ void CDSNotificationInterface::TransactionAddedToMempool(const CTransactionRef& m_dstxman.TransactionAddedToMempool(ptx); } -void CDSNotificationInterface::TransactionRemovedFromMempool(const CTransactionRef& ptx, MemPoolRemovalReason reason, - uint64_t mempool_sequence) -{ - Assert(m_llmq_ctx)->isman->TransactionRemovedFromMempool(ptx); -} - void CDSNotificationInterface::BlockConnected(const std::shared_ptr& pblock, const CBlockIndex* pindex) { - Assert(m_llmq_ctx)->isman->BlockConnected(pblock, pindex); m_dstxman.BlockConnected(pblock, pindex); } void CDSNotificationInterface::BlockDisconnected(const std::shared_ptr& pblock, const CBlockIndex* pindexDisconnected) { - Assert(m_llmq_ctx)->isman->BlockDisconnected(pblock, pindexDisconnected); m_dstxman.BlockDisconnected(pblock, pindexDisconnected); } @@ -117,7 +99,6 @@ void CDSNotificationInterface::NotifyMasternodeListChanged(bool undo, const CDet void CDSNotificationInterface::NotifyChainLock(const CBlockIndex* pindex, const std::shared_ptr& clsig) { - Assert(m_llmq_ctx)->isman->NotifyChainLock(pindex); if (m_mn_sync.IsBlockchainSynced()) { m_dstxman.NotifyChainLock(pindex); } diff --git a/src/dsnotificationinterface.h b/src/dsnotificationinterface.h index f8ae3b02560e..07d49332fd60 100644 --- a/src/dsnotificationinterface.h +++ b/src/dsnotificationinterface.h @@ -13,7 +13,6 @@ class CDeterministicMNManager; class CGovernanceManager; class ChainstateManager; class CMasternodeSync; -struct LLMQContext; class CDSNotificationInterface : public CValidationInterface { @@ -21,13 +20,9 @@ class CDSNotificationInterface : public CValidationInterface CDSNotificationInterface() = delete; CDSNotificationInterface(const CDSNotificationInterface&) = delete; CDSNotificationInterface& operator=(const CDSNotificationInterface&) = delete; - explicit CDSNotificationInterface(CConnman& connman, - CDSTXManager& dstxman, - CMasternodeSync& mn_sync, - CGovernanceManager& govman, - const ChainstateManager& chainman, - const std::unique_ptr& dmnman, - const std::unique_ptr& llmq_ctx); + explicit CDSNotificationInterface(CConnman& connman, CDSTXManager& dstxman, CMasternodeSync& mn_sync, + CGovernanceManager& govman, const ChainstateManager& chainman, + const std::unique_ptr& dmnman); virtual ~CDSNotificationInterface(); // a small helper to initialize current block height in sub-modules on startup @@ -40,8 +35,6 @@ class CDSNotificationInterface : public CValidationInterface void SynchronousUpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override; void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override; void TransactionAddedToMempool(const CTransactionRef& tx, int64_t nAcceptTime, uint64_t mempool_sequence) override; - void TransactionRemovedFromMempool(const CTransactionRef& ptx, MemPoolRemovalReason reason, - uint64_t mempool_sequence) override; void BlockConnected(const std::shared_ptr& pblock, const CBlockIndex* pindex) override; void BlockDisconnected(const std::shared_ptr& pblock, const CBlockIndex* pindexDisconnected) override; void NotifyMasternodeListChanged(bool undo, const CDeterministicMNList& oldMNList, const CDeterministicMNListDiff& diff) override; @@ -54,7 +47,6 @@ class CDSNotificationInterface : public CValidationInterface CGovernanceManager& m_govman; const ChainstateManager& m_chainman; const std::unique_ptr& m_dmnman; - const std::unique_ptr& m_llmq_ctx; }; extern std::unique_ptr g_ds_notification_interface; diff --git a/src/init.cpp b/src/init.cpp index 9d55a385b908..fd70cda79cb9 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2175,7 +2175,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) RegisterValidationInterface(node.peerman.get()); g_ds_notification_interface = std::make_unique( - *node.connman, *node.dstxman, *node.mn_sync, *node.govman, chainman, node.dmnman, node.llmq_ctx + *node.connman, *node.dstxman, *node.mn_sync, *node.govman, chainman, node.dmnman // todo: replace unique_ptr for dmnman to reference ); RegisterValidationInterface(g_ds_notification_interface.get()); diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index 345bc95fb12e..9a3811605f88 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -144,13 +144,13 @@ void CInstantSendManager::AddPendingISLock(const uint256& hash, const instantsen pendingNoTxInstantSendLocks.try_emplace(hash, instantsend::PendingISLockFromPeer{from, islock}); } -void CInstantSendManager::TransactionRemovedFromMempool(const CTransactionRef& tx) +void CInstantSendManager::TransactionIsRemoved(const CTransactionRef& tx) { if (tx->vin.empty()) { return; } - instantsend::InstantSendLockPtr islock = db.GetInstantSendLockByTxid(tx->GetHash()); + instantsend::InstantSendLockPtr islock = GetInstantSendLockByTxid(tx->GetHash()); if (islock == nullptr) { return; @@ -161,49 +161,14 @@ void CInstantSendManager::TransactionRemovedFromMempool(const CTransactionRef& t RemoveConflictingLock(::SerializeHash(*islock), *islock); } -void CInstantSendManager::BlockConnected(const std::shared_ptr& pblock, const CBlockIndex* pindex) +void CInstantSendManager::WriteBlockISLocks(const std::shared_ptr& pblock, const CBlockIndex* pindex) { - if (!IsInstantSendEnabled()) { - return; - } - - CacheTipHeight(pindex); - - if (m_mn_sync.IsBlockchainSynced()) { - const bool has_chainlock = m_chainlocks.HasChainLock(pindex->nHeight, pindex->GetBlockHash()); - for (const auto& tx : pblock->vtx) { - if (tx->IsCoinBase() || tx->vin.empty()) { - // coinbase and TXs with no inputs can't be locked - continue; - } - - if (!IsLocked(tx->GetHash()) && !has_chainlock) { - if (auto signer = m_signer.load(std::memory_order_acquire); signer) { - signer->ProcessTx(*tx, true, Params().GetConsensus()); - } - // TX is not locked, so make sure it is tracked - AddNonLockedTx(tx, pindex); - } else { - // TX is locked, so make sure we don't track it anymore - RemoveNonLockedTx(tx->GetHash(), true); - } - } - } - db.WriteBlockInstantSendLocks(pblock, pindex); } -void CInstantSendManager::BlockDisconnected(const std::shared_ptr& pblock, - const CBlockIndex* pindexDisconnected) +void CInstantSendManager::RemoveBlockISLocks(const std::shared_ptr& pblock, const CBlockIndex* pindex) { - { - LOCK(cs_height_cache); - m_cached_block_heights.erase(pindexDisconnected->GetBlockHash()); - } - - CacheTipHeight(pindexDisconnected->pprev); - - db.RemoveBlockInstantSendLocks(pblock, pindexDisconnected); + db.RemoveBlockInstantSendLocks(pblock, pindex); } instantsend::InstantSendLockPtr CInstantSendManager::AttachISLockToTx(const CTransactionRef& tx) @@ -340,30 +305,6 @@ void CInstantSendManager::TryEmplacePendingLock(const uint256& hash, const NodeI } } -void CInstantSendManager::NotifyChainLock(const CBlockIndex* pindexChainLock) -{ - HandleFullyConfirmedBlock(pindexChainLock); -} - -void CInstantSendManager::UpdatedBlockTip(const CBlockIndex* pindexNew) -{ - CacheTipHeight(pindexNew); - - bool fDIP0008Active = pindexNew->pprev && pindexNew->pprev->nHeight >= Params().GetConsensus().DIP0008Height; - - if (m_chainlocks.IsEnabled() && fDIP0008Active) { - // Nothing to do here. We should keep all islocks and let chainlocks handle them. - return; - } - - int nConfirmedHeight = pindexNew->nHeight - Params().GetConsensus().nInstantSendKeepLock; - const CBlockIndex* pindex = pindexNew->GetAncestor(nConfirmedHeight); - - if (pindex) { - HandleFullyConfirmedBlock(pindex); - } -} - void CInstantSendManager::HandleFullyConfirmedBlock(const CBlockIndex* pindex) { if (!IsInstantSendEnabled()) { @@ -633,6 +574,12 @@ void CInstantSendManager::CacheBlockHeight(const CBlockIndex* const block_index) CacheBlockHeightInternal(block_index); } +void CInstantSendManager::CacheDisconnectBlock(const CBlockIndex* pindexDisconnected) +{ + LOCK(cs_height_cache); + m_cached_block_heights.erase(pindexDisconnected->GetBlockHash()); +} + std::optional CInstantSendManager::GetBlockHeight(const uint256& hash) const { if (hash.IsNull()) { diff --git a/src/instantsend/instantsend.h b/src/instantsend/instantsend.h index 05810f140d3d..7b5adc42b1fd 100644 --- a/src/instantsend/instantsend.h +++ b/src/instantsend/instantsend.h @@ -142,7 +142,6 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent void HandleFullyConfirmedBlock(const CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); -public: bool IsLocked(const uint256& txHash) const override; bool IsWaitingForTx(const uint256& txHash) const EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); instantsend::InstantSendLockPtr GetConflictingLock(const CTransaction& tx) const override; @@ -157,28 +156,20 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent CSigningManager& Sigman() { return sigman; } const chainlock::Chainlocks& Chainlocks() { return m_chainlocks; } + void RemoveBlockISLocks(const std::shared_ptr& pblock, const CBlockIndex* pindex); + void WriteBlockISLocks(const std::shared_ptr& pblock, const CBlockIndex* pindex); void WriteNewISLock(const uint256& hash, const instantsend::InstantSendLockPtr& islock, std::optional minedHeight); void AddPendingISLock(const uint256& hash, const instantsend::InstantSendLockPtr& islock, NodeId from) EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); bool PreVerifyIsLock(const uint256& hash, const instantsend::InstantSendLockPtr& islock, NodeId from) const; - // -- CValidationInterface - void UpdatedBlockTip(const CBlockIndex* pindexNew) - EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry, !cs_height_cache); - 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, !cs_height_cache); - void BlockDisconnected(const std::shared_ptr& pblock, const CBlockIndex* pindexDisconnected) - EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); - void NotifyChainLock(const CBlockIndex* pindexChainLock) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); - - bool AlreadyHave(const CInv& inv) const EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); bool GetInstantSendLockByHash(const uint256& hash, instantsend::InstantSendLock& ret) const EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); instantsend::InstantSendLockPtr GetInstantSendLockByTxid(const uint256& txid) const; + void TransactionIsRemoved(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); 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 @@ -195,6 +186,7 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent Counts GetCounts() const EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks, !cs_nonLocked); void CacheBlockHeight(const CBlockIndex* const block_index) const EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); + void CacheDisconnectBlock(const CBlockIndex* pindexDisconnected) 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); diff --git a/src/instantsend/net_instantsend.cpp b/src/instantsend/net_instantsend.cpp index 5aa3495a0079..d1779bb30d36 100644 --- a/src/instantsend/net_instantsend.cpp +++ b/src/instantsend/net_instantsend.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -443,3 +444,71 @@ void NetInstantSend::RemoveMempoolConflictsForLock(const uint256& hash, const in m_is_manager.RemoveConflictedTx(*p.second); } } + +void NetInstantSend::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload) +{ + m_is_manager.CacheTipHeight(pindexNew); + + bool fDIP0008Active = pindexNew->pprev && pindexNew->pprev->nHeight >= Params().GetConsensus().DIP0008Height; + + if (m_is_manager.Chainlocks().IsEnabled() && fDIP0008Active) { + // Nothing to do here. We should keep all islocks and let chainlocks handle them. + return; + } + + int nConfirmedHeight = pindexNew->nHeight - Params().GetConsensus().nInstantSendKeepLock; + const CBlockIndex* pindex = pindexNew->GetAncestor(nConfirmedHeight); + + if (pindex) { + m_is_manager.HandleFullyConfirmedBlock(pindex); + } +} + +void NetInstantSend::TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, + uint64_t mempool_sequence) +{ + m_is_manager.TransactionIsRemoved(tx); +} + +void NetInstantSend::BlockConnected(const std::shared_ptr& pblock, const CBlockIndex* pindex) +{ + if (!m_is_manager.IsInstantSendEnabled()) { + return; + } + + m_is_manager.CacheTipHeight(pindex); + + if (m_mn_sync.IsBlockchainSynced()) { + const bool has_chainlock = m_is_manager.Chainlocks().HasChainLock(pindex->nHeight, pindex->GetBlockHash()); + for (const auto& tx : pblock->vtx) { + if (tx->IsCoinBase() || tx->vin.empty()) { + // coinbase and TXs with no inputs can't be locked + continue; + } + + if (!m_is_manager.IsLocked(tx->GetHash()) && !has_chainlock) { + if (auto signer = m_is_manager.Signer(); signer) { + signer->ProcessTx(*tx, true, Params().GetConsensus()); + } + // TX is not locked, so make sure it is tracked + m_is_manager.AddNonLockedTx(tx, pindex); + } else { + // TX is locked, so make sure we don't track it anymore + m_is_manager.RemoveNonLockedTx(tx->GetHash(), true); + } + } + } + m_is_manager.WriteBlockISLocks(pblock, pindex); +} + +void NetInstantSend::BlockDisconnected(const std::shared_ptr& pblock, const CBlockIndex* pindexDisconnected) +{ + m_is_manager.CacheDisconnectBlock(pindexDisconnected); + m_is_manager.CacheTipHeight(pindexDisconnected->pprev); + m_is_manager.RemoveBlockISLocks(pblock, pindexDisconnected); +} + +void NetInstantSend::NotifyChainLock(const CBlockIndex* pindex, const std::shared_ptr& clsig) +{ + m_is_manager.HandleFullyConfirmedBlock(pindex); +} diff --git a/src/instantsend/net_instantsend.h b/src/instantsend/net_instantsend.h index 71b9970d73d7..b02f0d1d965e 100644 --- a/src/instantsend/net_instantsend.h +++ b/src/instantsend/net_instantsend.h @@ -6,8 +6,6 @@ #define BITCOIN_INSTANTSEND_NET_INSTANTSEND_H #include -#include - #include #include @@ -56,7 +54,14 @@ class NetInstantSend final : public NetHandler, public CValidationInterface void WorkThreadMain(); protected: + // -- CValidationInterface + void UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload) override; void TransactionAddedToMempool(const CTransactionRef&, int64_t, uint64_t mempool_sequence) override; + void TransactionRemovedFromMempool(const CTransactionRef& ptx, MemPoolRemovalReason reason, + uint64_t mempool_sequence) override; + void BlockConnected(const std::shared_ptr& pblock, const CBlockIndex* pindex) override; + void BlockDisconnected(const std::shared_ptr& pblock, const CBlockIndex* pindexDisconnected) override; + void NotifyChainLock(const CBlockIndex* pindex, const std::shared_ptr& clsig) override; private: struct BatchVerificationData; @@ -82,7 +87,7 @@ class NetInstantSend final : public NetHandler, public CValidationInterface const std::vector& pend); llmq::CInstantSendManager& m_is_manager; llmq::CQuorumManager& m_qman; - const CChainState& m_chainstate; + CChainState& m_chainstate; CTxMemPool& m_mempool; const CMasternodeSync& m_mn_sync; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 3d428992565e..f15700280c7a 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -4756,7 +4756,7 @@ void PeerManagerImpl::ProcessMessage( // We will continue to reject this tx since it has rejected // parents so avoid re-requesting it from other peers. m_recent_rejects.insert(tx.GetHash()); - m_llmq_ctx->isman->TransactionRemovedFromMempool(ptx); + m_llmq_ctx->isman->TransactionIsRemoved(ptx); } } else { m_recent_rejects.insert(tx.GetHash()); @@ -4787,7 +4787,7 @@ void PeerManagerImpl::ProcessMessage( pfrom.GetId(), state.ToString()); MaybePunishNodeForTx(pfrom.GetId(), state); - m_llmq_ctx->isman->TransactionRemovedFromMempool(ptx); + m_llmq_ctx->isman->TransactionIsRemoved(ptx); } return; } From b2abb13af74f2b3deae41c541263f37fd6bcde81 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 12 Feb 2026 14:17:37 +0700 Subject: [PATCH 06/15] refactor: move ResolveBlockConflicts to NetInstantSend --- src/instantsend/instantsend.cpp | 67 ++++------------------------- src/instantsend/instantsend.h | 6 ++- src/instantsend/net_instantsend.cpp | 66 +++++++++++++++++++++++++++- src/instantsend/net_instantsend.h | 3 ++ 4 files changed, 80 insertions(+), 62 deletions(-) diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index 9a3811605f88..714825ff4b9f 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -347,7 +347,8 @@ void CInstantSendManager::HandleFullyConfirmedBlock(const CBlockIndex* pindex) } } -void CInstantSendManager::ResolveBlockConflicts(const uint256& islockHash, const instantsend::InstantSendLock& islock) +std::unordered_map> CInstantSendManager::RetrieveISConflicts( + const uint256& islockHash, const instantsend::InstantSendLock& islock) { // Lets first collect all non-locked TXs which conflict with the given ISLOCK std::unordered_map> conflicts; @@ -375,65 +376,13 @@ void CInstantSendManager::ResolveBlockConflicts(const uint256& islockHash, const } } - // Lets see if any of the conflicts was already mined into a ChainLocked block - bool hasChainLockedConflict = false; - for (const auto& p : conflicts) { - const auto* pindex = p.first; - if (m_chainlocks.HasChainLock(pindex->nHeight, pindex->GetBlockHash())) { - hasChainLockedConflict = true; - break; - } - } - - // If a conflict was mined into a ChainLocked block, then we have no other choice and must prune the ISLOCK and all - // chained ISLOCKs that build on top of this one. The probability of this is practically zero and can only happen - // when large parts of the masternode network are controlled by an attacker. In this case we must still find - // consensus and its better to sacrifice individual ISLOCKs then to sacrifice whole ChainLocks. - if (hasChainLockedConflict) { - LogPrintf("CInstantSendManager::%s -- txid=%s, islock=%s: at least one conflicted TX already got a ChainLock\n", - __func__, islock.txid.ToString(), islockHash.ToString()); - RemoveConflictingLock(islockHash, islock); - return; - } - - bool isLockedTxKnown = WITH_LOCK(cs_pendingLocks, return pendingNoTxInstantSendLocks.find(islockHash) == - pendingNoTxInstantSendLocks.end()); - - bool activateBestChain = false; - for (const auto& p : conflicts) { - const auto* pindex = p.first; - for (const auto& p2 : p.second) { - const auto& tx = *p2.second; - RemoveConflictedTx(tx); - } - - LogPrintf("CInstantSendManager::%s -- invalidating block %s\n", __func__, pindex->GetBlockHash().ToString()); - - BlockValidationState state; - // need non-const pointer - auto pindex2 = WITH_LOCK(::cs_main, return m_chainstate.m_blockman.LookupBlockIndex(pindex->GetBlockHash())); - if (!m_chainstate.InvalidateBlock(state, pindex2)) { - LogPrintf("CInstantSendManager::%s -- InvalidateBlock failed: %s\n", __func__, state.ToString()); - // This should not have happened and we are in a state were it's not safe to continue anymore - assert(false); - } - if (isLockedTxKnown) { - activateBestChain = true; - } else { - LogPrintf("CInstantSendManager::%s -- resetting block %s\n", __func__, pindex2->GetBlockHash().ToString()); - LOCK(::cs_main); - m_chainstate.ResetBlockFailureFlags(pindex2); - } - } + return conflicts; +} - if (activateBestChain) { - BlockValidationState state; - if (!m_chainstate.ActivateBestChain(state)) { - LogPrintf("CInstantSendManager::%s -- ActivateBestChain failed: %s\n", __func__, state.ToString()); - // This should not have happened and we are in a state were it's not safe to continue anymore - assert(false); - } - } +bool CInstantSendManager::IsKnownTx(const uint256& islockHash) const +{ + LOCK(cs_pendingLocks); + return pendingNoTxInstantSendLocks.find(islockHash) == pendingNoTxInstantSendLocks.end(); } void CInstantSendManager::RemoveConflictingLock(const uint256& islockHash, const instantsend::InstantSendLock& islock) diff --git a/src/instantsend/instantsend.h b/src/instantsend/instantsend.h index 7b5adc42b1fd..ddb8e2e1a482 100644 --- a/src/instantsend/instantsend.h +++ b/src/instantsend/instantsend.h @@ -135,8 +135,10 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); void TruncateRecoveredSigsForInputs(const instantsend::InstantSendLock& islock); - void ResolveBlockConflicts(const uint256& islockHash, const instantsend::InstantSendLock& islock) - EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks, !cs_pendingRetry, !cs_height_cache); + std::unordered_map> RetrieveISConflicts( + const uint256& islockHash, const instantsend::InstantSendLock& islock) + EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks); + bool IsKnownTx(const uint256& islockHash) const EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); private: void HandleFullyConfirmedBlock(const CBlockIndex* pindex) diff --git a/src/instantsend/net_instantsend.cpp b/src/instantsend/net_instantsend.cpp index d1779bb30d36..62c031eeb110 100644 --- a/src/instantsend/net_instantsend.cpp +++ b/src/instantsend/net_instantsend.cpp @@ -355,7 +355,7 @@ void NetInstantSend::ProcessInstantSendLock(NodeId from, const uint256& hash, co // We don't need the recovered sigs for the inputs anymore. This prevents unnecessary propagation of these sigs. // We only need the ISLOCK from now on to detect conflicts m_is_manager.TruncateRecoveredSigsForInputs(*islock); - m_is_manager.ResolveBlockConflicts(hash, *islock); + ResolveBlockConflicts(hash, *islock); if (found_transaction) { RemoveMempoolConflictsForLock(hash, *islock); @@ -512,3 +512,67 @@ void NetInstantSend::NotifyChainLock(const CBlockIndex* pindex, const std::share { m_is_manager.HandleFullyConfirmedBlock(pindex); } + +void NetInstantSend::ResolveBlockConflicts(const uint256& islockHash, const instantsend::InstantSendLock& islock) +{ + auto conflicts = m_is_manager.RetrieveISConflicts(islockHash, islock); + + // Lets see if any of the conflicts was already mined into a ChainLocked block + bool hasChainLockedConflict = false; + for (const auto& p : conflicts) { + const auto* pindex = p.first; + if (m_is_manager.Chainlocks().HasChainLock(pindex->nHeight, pindex->GetBlockHash())) { + hasChainLockedConflict = true; + break; + } + } + + // If a conflict was mined into a ChainLocked block, then we have no other choice and must prune the ISLOCK and all + // chained ISLOCKs that build on top of this one. The probability of this is practically zero and can only happen + // when large parts of the masternode network are controlled by an attacker. In this case we must still find + // consensus and its better to sacrifice individual ISLOCKs then to sacrifice whole ChainLocks. + if (hasChainLockedConflict) { + LogPrintf("NetInstantSend::%s -- txid=%s, islock=%s: at least one conflicted TX already got a ChainLock\n", + __func__, islock.txid.ToString(), islockHash.ToString()); + m_is_manager.RemoveConflictingLock(islockHash, islock); + return; + } + + bool isLockedTxKnown = m_is_manager.IsKnownTx(islockHash); + + bool activateBestChain = false; + for (const auto& p : conflicts) { + const auto* pindex = p.first; + for (const auto& p2 : p.second) { + const auto& tx = *p2.second; + m_is_manager.RemoveConflictedTx(tx); + } + + LogPrintf("NetInstantSend::%s -- invalidating block %s\n", __func__, pindex->GetBlockHash().ToString()); + + BlockValidationState state; + // need non-const pointer + auto pindex2 = WITH_LOCK(::cs_main, return m_chainstate.m_blockman.LookupBlockIndex(pindex->GetBlockHash())); + if (!m_chainstate.InvalidateBlock(state, pindex2)) { + LogPrintf("NetInstantSend::%s -- InvalidateBlock failed: %s\n", __func__, state.ToString()); + // This should not have happened and we are in a state were it's not safe to continue anymore + assert(false); + } + if (isLockedTxKnown) { + activateBestChain = true; + } else { + LogPrintf("NetInstantSend::%s -- resetting block %s\n", __func__, pindex2->GetBlockHash().ToString()); + LOCK(::cs_main); + m_chainstate.ResetBlockFailureFlags(pindex2); + } + } + + if (activateBestChain) { + BlockValidationState state; + if (!m_chainstate.ActivateBestChain(state)) { + LogPrintf("NetInstantSend::%s -- ActivateBestChain failed: %s\n", __func__, state.ToString()); + // This should not have happened and we are in a state were it's not safe to continue anymore + assert(false); + } + } +} diff --git a/src/instantsend/net_instantsend.h b/src/instantsend/net_instantsend.h index b02f0d1d965e..b9c2bfd8f689 100644 --- a/src/instantsend/net_instantsend.h +++ b/src/instantsend/net_instantsend.h @@ -85,6 +85,9 @@ class NetInstantSend final : public NetHandler, public CValidationInterface Uint256HashSet ProcessPendingInstantSendLocks( const Consensus::LLMQParams& llmq_params, int signOffset, bool ban, const std::vector& pend); + + void ResolveBlockConflicts(const uint256& islockHash, const instantsend::InstantSendLock& islock); + llmq::CInstantSendManager& m_is_manager; llmq::CQuorumManager& m_qman; CChainState& m_chainstate; From f3710b8f85b64e5127ca494e87f820d9f83b1218 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 12 Feb 2026 14:28:58 +0700 Subject: [PATCH 07/15] refactor: use only cached height of tip, don't retrieve it directly ever --- src/instantsend/instantsend.cpp | 17 +++++++---------- src/instantsend/net_instantsend.cpp | 6 +++++- src/instantsend/net_instantsend.h | 2 ++ 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index 714825ff4b9f..cc7fd69f3576 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -562,17 +562,14 @@ void CInstantSendManager::CacheTipHeight(const CBlockIndex* const tip) const int CInstantSendManager::GetTipHeight() const { - { - LOCK(cs_height_cache); - if (m_cached_tip_height >= 0) { - return m_cached_tip_height; - } + // It returns the cached tip height which is updated through notification mechanism + // If cached tip is not set by any reason, it's okay to return 0 because + // chainstate is not fully loaded yet and tip is not set + 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; + return 0; } bool CInstantSendManager::IsInstantSendEnabled() const diff --git a/src/instantsend/net_instantsend.cpp b/src/instantsend/net_instantsend.cpp index 62c031eeb110..7b5db51270d7 100644 --- a/src/instantsend/net_instantsend.cpp +++ b/src/instantsend/net_instantsend.cpp @@ -445,10 +445,14 @@ void NetInstantSend::RemoveMempoolConflictsForLock(const uint256& hash, const in } } -void NetInstantSend::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload) +void NetInstantSend::SynchronousUpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, + bool fInitialDownload) { m_is_manager.CacheTipHeight(pindexNew); +} +void NetInstantSend::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload) +{ bool fDIP0008Active = pindexNew->pprev && pindexNew->pprev->nHeight >= Params().GetConsensus().DIP0008Height; if (m_is_manager.Chainlocks().IsEnabled() && fDIP0008Active) { diff --git a/src/instantsend/net_instantsend.h b/src/instantsend/net_instantsend.h index b9c2bfd8f689..b23334a68bfb 100644 --- a/src/instantsend/net_instantsend.h +++ b/src/instantsend/net_instantsend.h @@ -55,6 +55,8 @@ class NetInstantSend final : public NetHandler, public CValidationInterface protected: // -- CValidationInterface + void SynchronousUpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, + bool fInitialDownload) override; void UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload) override; void TransactionAddedToMempool(const CTransactionRef&, int64_t, uint64_t mempool_sequence) override; void TransactionRemovedFromMempool(const CTransactionRef& ptx, MemPoolRemovalReason reason, From b069dabd780d52dc9f4a05dd12cb7f53bada0a48 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 12 Feb 2026 15:01:04 +0700 Subject: [PATCH 08/15] refactor: remove dependency of CInstantSendManager on chainstate --- src/instantsend/instantsend.cpp | 38 +++++++------------------ src/instantsend/instantsend.h | 13 +++------ src/instantsend/net_instantsend.cpp | 23 +++++++++++++-- src/instantsend/signing.cpp | 28 +++++++++++------- src/instantsend/signing.h | 3 +- src/llmq/context.cpp | 3 +- test/lint/lint-circular-dependencies.py | 2 +- 7 files changed, 56 insertions(+), 54 deletions(-) diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index cc7fd69f3576..536e1abf3500 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -12,7 +12,6 @@ #include #include #include -#include using node::fImporting; using node::fReindex; @@ -39,12 +38,11 @@ Uint256HashSet GetIdsFromLockable(const std::vector& vec) } } // anonymous namespace -CInstantSendManager::CInstantSendManager(const chainlock::Chainlocks& chainlocks, CChainState& chainstate, - CSigningManager& _sigman, CSporkManager& sporkman, - const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params) : +CInstantSendManager::CInstantSendManager(const chainlock::Chainlocks& chainlocks, CSigningManager& _sigman, + CSporkManager& sporkman, const CMasternodeSync& mn_sync, + const util::DbWrapperParams& db_params) : db{db_params}, m_chainlocks{chainlocks}, - m_chainstate{chainstate}, sigman{_sigman}, spork_manager{sporkman}, m_mn_sync{mn_sync} @@ -511,16 +509,10 @@ CInstantSendManager::Counts CInstantSendManager::GetCounts() const return ret; } -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); + m_cached_block_heights.insert(block_index->GetBlockHash(), block_index->nHeight); } void CInstantSendManager::CacheDisconnectBlock(const CBlockIndex* pindexDisconnected) @@ -529,31 +521,21 @@ void CInstantSendManager::CacheDisconnectBlock(const CBlockIndex* pindexDisconne m_cached_block_heights.erase(pindexDisconnected->GetBlockHash()); } -std::optional CInstantSendManager::GetBlockHeight(const uint256& hash) const +std::optional CInstantSendManager::GetCachedHeight(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; - } + LOCK(cs_height_cache); - const CBlockIndex* pindex = WITH_LOCK(::cs_main, return m_chainstate.m_blockman.LookupBlockIndex(hash)); - if (pindex == nullptr) { - return std::nullopt; - } + int cached_height{0}; + if (m_cached_block_heights.get(hash, cached_height)) return cached_height; - CacheBlockHeight(pindex); - return pindex->nHeight; + return std::nullopt; } void CInstantSendManager::CacheTipHeight(const CBlockIndex* const tip) const { LOCK(cs_height_cache); if (tip) { - CacheBlockHeightInternal(tip); + m_cached_block_heights.insert(tip->GetBlockHash(), tip->nHeight); m_cached_tip_height = tip->nHeight; } else { m_cached_tip_height = -1; diff --git a/src/instantsend/instantsend.h b/src/instantsend/instantsend.h index ddb8e2e1a482..ef380f425616 100644 --- a/src/instantsend/instantsend.h +++ b/src/instantsend/instantsend.h @@ -24,7 +24,6 @@ #include class CBlockIndex; -class CChainState; class CDataStream; class CMasternodeSync; class CSporkManager; @@ -66,7 +65,6 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent instantsend::CInstantSendDb db; const chainlock::Chainlocks& m_chainlocks; - CChainState& m_chainstate; CSigningManager& sigman; CSporkManager& spork_manager; const CMasternodeSync& m_mn_sync; @@ -103,15 +101,12 @@ 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 CBlockIndex* const block_index) const EXCLUSIVE_LOCKS_REQUIRED(cs_height_cache); - public: CInstantSendManager() = delete; CInstantSendManager(const CInstantSendManager&) = delete; CInstantSendManager& operator=(const CInstantSendManager&) = delete; - explicit CInstantSendManager(const chainlock::Chainlocks& chainlocks, CChainState& chainstate, - CSigningManager& _sigman, CSporkManager& sporkman, const CMasternodeSync& mn_sync, - const util::DbWrapperParams& db_params); + explicit CInstantSendManager(const chainlock::Chainlocks& chainlocks, CSigningManager& _sigman, CSporkManager& sporkman, + const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params); ~CInstantSendManager(); void ConnectSigner(gsl::not_null signer) @@ -187,9 +182,9 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent }; Counts GetCounts() const EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks, !cs_nonLocked); - void CacheBlockHeight(const CBlockIndex* const block_index) const EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); + void CacheBlockHeight(const CBlockIndex* const block_index) const override EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); void CacheDisconnectBlock(const CBlockIndex* pindexDisconnected) EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); - std::optional GetBlockHeight(const uint256& hash) const override EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); + std::optional GetCachedHeight(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); diff --git a/src/instantsend/net_instantsend.cpp b/src/instantsend/net_instantsend.cpp index 7b5db51270d7..a29c9f03e99d 100644 --- a/src/instantsend/net_instantsend.cpp +++ b/src/instantsend/net_instantsend.cpp @@ -36,6 +36,23 @@ constexpr int OLD_ACTIVE_SET_FAILURE_MISBEHAVIOR_SCORE{20}; constexpr auto WORK_THREAD_SLEEP_INTERVAL{std::chrono::milliseconds{100}}; } // namespace +static std::optional GetBlockHeight(llmq::CInstantSendManager& is_manager, const CChainState& chainstate, + const uint256& hash) +{ + if (hash.IsNull()) { + return std::nullopt; + } + auto ret = is_manager.GetCachedHeight(hash); + if (ret) return ret; + + const CBlockIndex* pindex = WITH_LOCK(::cs_main, return chainstate.m_blockman.LookupBlockIndex(hash)); + if (pindex == nullptr) { + return std::nullopt; + } + is_manager.CacheBlockHeight(pindex); + return pindex->nHeight; +} + struct NetInstantSend::BatchVerificationData { CBLSBatchVerifier batchVerifier{false, true, BATCH_VERIFIER_SOURCE_THRESHOLD}; Uint256HashMap recSigs; @@ -55,7 +72,7 @@ bool NetInstantSend::ValidateIncomingISLock(const instantsend::InstantSendLock& std::optional NetInstantSend::ResolveCycleHeight(const uint256& cycle_hash) { - auto cycle_height = m_is_manager.GetBlockHeight(cycle_hash); + auto cycle_height = GetBlockHeight(m_is_manager, m_chainstate, cycle_hash); if (cycle_height) { return cycle_height; } @@ -113,7 +130,7 @@ std::unique_ptr NetInstantSend::BuildVeri continue; } - auto cycleHeightOpt = m_is_manager.GetBlockHeight(islock->cycleHash); + auto cycleHeightOpt = GetBlockHeight(m_is_manager, m_chainstate, islock->cycleHash); if (!cycleHeightOpt) { data->batchVerifier.badSources.emplace(nodeId); continue; @@ -325,7 +342,7 @@ void NetInstantSend::ProcessInstantSendLock(NodeId from, const uint256& hash, co auto tx = GetTransaction(nullptr, &m_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 - std::optional minedHeight = m_is_manager.GetBlockHeight(hashBlock); + std::optional minedHeight = GetBlockHeight(m_is_manager, m_chainstate, hashBlock); if (found_transaction) { if (!minedHeight.has_value()) { const CBlockIndex* pindexMined = WITH_LOCK(::cs_main, diff --git a/src/instantsend/signing.cpp b/src/instantsend/signing.cpp index 40dcc58d7589..30ea5f6ab59b 100644 --- a/src/instantsend/signing.cpp +++ b/src/instantsend/signing.cpp @@ -204,28 +204,36 @@ bool InstantSendSigner::CheckCanLock(const COutPoint& outpoint, bool printDebug, return false; } - 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()); + int blockHeight{0}; + if (!hashBlock.IsNull()) { + auto ret = m_isman.GetCachedHeight(hashBlock); + if (ret) blockHeight = *ret; + + const CBlockIndex* pindex = WITH_LOCK(::cs_main, return m_chainstate.m_blockman.LookupBlockIndex(hashBlock)); + if (pindex == nullptr) { + 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; } - return false; + m_isman.CacheBlockHeight(pindex); + blockHeight = pindex->nHeight; } const int tipHeight = m_isman.GetTipHeight(); - if (tipHeight < *blockHeight) { + 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()); + __func__, txHash.ToString(), tipHeight, blockHeight, outpoint.hash.ToString()); } return false; } - const int nTxAge = tipHeight - *blockHeight + 1; + const int nTxAge = tipHeight - blockHeight + 1; - if (nTxAge < nInstantSendConfirmationsRequired && !m_chainlocks.HasChainLock(*blockHeight, hashBlock)) { + if (nTxAge < nInstantSendConfirmationsRequired && !m_chainlocks.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 18860a22ffbd..4e3fc4437001 100644 --- a/src/instantsend/signing.h +++ b/src/instantsend/signing.h @@ -38,8 +38,9 @@ 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 std::optional GetCachedHeight(const uint256& hash) const = 0; virtual int GetTipHeight() const = 0; + virtual void CacheBlockHeight(const CBlockIndex* const block_index) const = 0; }; class InstantSendSigner final : public llmq::CRecoveredSigsListener diff --git a/src/llmq/context.cpp b/src/llmq/context.cpp index 112af2780a1d..fa3e876972d1 100644 --- a/src/llmq/context.cpp +++ b/src/llmq/context.cpp @@ -22,8 +22,7 @@ LLMQContext::LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSpork qman{std::make_unique(*bls_worker, dmnman, evo_db, *quorum_block_processor, *qsnapman, chainman, db_params)}, sigman{std::make_unique(*qman, db_params, max_recsigs_age)}, - isman{std::make_unique(chainlocks, chainman.ActiveChainstate(), *sigman, sporkman, - mn_sync, db_params)} + isman{std::make_unique(chainlocks, *sigman, sporkman, mn_sync, db_params)} { // Have to start it early to let VerifyDB check ChainLock signatures in coinbase bls_worker->Start(); diff --git a/test/lint/lint-circular-dependencies.py b/test/lint/lint-circular-dependencies.py index 289c81da1b29..93df62fb1a91 100755 --- a/test/lint/lint-circular-dependencies.py +++ b/test/lint/lint-circular-dependencies.py @@ -42,7 +42,7 @@ "evo/specialtxman -> validation -> evo/specialtxman", "governance/classes -> governance/object -> governance/governance -> governance/classes", "governance/governance -> governance/signing -> governance/object -> governance/governance", - "instantsend/instantsend -> validation -> txmempool -> instantsend/instantsend", + "instantsend/instantsend -> instantsend/signing -> validation -> txmempool -> instantsend/instantsend", "llmq/blockprocessor -> llmq/utils -> llmq/snapshot -> llmq/blockprocessor", "llmq/commitment -> llmq/utils -> llmq/snapshot -> llmq/commitment", "llmq/dkgsessionhandler -> net_processing -> llmq/dkgsessionmgr -> llmq/dkgsessionhandler", From f8c9a2ccb518eedcb5a8136d8624b95efea04595 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 12 Feb 2026 21:58:11 +0700 Subject: [PATCH 09/15] refactor: remove dependency of CInstantSendManager on signer --- src/active/context.cpp | 3 - src/active/context.h | 3 +- src/init.cpp | 2 +- src/instantsend/instantsend.cpp | 113 ++++++++-------------------- src/instantsend/instantsend.h | 26 +------ src/instantsend/net_instantsend.cpp | 102 ++++++++++++++++++++----- src/instantsend/net_instantsend.h | 13 +++- 7 files changed, 132 insertions(+), 130 deletions(-) diff --git a/src/active/context.cpp b/src/active/context.cpp index dcc00b2f7ca2..c40e62958d69 100644 --- a/src/active/context.cpp +++ b/src/active/context.cpp @@ -32,7 +32,6 @@ ActiveContext::ActiveContext(CBLSWorker& bls_worker, ChainstateManager& chainman const CMasternodeSync& mn_sync, const CBLSSecretKey& operator_sk, const llmq::QvvecSyncModeMap& sync_map, const util::DbWrapperParams& db_params, bool quorums_recovery, bool quorums_watch) : - m_isman{isman}, m_qman{qman}, nodeman{std::make_unique(connman, dmnman, operator_sk)}, dkgdbgman{std::make_unique()}, @@ -53,14 +52,12 @@ ActiveContext::ActiveContext(CBLSWorker& bls_worker, ChainstateManager& chainman qblockman, qsnapman, *nodeman, chainman, sporkman, llmq_params, quorums_watch, quorum_idx); }); - m_isman.ConnectSigner(is_signer.get()); m_qman.ConnectManagers(qman_handler.get(), qdkgsman.get()); } ActiveContext::~ActiveContext() { m_qman.DisconnectManagers(); - m_isman.DisconnectSigner(); } void ActiveContext::Start(CConnman& connman, PeerManager& peerman) diff --git a/src/active/context.h b/src/active/context.h index e6b90f4b3532..9a2951ac80dd 100644 --- a/src/active/context.h +++ b/src/active/context.h @@ -54,7 +54,6 @@ struct DbWrapperParams; struct ActiveContext final : public CValidationInterface { private: - llmq::CInstantSendManager& m_isman; llmq::CQuorumManager& m_qman; public: @@ -97,6 +96,8 @@ struct ActiveContext final : public CValidationInterface { const std::unique_ptr ehf_sighandler; const std::unique_ptr qman_handler; const std::unique_ptr cl_signer; + +public: const std::unique_ptr is_signer; /** Owned by PeerManager, use GetCJServer() */ diff --git a/src/init.cpp b/src/init.cpp index fd70cda79cb9..15c732a1e98e 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2207,7 +2207,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // ********************************************************* Step 7d: Setup other Dash services - node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.llmq_ctx->isman, *node.llmq_ctx->qman, chainman.ActiveChainstate(), *node.mempool, *node.mn_sync)); + node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.llmq_ctx->isman, node.active_ctx ? node.active_ctx->is_signer.get() : nullptr, *node.llmq_ctx->qman, chainman.ActiveChainstate(), *node.mempool, *node.mn_sync)); node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.llmq_ctx->sigman, node.active_ctx ? node.active_ctx->shareman.get() : nullptr, *node.sporkman)); if (node.active_ctx) { diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index 536e1abf3500..775615e7d0c5 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -17,27 +17,6 @@ using node::fImporting; using node::fReindex; namespace llmq { -namespace { -template - requires std::same_as || std::same_as -Uint256HashSet GetIdsFromLockable(const std::vector& vec) -{ - Uint256HashSet ret{}; - if (vec.empty()) return ret; - ret.reserve(vec.size()); - for (const auto& in : vec) { - if constexpr (std::is_same_v) { - ret.emplace(instantsend::GenInputLockRequestId(in)); - } else if constexpr (std::is_same_v) { - ret.emplace(instantsend::GenInputLockRequestId(in.prevout)); - } else { - assert(false); - } - } - return ret; -} -} // anonymous namespace - CInstantSendManager::CInstantSendManager(const chainlock::Chainlocks& chainlocks, CSigningManager& _sigman, CSporkManager& sporkman, const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params) : @@ -274,25 +253,6 @@ std::vector CInstantSendManager::PrepareTxToRetry() return txns; } -void CInstantSendManager::RemoveConflictedTx(const CTransaction& tx) -{ - RemoveNonLockedTx(tx.GetHash(), false); - if (auto signer = m_signer.load(std::memory_order_acquire); signer) { - signer->ClearInputsFromQueue(GetIdsFromLockable(tx.vin)); - } -} - -void CInstantSendManager::TruncateRecoveredSigsForInputs(const instantsend::InstantSendLock& islock) -{ - auto ids = GetIdsFromLockable(islock.inputs); - if (auto signer = m_signer.load(std::memory_order_acquire); signer) { - signer->ClearInputsFromQueue(ids); - } - for (const auto& id : ids) { - sigman.TruncateRecoveredSig(Params().GetConsensus().llmqTypeDIP0024InstantSend, id); - } -} - void CInstantSendManager::TryEmplacePendingLock(const uint256& hash, const NodeId id, const instantsend::InstantSendLockPtr& islock) { @@ -303,48 +263,6 @@ void CInstantSendManager::TryEmplacePendingLock(const uint256& hash, const NodeI } } -void CInstantSendManager::HandleFullyConfirmedBlock(const CBlockIndex* pindex) -{ - if (!IsInstantSendEnabled()) { - return; - } - - auto removeISLocks = db.RemoveConfirmedInstantSendLocks(pindex->nHeight); - - for (const auto& [islockHash, islock] : removeISLocks) { - LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- txid=%s, islock=%s: removed islock as it got fully confirmed\n", __func__, - islock->txid.ToString(), islockHash.ToString()); - - // No need to keep recovered sigs for fully confirmed IS locks, as there is no chance for conflicts - // from now on. All inputs are spent now and can't be spend in any other TX. - TruncateRecoveredSigsForInputs(*islock); - - // And we don't need the recovered sig for the ISLOCK anymore, as the block in which it got mined is considered - // fully confirmed now - sigman.TruncateRecoveredSig(Params().GetConsensus().llmqTypeDIP0024InstantSend, islock->GetRequestId()); - } - - db.RemoveArchivedInstantSendLocks(pindex->nHeight - 100); - - // Find all previously unlocked TXs that got locked by this fully confirmed (ChainLock) block and remove them - // from the nonLockedTxs map. Also collect all children of these TXs and mark them for retrying of IS locking. - std::vector toRemove; - { - LOCK(cs_nonLocked); - for (const auto& p : nonLockedTxs) { - const auto* pindexMined = p.second.pindexMined; - - if (pindexMined && pindex->GetAncestor(pindexMined->nHeight) == pindexMined) { - toRemove.emplace_back(p.first); - } - } - } - for (const auto& txid : toRemove) { - // This will also add children to pendingRetryTxs - RemoveNonLockedTx(txid, true); - } -} - std::unordered_map> CInstantSendManager::RetrieveISConflicts( const uint256& islockHash, const instantsend::InstantSendLock& islock) { @@ -571,4 +489,35 @@ bool CInstantSendManager::RejectConflictingBlocks() const return true; } +void CInstantSendManager::RemoveArchivedInstantSendLocks(int nUntilHeight) +{ + db.RemoveArchivedInstantSendLocks(nUntilHeight); +} + +Uint256HashMap CInstantSendManager::RemoveConfirmedInstantSendLocks(int nUntilHeight) +{ + return db.RemoveConfirmedInstantSendLocks(nUntilHeight); +} + +void CInstantSendManager::RemoveNonLockedForConfirmed(const CBlockIndex* pindex) +{ + // Find all previously unlocked TXs that got locked by this fully confirmed (ChainLock) block and remove them + // from the nonLockedTxs map. Also collect all children of these TXs and mark them for retrying of IS locking. + std::vector toRemove; + { + LOCK(cs_nonLocked); + for (const auto& p : nonLockedTxs) { + const auto* pindexMined = p.second.pindexMined; + + if (pindexMined && pindex->GetAncestor(pindexMined->nHeight) == pindexMined) { + toRemove.emplace_back(p.first); + } + } + } + for (const auto& txid : toRemove) { + // This will also add children to pendingRetryTxs + RemoveNonLockedTx(txid, true); + } +} + } // namespace llmq diff --git a/src/instantsend/instantsend.h b/src/instantsend/instantsend.h index ef380f425616..0bc342eaf079 100644 --- a/src/instantsend/instantsend.h +++ b/src/instantsend/instantsend.h @@ -16,7 +16,6 @@ #include #include -#include #include #include #include @@ -39,7 +38,6 @@ class Chainlocks; } namespace instantsend { -class InstantSendSigner; struct PendingISLockFromPeer { NodeId node_id; @@ -69,8 +67,6 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent CSporkManager& spork_manager; const CMasternodeSync& m_mn_sync; - std::atomic m_signer{nullptr}; - mutable Mutex cs_pendingLocks; // Incoming and not verified yet Uint256HashMap pendingInstantSendLocks GUARDED_BY(cs_pendingLocks); @@ -109,16 +105,6 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params); ~CInstantSendManager(); - void ConnectSigner(gsl::not_null signer) - { - // Prohibit double initialization - assert(m_signer.load(std::memory_order_acquire) == nullptr); - m_signer.store(signer, std::memory_order_release); - } - void DisconnectSigner() { m_signer.store(nullptr, std::memory_order_release); } - - instantsend::InstantSendSigner* Signer() const { return m_signer.load(std::memory_order_acquire); } - void AddNonLockedTx(const CTransactionRef& tx, const CBlockIndex* pindexMined) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks, !cs_timingsTxSeen); void RemoveNonLockedTx(const uint256& txid, bool retryChildren) @@ -126,19 +112,11 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent instantsend::InstantSendLockPtr AttachISLockToTx(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); - void RemoveConflictedTx(const CTransaction& tx) - EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); - - void TruncateRecoveredSigsForInputs(const instantsend::InstantSendLock& islock); std::unordered_map> RetrieveISConflicts( const uint256& islockHash, const instantsend::InstantSendLock& islock) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks); bool IsKnownTx(const uint256& islockHash) const EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); -private: - void HandleFullyConfirmedBlock(const CBlockIndex* pindex) - EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); - bool IsLocked(const uint256& txHash) const override; bool IsWaitingForTx(const uint256& txHash) const EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); instantsend::InstantSendLockPtr GetConflictingLock(const CTransaction& tx) const override; @@ -195,6 +173,10 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent * allows ChainLocks to continue even while this spork is disabled. */ bool RejectConflictingBlocks() const; + void RemoveArchivedInstantSendLocks(int nUntilHeight); + Uint256HashMap RemoveConfirmedInstantSendLocks(int nUntilHeight); + EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); + void RemoveNonLockedForConfirmed(const CBlockIndex* pindex); }; } // namespace llmq diff --git a/src/instantsend/net_instantsend.cpp b/src/instantsend/net_instantsend.cpp index a29c9f03e99d..c7e4fd98bd4f 100644 --- a/src/instantsend/net_instantsend.cpp +++ b/src/instantsend/net_instantsend.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -218,6 +219,27 @@ Uint256HashSet NetInstantSend::ApplyVerificationResults( return badISLocks; } +namespace { +template + requires std::same_as || std::same_as +Uint256HashSet GetIdsFromLockable(const std::vector& vec) +{ + Uint256HashSet ret{}; + if (vec.empty()) return ret; + ret.reserve(vec.size()); + for (const auto& in : vec) { + if constexpr (std::is_same_v) { + ret.emplace(instantsend::GenInputLockRequestId(in)); + } else if constexpr (std::is_same_v) { + ret.emplace(instantsend::GenInputLockRequestId(in.prevout)); + } else { + assert(false); + } + } + return ret; +} +} // anonymous namespace + void NetInstantSend::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) { if (msg_type != NetMsgType::ISDLOCK) { @@ -333,8 +355,8 @@ void NetInstantSend::ProcessInstantSendLock(NodeId from, const uint256& hash, co LogPrint(BCLog::INSTANTSEND, "NetSigning::%s -- txid=%s, islock=%s: processing islock, peer=%d\n", __func__, islock->txid.ToString(), hash.ToString(), from); - if (auto signer = m_is_manager.Signer(); signer) { - signer->ClearLockFromQueue(islock); + if (m_signer) { + m_signer->ClearLockFromQueue(islock); } if (!m_is_manager.PreVerifyIsLock(hash, islock, from)) return; @@ -371,7 +393,7 @@ void NetInstantSend::ProcessInstantSendLock(NodeId from, const uint256& hash, co m_is_manager.RemoveNonLockedTx(islock->txid, true); // We don't need the recovered sigs for the inputs anymore. This prevents unnecessary propagation of these sigs. // We only need the ISLOCK from now on to detect conflicts - m_is_manager.TruncateRecoveredSigsForInputs(*islock); + TruncateRecoveredSigsForInputs(*islock); ResolveBlockConflicts(hash, *islock); if (found_transaction) { @@ -402,8 +424,8 @@ void NetInstantSend::WorkThreadMain() if (!locks.empty()) { ProcessPendingISLocks(std::move(locks)); } - if (auto signer = m_is_manager.Signer(); signer) { - signer->ProcessPendingRetryLockTxs(m_is_manager.PrepareTxToRetry()); + if (m_signer) { + m_signer->ProcessPendingRetryLockTxs(m_is_manager.PrepareTxToRetry()); } return more_work; }(); @@ -422,8 +444,8 @@ void NetInstantSend::TransactionAddedToMempool(const CTransactionRef& tx, int64_ instantsend::InstantSendLockPtr islock = m_is_manager.AttachISLockToTx(tx); if (islock == nullptr) { - if (auto signer = m_is_manager.Signer(); signer) { - signer->ProcessTx(*tx, false, Params().GetConsensus()); + if (m_signer) { + m_signer->ProcessTx(*tx, false, Params().GetConsensus()); } // TX is not locked, so make sure it is tracked m_is_manager.AddNonLockedTx(tx, nullptr); @@ -432,6 +454,16 @@ void NetInstantSend::TransactionAddedToMempool(const CTransactionRef& tx, int64_ } } +void NetInstantSend::ClearConflicting(const Uint256HashMap& to_delete) +{ + for (const auto& [_, tx] : to_delete) { + m_is_manager.RemoveNonLockedTx(tx->GetHash(), false); + if (m_signer) { + m_signer->ClearInputsFromQueue(GetIdsFromLockable(tx->vin)); + } + } +} + void NetInstantSend::RemoveMempoolConflictsForLock(const uint256& hash, const instantsend::InstantSendLock& islock) { Uint256HashMap toDelete; @@ -456,10 +488,7 @@ void NetInstantSend::RemoveMempoolConflictsForLock(const uint256& hash, const in m_mempool.removeRecursive(*p.second, MemPoolRemovalReason::CONFLICT); } } - - for (const auto& p : toDelete) { - m_is_manager.RemoveConflictedTx(*p.second); - } + ClearConflicting(toDelete); } void NetInstantSend::SynchronousUpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, @@ -481,7 +510,7 @@ void NetInstantSend::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockI const CBlockIndex* pindex = pindexNew->GetAncestor(nConfirmedHeight); if (pindex) { - m_is_manager.HandleFullyConfirmedBlock(pindex); + HandleFullyConfirmedBlock(pindex); } } @@ -508,8 +537,8 @@ void NetInstantSend::BlockConnected(const std::shared_ptr& pblock, } if (!m_is_manager.IsLocked(tx->GetHash()) && !has_chainlock) { - if (auto signer = m_is_manager.Signer(); signer) { - signer->ProcessTx(*tx, true, Params().GetConsensus()); + if (m_signer) { + m_signer->ProcessTx(*tx, true, Params().GetConsensus()); } // TX is not locked, so make sure it is tracked m_is_manager.AddNonLockedTx(tx, pindex); @@ -531,7 +560,7 @@ void NetInstantSend::BlockDisconnected(const std::shared_ptr& pblo void NetInstantSend::NotifyChainLock(const CBlockIndex* pindex, const std::shared_ptr& clsig) { - m_is_manager.HandleFullyConfirmedBlock(pindex); + HandleFullyConfirmedBlock(pindex); } void NetInstantSend::ResolveBlockConflicts(const uint256& islockHash, const instantsend::InstantSendLock& islock) @@ -564,10 +593,7 @@ void NetInstantSend::ResolveBlockConflicts(const uint256& islockHash, const inst bool activateBestChain = false; for (const auto& p : conflicts) { const auto* pindex = p.first; - for (const auto& p2 : p.second) { - const auto& tx = *p2.second; - m_is_manager.RemoveConflictedTx(tx); - } + ClearConflicting(p.second); LogPrintf("NetInstantSend::%s -- invalidating block %s\n", __func__, pindex->GetBlockHash().ToString()); @@ -597,3 +623,41 @@ void NetInstantSend::ResolveBlockConflicts(const uint256& islockHash, const inst } } } + +void NetInstantSend::TruncateRecoveredSigsForInputs(const instantsend::InstantSendLock& islock) +{ + auto ids = GetIdsFromLockable(islock.inputs); + if (m_signer) { + m_signer->ClearInputsFromQueue(ids); + } + for (const auto& id : ids) { + m_is_manager.Sigman().TruncateRecoveredSig(Params().GetConsensus().llmqTypeDIP0024InstantSend, id); + } +} + +void NetInstantSend::HandleFullyConfirmedBlock(const CBlockIndex* pindex) +{ + if (!m_is_manager.IsInstantSendEnabled()) { + return; + } + + auto removeISLocks = m_is_manager.RemoveConfirmedInstantSendLocks(pindex->nHeight); + + for (const auto& [islockHash, islock] : removeISLocks) { + LogPrint(BCLog::INSTANTSEND, "NetInstantSend::%s -- txid=%s, islock=%s: removed islock as it got fully confirmed\n", + __func__, islock->txid.ToString(), islockHash.ToString()); + + // No need to keep recovered sigs for fully confirmed IS locks, as there is no chance for conflicts + // from now on. All inputs are spent now and can't be spend in any other TX. + TruncateRecoveredSigsForInputs(*islock); + + // And we don't need the recovered sig for the ISLOCK anymore, as the block in which it got mined is considered + // fully confirmed now + m_is_manager.Sigman().TruncateRecoveredSig(Params().GetConsensus().llmqTypeDIP0024InstantSend, + islock->GetRequestId()); + } + + m_is_manager.RemoveArchivedInstantSendLocks(pindex->nHeight - 100); + + m_is_manager.RemoveNonLockedForConfirmed(pindex); +} diff --git a/src/instantsend/net_instantsend.h b/src/instantsend/net_instantsend.h index b23334a68bfb..338ca7f12ec4 100644 --- a/src/instantsend/net_instantsend.h +++ b/src/instantsend/net_instantsend.h @@ -25,6 +25,7 @@ namespace instantsend { struct InstantSendLock; using InstantSendLockPtr = std::shared_ptr; struct PendingISLockEntry; +class InstantSendSigner; } // namespace instantsend namespace llmq { class CInstantSendManager; @@ -34,10 +35,12 @@ class CQuorumManager; class NetInstantSend final : public NetHandler, public CValidationInterface { public: - NetInstantSend(PeerManagerInternal* peer_manager, llmq::CInstantSendManager& is_manager, llmq::CQuorumManager& qman, - CChainState& chainstate, CTxMemPool& mempool, const CMasternodeSync& mn_sync) : + NetInstantSend(PeerManagerInternal* peer_manager, llmq::CInstantSendManager& is_manager, + instantsend::InstantSendSigner* signer, llmq::CQuorumManager& qman, CChainState& chainstate, + CTxMemPool& mempool, const CMasternodeSync& mn_sync) : NetHandler(peer_manager), m_is_manager{is_manager}, + m_signer{signer}, m_qman(qman), m_chainstate{chainstate}, m_mempool{mempool}, @@ -90,7 +93,13 @@ class NetInstantSend final : public NetHandler, public CValidationInterface void ResolveBlockConflicts(const uint256& islockHash, const instantsend::InstantSendLock& islock); + void TruncateRecoveredSigsForInputs(const instantsend::InstantSendLock& islock); + + void HandleFullyConfirmedBlock(const CBlockIndex* pindex); + void ClearConflicting(const Uint256HashMap& to_delete); + llmq::CInstantSendManager& m_is_manager; + instantsend::InstantSendSigner* m_signer; // non-null only for masternode llmq::CQuorumManager& m_qman; CChainState& m_chainstate; CTxMemPool& m_mempool; From 90635d820b60265c9d857362899a3f427790f430 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 12 Feb 2026 22:07:37 +0700 Subject: [PATCH 10/15] refactor: re-order clean-up calls for confirmed block for instant-send handling --- src/instantsend/instantsend.cpp | 25 +++++++++++++++---------- src/instantsend/instantsend.h | 4 +--- src/instantsend/net_instantsend.cpp | 17 ++--------------- 3 files changed, 18 insertions(+), 28 deletions(-) diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index 775615e7d0c5..526c5361c58e 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -489,18 +489,22 @@ bool CInstantSendManager::RejectConflictingBlocks() const return true; } -void CInstantSendManager::RemoveArchivedInstantSendLocks(int nUntilHeight) +Uint256HashMap CInstantSendManager::RemoveConfirmedInstantSendLocks(const CBlockIndex* pindex) { - db.RemoveArchivedInstantSendLocks(nUntilHeight); -} + int nUntilHeight = pindex->nHeight; + auto removeISLocks = db.RemoveConfirmedInstantSendLocks(nUntilHeight); -Uint256HashMap CInstantSendManager::RemoveConfirmedInstantSendLocks(int nUntilHeight) -{ - return db.RemoveConfirmedInstantSendLocks(nUntilHeight); -} + for (const auto& [islockHash, islock] : removeISLocks) { + LogPrint(BCLog::INSTANTSEND, "NetInstantSend::%s -- txid=%s, islock=%s: removed islock as it got fully confirmed\n", + __func__, islock->txid.ToString(), islockHash.ToString()); + + // And we don't need the recovered sig for the ISLOCK anymore, as the block in which it got mined is considered + // fully confirmed now + sigman.TruncateRecoveredSig(Params().GetConsensus().llmqTypeDIP0024InstantSend, islock->GetRequestId()); + } + + db.RemoveArchivedInstantSendLocks(nUntilHeight - 100); -void CInstantSendManager::RemoveNonLockedForConfirmed(const CBlockIndex* pindex) -{ // Find all previously unlocked TXs that got locked by this fully confirmed (ChainLock) block and remove them // from the nonLockedTxs map. Also collect all children of these TXs and mark them for retrying of IS locking. std::vector toRemove; @@ -518,6 +522,7 @@ void CInstantSendManager::RemoveNonLockedForConfirmed(const CBlockIndex* pindex) // This will also add children to pendingRetryTxs RemoveNonLockedTx(txid, true); } -} + return removeISLocks; +} } // namespace llmq diff --git a/src/instantsend/instantsend.h b/src/instantsend/instantsend.h index 0bc342eaf079..383f1324d2fe 100644 --- a/src/instantsend/instantsend.h +++ b/src/instantsend/instantsend.h @@ -173,10 +173,8 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent * allows ChainLocks to continue even while this spork is disabled. */ bool RejectConflictingBlocks() const; - void RemoveArchivedInstantSendLocks(int nUntilHeight); - Uint256HashMap RemoveConfirmedInstantSendLocks(int nUntilHeight); + Uint256HashMap RemoveConfirmedInstantSendLocks(const CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); - void RemoveNonLockedForConfirmed(const CBlockIndex* pindex); }; } // namespace llmq diff --git a/src/instantsend/net_instantsend.cpp b/src/instantsend/net_instantsend.cpp index c7e4fd98bd4f..11fb7d874622 100644 --- a/src/instantsend/net_instantsend.cpp +++ b/src/instantsend/net_instantsend.cpp @@ -641,23 +641,10 @@ void NetInstantSend::HandleFullyConfirmedBlock(const CBlockIndex* pindex) return; } - auto removeISLocks = m_is_manager.RemoveConfirmedInstantSendLocks(pindex->nHeight); - - for (const auto& [islockHash, islock] : removeISLocks) { - LogPrint(BCLog::INSTANTSEND, "NetInstantSend::%s -- txid=%s, islock=%s: removed islock as it got fully confirmed\n", - __func__, islock->txid.ToString(), islockHash.ToString()); - + auto removeISLocks = m_is_manager.RemoveConfirmedInstantSendLocks(pindex); + for (const auto& [_, islock] : removeISLocks) { // No need to keep recovered sigs for fully confirmed IS locks, as there is no chance for conflicts // from now on. All inputs are spent now and can't be spend in any other TX. TruncateRecoveredSigsForInputs(*islock); - - // And we don't need the recovered sig for the ISLOCK anymore, as the block in which it got mined is considered - // fully confirmed now - m_is_manager.Sigman().TruncateRecoveredSig(Params().GetConsensus().llmqTypeDIP0024InstantSend, - islock->GetRequestId()); } - - m_is_manager.RemoveArchivedInstantSendLocks(pindex->nHeight - 100); - - m_is_manager.RemoveNonLockedForConfirmed(pindex); } From 21b24a8963ba98c3f8407315a93c12f200fb0fe2 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 12 Feb 2026 22:26:13 +0700 Subject: [PATCH 11/15] refactor: drop helper interface InstantSendSignerParent --- src/init.cpp | 1 + src/instantsend/instantsend.cpp | 1 - src/instantsend/instantsend.h | 17 ++++++++--------- src/instantsend/signing.cpp | 12 ++++++------ src/instantsend/signing.h | 18 +++--------------- test/lint/lint-circular-dependencies.py | 2 +- 6 files changed, 19 insertions(+), 32 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 15c732a1e98e..df9ec01c772a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -97,6 +97,7 @@ #include #include #include +#include #include #include #include diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index 526c5361c58e..15c2bcaf27af 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include diff --git a/src/instantsend/instantsend.h b/src/instantsend/instantsend.h index 383f1324d2fe..067f125585fe 100644 --- a/src/instantsend/instantsend.h +++ b/src/instantsend/instantsend.h @@ -7,7 +7,6 @@ #include #include -#include #include #include @@ -57,7 +56,7 @@ struct PendingState { namespace llmq { class CSigningManager; -class CInstantSendManager final : public instantsend::InstantSendSignerParent +class CInstantSendManager { private: instantsend::CInstantSendDb db; @@ -117,9 +116,9 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks); bool IsKnownTx(const uint256& islockHash) const EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); - bool IsLocked(const uint256& txHash) const override; + bool IsLocked(const uint256& txHash) const; bool IsWaitingForTx(const uint256& txHash) const EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); - instantsend::InstantSendLockPtr GetConflictingLock(const CTransaction& tx) const override; + instantsend::InstantSendLockPtr GetConflictingLock(const CTransaction& tx) const; /* Helpers for communications between CInstantSendManager & NetInstantSend */ // This helper returns up to 32 pending locks and remove them from queue of pending @@ -147,7 +146,7 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent void TransactionIsRemoved(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); 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 + void TryEmplacePendingLock(const uint256& hash, const NodeId id, const instantsend::InstantSendLockPtr& islock) EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); size_t GetInstantSendLockCount() const; @@ -160,13 +159,13 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent }; Counts GetCounts() const EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks, !cs_nonLocked); - void CacheBlockHeight(const CBlockIndex* const block_index) const override EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); + void CacheBlockHeight(const CBlockIndex* const block_index) const EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); void CacheDisconnectBlock(const CBlockIndex* pindexDisconnected) EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); - std::optional GetCachedHeight(const uint256& hash) const override EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); + std::optional GetCachedHeight(const uint256& hash) const 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); + int GetTipHeight() const EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); - bool IsInstantSendEnabled() const override; + bool IsInstantSendEnabled() const; /** * If true, MN should sign all transactions, if false, MN should not sign * transactions in mempool, but should sign txes included in a block. This diff --git a/src/instantsend/signing.cpp b/src/instantsend/signing.cpp index 30ea5f6ab59b..b1c946487c4e 100644 --- a/src/instantsend/signing.cpp +++ b/src/instantsend/signing.cpp @@ -5,17 +5,17 @@ #include #include +#include #include #include -#include -#include -#include - -#include +#include #include #include +#include #include #include +#include +#include // Forward declaration to break dependency over node/transaction.h namespace node { @@ -29,7 +29,7 @@ using node::GetTransaction; namespace instantsend { InstantSendSigner::InstantSendSigner(CChainState& chainstate, const chainlock::Chainlocks& chainlocks, - InstantSendSignerParent& isman, llmq::CSigningManager& sigman, + llmq::CInstantSendManager& isman, llmq::CSigningManager& sigman, llmq::CSigSharesManager& shareman, llmq::CQuorumManager& qman, CSporkManager& sporkman, CTxMemPool& mempool, const CMasternodeSync& mn_sync) : m_chainstate{chainstate}, diff --git a/src/instantsend/signing.h b/src/instantsend/signing.h index 4e3fc4437001..45e6174dcd9f 100644 --- a/src/instantsend/signing.h +++ b/src/instantsend/signing.h @@ -6,6 +6,7 @@ #define BITCOIN_INSTANTSEND_SIGNING_H #include +#include #include #include @@ -29,26 +30,13 @@ class CQuorumManager; } // namespace llmq namespace instantsend { -class InstantSendSignerParent -{ -public: - virtual ~InstantSendSignerParent() = default; - - virtual bool IsInstantSendEnabled() const = 0; - 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 GetCachedHeight(const uint256& hash) const = 0; - virtual int GetTipHeight() const = 0; - virtual void CacheBlockHeight(const CBlockIndex* const block_index) const = 0; -}; class InstantSendSigner final : public llmq::CRecoveredSigsListener { private: CChainState& m_chainstate; const chainlock::Chainlocks& m_chainlocks; - InstantSendSignerParent& m_isman; + llmq::CInstantSendManager& m_isman; llmq::CSigningManager& m_sigman; llmq::CSigSharesManager& m_shareman; llmq::CQuorumManager& m_qman; @@ -80,7 +68,7 @@ class InstantSendSigner final : public llmq::CRecoveredSigsListener InstantSendSigner(const InstantSendSigner&) = delete; InstantSendSigner& operator=(const InstantSendSigner&) = delete; explicit InstantSendSigner(CChainState& chainstate, const chainlock::Chainlocks& chainlocks, - InstantSendSignerParent& isman, llmq::CSigningManager& sigman, + llmq::CInstantSendManager& isman, llmq::CSigningManager& sigman, llmq::CSigSharesManager& shareman, llmq::CQuorumManager& qman, CSporkManager& sporkman, CTxMemPool& mempool, const CMasternodeSync& mn_sync); ~InstantSendSigner(); diff --git a/test/lint/lint-circular-dependencies.py b/test/lint/lint-circular-dependencies.py index 93df62fb1a91..064d8ea504c3 100755 --- a/test/lint/lint-circular-dependencies.py +++ b/test/lint/lint-circular-dependencies.py @@ -42,7 +42,7 @@ "evo/specialtxman -> validation -> evo/specialtxman", "governance/classes -> governance/object -> governance/governance -> governance/classes", "governance/governance -> governance/signing -> governance/object -> governance/governance", - "instantsend/instantsend -> instantsend/signing -> validation -> txmempool -> instantsend/instantsend", + "instantsend/instantsend -> node/blockstorage -> validation -> txmempool -> instantsend/instantsend", "llmq/blockprocessor -> llmq/utils -> llmq/snapshot -> llmq/blockprocessor", "llmq/commitment -> llmq/utils -> llmq/snapshot -> llmq/commitment", "llmq/dkgsessionhandler -> net_processing -> llmq/dkgsessionmgr -> llmq/dkgsessionhandler", From fde17fd3586f465415c4a74019f09ffa1def8197 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 12 Feb 2026 22:40:03 +0700 Subject: [PATCH 12/15] refactor: move usages sigman directly to NetInstantSend --- src/instantsend/instantsend.cpp | 9 --------- src/instantsend/net_instantsend.cpp | 10 +++++++++- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index 15c2bcaf27af..1a5d432f6674 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -493,15 +493,6 @@ Uint256HashMap CInstantSendManager::RemoveConfi int nUntilHeight = pindex->nHeight; auto removeISLocks = db.RemoveConfirmedInstantSendLocks(nUntilHeight); - for (const auto& [islockHash, islock] : removeISLocks) { - LogPrint(BCLog::INSTANTSEND, "NetInstantSend::%s -- txid=%s, islock=%s: removed islock as it got fully confirmed\n", - __func__, islock->txid.ToString(), islockHash.ToString()); - - // And we don't need the recovered sig for the ISLOCK anymore, as the block in which it got mined is considered - // fully confirmed now - sigman.TruncateRecoveredSig(Params().GetConsensus().llmqTypeDIP0024InstantSend, islock->GetRequestId()); - } - db.RemoveArchivedInstantSendLocks(nUntilHeight - 100); // Find all previously unlocked TXs that got locked by this fully confirmed (ChainLock) block and remove them diff --git a/src/instantsend/net_instantsend.cpp b/src/instantsend/net_instantsend.cpp index 11fb7d874622..21072abc96d4 100644 --- a/src/instantsend/net_instantsend.cpp +++ b/src/instantsend/net_instantsend.cpp @@ -642,7 +642,15 @@ void NetInstantSend::HandleFullyConfirmedBlock(const CBlockIndex* pindex) } auto removeISLocks = m_is_manager.RemoveConfirmedInstantSendLocks(pindex); - for (const auto& [_, islock] : removeISLocks) { + for (const auto& [islockHash, islock] : removeISLocks) { + LogPrint(BCLog::INSTANTSEND, "NetInstantSend::%s -- txid=%s, islock=%s: removed islock as it got fully confirmed\n", + __func__, islock->txid.ToString(), islockHash.ToString()); + + // And we don't need the recovered sig for the ISLOCK anymore, as the block in which it got mined is considered + // fully confirmed now + m_is_manager.Sigman().TruncateRecoveredSig(Params().GetConsensus().llmqTypeDIP0024InstantSend, + islock->GetRequestId()); + // No need to keep recovered sigs for fully confirmed IS locks, as there is no chance for conflicts // from now on. All inputs are spent now and can't be spend in any other TX. TruncateRecoveredSigsForInputs(*islock); From fa334afd51be84e7775f400408bbe53c8e515adf Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 12 Feb 2026 23:04:54 +0700 Subject: [PATCH 13/15] refactor: drop dependency of CInstantSendManager on llmq::CSigningManager --- src/init.cpp | 2 +- src/instantsend/instantsend.cpp | 6 ++---- src/instantsend/instantsend.h | 8 ++------ src/instantsend/net_instantsend.cpp | 13 ++++++------- src/instantsend/net_instantsend.h | 7 +++++-- src/llmq/context.cpp | 2 +- 6 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index df9ec01c772a..7f97004de959 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2208,7 +2208,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // ********************************************************* Step 7d: Setup other Dash services - node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.llmq_ctx->isman, node.active_ctx ? node.active_ctx->is_signer.get() : nullptr, *node.llmq_ctx->qman, chainman.ActiveChainstate(), *node.mempool, *node.mn_sync)); + node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.llmq_ctx->isman, node.active_ctx ? node.active_ctx->is_signer.get() : nullptr, *node.llmq_ctx->sigman, *node.llmq_ctx->qman, chainman.ActiveChainstate(), *node.mempool, *node.mn_sync)); node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.llmq_ctx->sigman, node.active_ctx ? node.active_ctx->shareman.get() : nullptr, *node.sporkman)); if (node.active_ctx) { diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index 1a5d432f6674..75b277cd6f41 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -16,12 +16,10 @@ using node::fImporting; using node::fReindex; namespace llmq { -CInstantSendManager::CInstantSendManager(const chainlock::Chainlocks& chainlocks, CSigningManager& _sigman, - CSporkManager& sporkman, const CMasternodeSync& mn_sync, - const util::DbWrapperParams& db_params) : +CInstantSendManager::CInstantSendManager(const chainlock::Chainlocks& chainlocks, CSporkManager& sporkman, + const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params) : db{db_params}, m_chainlocks{chainlocks}, - sigman{_sigman}, spork_manager{sporkman}, m_mn_sync{mn_sync} { diff --git a/src/instantsend/instantsend.h b/src/instantsend/instantsend.h index 067f125585fe..c77e3166743e 100644 --- a/src/instantsend/instantsend.h +++ b/src/instantsend/instantsend.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -19,8 +20,6 @@ #include #include -#include - class CBlockIndex; class CDataStream; class CMasternodeSync; @@ -54,7 +53,6 @@ struct PendingState { } // namespace instantsend namespace llmq { -class CSigningManager; class CInstantSendManager { @@ -62,7 +60,6 @@ class CInstantSendManager instantsend::CInstantSendDb db; const chainlock::Chainlocks& m_chainlocks; - CSigningManager& sigman; CSporkManager& spork_manager; const CMasternodeSync& m_mn_sync; @@ -100,7 +97,7 @@ class CInstantSendManager CInstantSendManager() = delete; CInstantSendManager(const CInstantSendManager&) = delete; CInstantSendManager& operator=(const CInstantSendManager&) = delete; - explicit CInstantSendManager(const chainlock::Chainlocks& chainlocks, CSigningManager& _sigman, CSporkManager& sporkman, + explicit CInstantSendManager(const chainlock::Chainlocks& chainlocks, CSporkManager& sporkman, const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params); ~CInstantSendManager(); @@ -127,7 +124,6 @@ class CInstantSendManager EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); [[nodiscard]] std::vector PrepareTxToRetry() EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); - CSigningManager& Sigman() { return sigman; } const chainlock::Chainlocks& Chainlocks() { return m_chainlocks; } void RemoveBlockISLocks(const std::shared_ptr& pblock, const CBlockIndex* pindex); diff --git a/src/instantsend/net_instantsend.cpp b/src/instantsend/net_instantsend.cpp index 21072abc96d4..8f82dd9e37f3 100644 --- a/src/instantsend/net_instantsend.cpp +++ b/src/instantsend/net_instantsend.cpp @@ -126,7 +126,7 @@ std::unique_ptr NetInstantSend::BuildVeri auto id = islock->GetRequestId(); // no need to verify an ISLOCK if we already have verified the recovered sig that belongs to it - if (m_is_manager.Sigman().HasRecoveredSig(llmq_params.type, id, islock->txid)) { + if (m_sigman.HasRecoveredSig(llmq_params.type, id, islock->txid)) { data->alreadyVerified++; continue; } @@ -160,7 +160,7 @@ std::unique_ptr NetInstantSend::BuildVeri // We can reconstruct the CRecoveredSig objects from the islock and pass it to the signing manager, which // avoids unnecessary double-verification of the signature. We however only do this when verification here // turns out to be good (which is checked further down) - if (!m_is_manager.Sigman().HasRecoveredSigForId(llmq_params.type, id)) { + if (!m_sigman.HasRecoveredSigForId(llmq_params.type, id)) { data->recSigs.try_emplace(hash, llmq::CRecoveredSig(llmq_params.type, quorum->qc->quorumHash, id, islock->txid, islock->sig)); } @@ -206,12 +206,12 @@ Uint256HashSet NetInstantSend::ApplyVerificationResults( auto it = data.recSigs.find(hash); if (it != data.recSigs.end()) { auto recSig = std::make_shared(std::move(it->second)); - if (!m_is_manager.Sigman().HasRecoveredSigForId(llmq_params.type, recSig->getId())) { + if (!m_sigman.HasRecoveredSigForId(llmq_params.type, recSig->getId())) { LogPrint(BCLog::INSTANTSEND, /* Continued */ "NetInstantSend::%s -- txid=%s, islock=%s: " "passing reconstructed recSig to signing mgr, peer=%d\n", __func__, islock->txid.ToString(), hash.ToString(), nodeId); - m_is_manager.Sigman().PushReconstructedRecoveredSig(recSig); + m_sigman.PushReconstructedRecoveredSig(recSig); } } } @@ -631,7 +631,7 @@ void NetInstantSend::TruncateRecoveredSigsForInputs(const instantsend::InstantSe m_signer->ClearInputsFromQueue(ids); } for (const auto& id : ids) { - m_is_manager.Sigman().TruncateRecoveredSig(Params().GetConsensus().llmqTypeDIP0024InstantSend, id); + m_sigman.TruncateRecoveredSig(Params().GetConsensus().llmqTypeDIP0024InstantSend, id); } } @@ -648,8 +648,7 @@ void NetInstantSend::HandleFullyConfirmedBlock(const CBlockIndex* pindex) // And we don't need the recovered sig for the ISLOCK anymore, as the block in which it got mined is considered // fully confirmed now - m_is_manager.Sigman().TruncateRecoveredSig(Params().GetConsensus().llmqTypeDIP0024InstantSend, - islock->GetRequestId()); + m_sigman.TruncateRecoveredSig(Params().GetConsensus().llmqTypeDIP0024InstantSend, islock->GetRequestId()); // No need to keep recovered sigs for fully confirmed IS locks, as there is no chance for conflicts // from now on. All inputs are spent now and can't be spend in any other TX. diff --git a/src/instantsend/net_instantsend.h b/src/instantsend/net_instantsend.h index 338ca7f12ec4..34b9e8f7ffe7 100644 --- a/src/instantsend/net_instantsend.h +++ b/src/instantsend/net_instantsend.h @@ -28,6 +28,7 @@ struct PendingISLockEntry; class InstantSendSigner; } // namespace instantsend namespace llmq { +class CSigningManager; class CInstantSendManager; class CQuorumManager; } // namespace llmq @@ -36,11 +37,12 @@ class NetInstantSend final : public NetHandler, public CValidationInterface { public: NetInstantSend(PeerManagerInternal* peer_manager, llmq::CInstantSendManager& is_manager, - instantsend::InstantSendSigner* signer, llmq::CQuorumManager& qman, CChainState& chainstate, - CTxMemPool& mempool, const CMasternodeSync& mn_sync) : + instantsend::InstantSendSigner* signer, llmq::CSigningManager& sigman, llmq::CQuorumManager& qman, + CChainState& chainstate, CTxMemPool& mempool, const CMasternodeSync& mn_sync) : NetHandler(peer_manager), m_is_manager{is_manager}, m_signer{signer}, + m_sigman{sigman}, m_qman(qman), m_chainstate{chainstate}, m_mempool{mempool}, @@ -100,6 +102,7 @@ class NetInstantSend final : public NetHandler, public CValidationInterface llmq::CInstantSendManager& m_is_manager; instantsend::InstantSendSigner* m_signer; // non-null only for masternode + llmq::CSigningManager& m_sigman; llmq::CQuorumManager& m_qman; CChainState& m_chainstate; CTxMemPool& m_mempool; diff --git a/src/llmq/context.cpp b/src/llmq/context.cpp index fa3e876972d1..4ad7e7f07dbf 100644 --- a/src/llmq/context.cpp +++ b/src/llmq/context.cpp @@ -22,7 +22,7 @@ LLMQContext::LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSpork qman{std::make_unique(*bls_worker, dmnman, evo_db, *quorum_block_processor, *qsnapman, chainman, db_params)}, sigman{std::make_unique(*qman, db_params, max_recsigs_age)}, - isman{std::make_unique(chainlocks, *sigman, sporkman, mn_sync, db_params)} + isman{std::make_unique(chainlocks, sporkman, mn_sync, db_params)} { // Have to start it early to let VerifyDB check ChainLock signatures in coinbase bls_worker->Start(); From 7bdab8bccdf1a84b60943e81cbe6ee6a107416f8 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Wed, 25 Feb 2026 18:01:47 +0700 Subject: [PATCH 14/15] refactor: drop dependency of CInstantSendManager on chainlocks --- src/init.cpp | 2 +- src/instantsend/instantsend.cpp | 6 ++---- src/instantsend/instantsend.h | 11 ++--------- src/instantsend/net_instantsend.cpp | 8 ++++---- src/instantsend/net_instantsend.h | 9 ++++++++- src/llmq/context.cpp | 4 ++-- src/llmq/context.h | 6 +----- src/node/chainstate.cpp | 2 +- 8 files changed, 21 insertions(+), 27 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 7f97004de959..f2962ff240df 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2208,7 +2208,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // ********************************************************* Step 7d: Setup other Dash services - node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.llmq_ctx->isman, node.active_ctx ? node.active_ctx->is_signer.get() : nullptr, *node.llmq_ctx->sigman, *node.llmq_ctx->qman, chainman.ActiveChainstate(), *node.mempool, *node.mn_sync)); + node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.llmq_ctx->isman, node.active_ctx ? node.active_ctx->is_signer.get() : nullptr, *node.llmq_ctx->sigman, *node.llmq_ctx->qman, *node.chainlocks, chainman.ActiveChainstate(), *node.mempool, *node.mn_sync)); node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.llmq_ctx->sigman, node.active_ctx ? node.active_ctx->shareman.get() : nullptr, *node.sporkman)); if (node.active_ctx) { diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index 75b277cd6f41..ed0133047f08 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -4,7 +4,6 @@ #include -#include #include #include #include @@ -16,10 +15,9 @@ using node::fImporting; using node::fReindex; namespace llmq { -CInstantSendManager::CInstantSendManager(const chainlock::Chainlocks& chainlocks, CSporkManager& sporkman, - const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params) : +CInstantSendManager::CInstantSendManager(CSporkManager& sporkman, const CMasternodeSync& mn_sync, + const util::DbWrapperParams& db_params) : db{db_params}, - m_chainlocks{chainlocks}, spork_manager{sporkman}, m_mn_sync{mn_sync} { diff --git a/src/instantsend/instantsend.h b/src/instantsend/instantsend.h index c77e3166743e..ac56fcf8e667 100644 --- a/src/instantsend/instantsend.h +++ b/src/instantsend/instantsend.h @@ -31,10 +31,6 @@ namespace util { struct DbWrapperParams; } // namespace util -namespace chainlock { -class Chainlocks; -} - namespace instantsend { struct PendingISLockFromPeer { @@ -58,8 +54,6 @@ class CInstantSendManager { private: instantsend::CInstantSendDb db; - - const chainlock::Chainlocks& m_chainlocks; CSporkManager& spork_manager; const CMasternodeSync& m_mn_sync; @@ -97,8 +91,8 @@ class CInstantSendManager CInstantSendManager() = delete; CInstantSendManager(const CInstantSendManager&) = delete; CInstantSendManager& operator=(const CInstantSendManager&) = delete; - explicit CInstantSendManager(const chainlock::Chainlocks& chainlocks, CSporkManager& sporkman, - const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params); + explicit CInstantSendManager(CSporkManager& sporkman, const CMasternodeSync& mn_sync, + const util::DbWrapperParams& db_params); ~CInstantSendManager(); void AddNonLockedTx(const CTransactionRef& tx, const CBlockIndex* pindexMined) @@ -124,7 +118,6 @@ class CInstantSendManager EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); [[nodiscard]] std::vector PrepareTxToRetry() EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); - const chainlock::Chainlocks& Chainlocks() { return m_chainlocks; } void RemoveBlockISLocks(const std::shared_ptr& pblock, const CBlockIndex* pindex); void WriteBlockISLocks(const std::shared_ptr& pblock, const CBlockIndex* pindex); diff --git a/src/instantsend/net_instantsend.cpp b/src/instantsend/net_instantsend.cpp index 8f82dd9e37f3..628db7a61b3d 100644 --- a/src/instantsend/net_instantsend.cpp +++ b/src/instantsend/net_instantsend.cpp @@ -376,7 +376,7 @@ void NetInstantSend::ProcessInstantSendLock(NodeId from, const uint256& hash, co } // 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 (minedHeight.has_value() && m_is_manager.Chainlocks().HasChainLock(*minedHeight, hashBlock)) { + if (minedHeight.has_value() && m_chainlocks.HasChainLock(*minedHeight, hashBlock)) { LogPrint(BCLog::INSTANTSEND, /* Continued */ "NetSigning::%s -- txlock=%s, islock=%s: dropping islock as it already got a " "ChainLock in block %s, peer=%d\n", @@ -501,7 +501,7 @@ void NetInstantSend::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockI { bool fDIP0008Active = pindexNew->pprev && pindexNew->pprev->nHeight >= Params().GetConsensus().DIP0008Height; - if (m_is_manager.Chainlocks().IsEnabled() && fDIP0008Active) { + if (m_chainlocks.IsEnabled() && fDIP0008Active) { // Nothing to do here. We should keep all islocks and let chainlocks handle them. return; } @@ -529,7 +529,7 @@ void NetInstantSend::BlockConnected(const std::shared_ptr& pblock, m_is_manager.CacheTipHeight(pindex); if (m_mn_sync.IsBlockchainSynced()) { - const bool has_chainlock = m_is_manager.Chainlocks().HasChainLock(pindex->nHeight, pindex->GetBlockHash()); + const bool has_chainlock = m_chainlocks.HasChainLock(pindex->nHeight, pindex->GetBlockHash()); for (const auto& tx : pblock->vtx) { if (tx->IsCoinBase() || tx->vin.empty()) { // coinbase and TXs with no inputs can't be locked @@ -571,7 +571,7 @@ void NetInstantSend::ResolveBlockConflicts(const uint256& islockHash, const inst bool hasChainLockedConflict = false; for (const auto& p : conflicts) { const auto* pindex = p.first; - if (m_is_manager.Chainlocks().HasChainLock(pindex->nHeight, pindex->GetBlockHash())) { + if (m_chainlocks.HasChainLock(pindex->nHeight, pindex->GetBlockHash())) { hasChainLockedConflict = true; break; } diff --git a/src/instantsend/net_instantsend.h b/src/instantsend/net_instantsend.h index 34b9e8f7ffe7..4d0b64609b5c 100644 --- a/src/instantsend/net_instantsend.h +++ b/src/instantsend/net_instantsend.h @@ -18,6 +18,10 @@ namespace Consensus { struct LLMQParams; } // namespace Consensus +namespace chainlock { +class Chainlocks; +} // namespace chainlock + class CMasternodeSync; class CTxMemPool; @@ -38,12 +42,14 @@ class NetInstantSend final : public NetHandler, public CValidationInterface public: NetInstantSend(PeerManagerInternal* peer_manager, llmq::CInstantSendManager& is_manager, instantsend::InstantSendSigner* signer, llmq::CSigningManager& sigman, llmq::CQuorumManager& qman, - CChainState& chainstate, CTxMemPool& mempool, const CMasternodeSync& mn_sync) : + const chainlock::Chainlocks& chainlocks, CChainState& chainstate, CTxMemPool& mempool, + const CMasternodeSync& mn_sync) : NetHandler(peer_manager), m_is_manager{is_manager}, m_signer{signer}, m_sigman{sigman}, m_qman(qman), + m_chainlocks{chainlocks}, m_chainstate{chainstate}, m_mempool{mempool}, m_mn_sync{mn_sync} @@ -104,6 +110,7 @@ class NetInstantSend final : public NetHandler, public CValidationInterface instantsend::InstantSendSigner* m_signer; // non-null only for masternode llmq::CSigningManager& m_sigman; llmq::CQuorumManager& m_qman; + const chainlock::Chainlocks& m_chainlocks; CChainState& m_chainstate; CTxMemPool& m_mempool; const CMasternodeSync& m_mn_sync; diff --git a/src/llmq/context.cpp b/src/llmq/context.cpp index 4ad7e7f07dbf..1a6087783a88 100644 --- a/src/llmq/context.cpp +++ b/src/llmq/context.cpp @@ -13,7 +13,7 @@ #include LLMQContext::LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSporkManager& sporkman, - chainlock::Chainlocks& chainlocks, ChainstateManager& chainman, const CMasternodeSync& mn_sync, + ChainstateManager& chainman, const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params, int8_t bls_threads, int64_t max_recsigs_age) : bls_worker{std::make_shared()}, qsnapman{std::make_unique(evo_db)}, @@ -22,7 +22,7 @@ LLMQContext::LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSpork qman{std::make_unique(*bls_worker, dmnman, evo_db, *quorum_block_processor, *qsnapman, chainman, db_params)}, sigman{std::make_unique(*qman, db_params, max_recsigs_age)}, - isman{std::make_unique(chainlocks, sporkman, mn_sync, db_params)} + isman{std::make_unique(sporkman, mn_sync, db_params)} { // Have to start it early to let VerifyDB check ChainLock signatures in coinbase bls_worker->Start(); diff --git a/src/llmq/context.h b/src/llmq/context.h index 0968d320e21a..2a03ffea7be6 100644 --- a/src/llmq/context.h +++ b/src/llmq/context.h @@ -17,10 +17,6 @@ class CMasternodeSync; class CSporkManager; class PeerManager; -namespace chainlock { -class Chainlocks; -} - namespace llmq { class CInstantSendManager; class CQuorumBlockProcessor; @@ -38,7 +34,7 @@ struct LLMQContext { LLMQContext(const LLMQContext&) = delete; LLMQContext& operator=(const LLMQContext&) = delete; explicit LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSporkManager& sporkman, - chainlock::Chainlocks& chainlocks, ChainstateManager& chainman, const CMasternodeSync& mn_sync, + ChainstateManager& chainman, const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params, int8_t bls_threads, int64_t max_recsigs_age); ~LLMQContext(); diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index 60f763aaa39f..251fceadbac5 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -227,7 +227,7 @@ void DashChainstateSetup(ChainstateManager& chainman, dmnman = std::make_unique(evodb, mn_metaman); llmq_ctx.reset(); - llmq_ctx = std::make_unique(*dmnman, evodb, sporkman, chainlocks, chainman, mn_sync, + llmq_ctx = std::make_unique(*dmnman, evodb, sporkman, chainman, mn_sync, util::DbWrapperParams{.path = data_dir, .memory = llmq_dbs_in_memory, .wipe = llmq_dbs_wipe}, bls_threads, max_recsigs_age); mempool->ConnectManagers(dmnman.get(), llmq_ctx->isman.get()); From 5163e5baa9bcb459595eae62ac9dd3e9164df361 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Wed, 25 Feb 2026 23:23:45 +0700 Subject: [PATCH 15/15] refactor: removed unused includes --- src/instantsend/instantsend.h | 3 +-- src/instantsend/signing.h | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/instantsend/instantsend.h b/src/instantsend/instantsend.h index ac56fcf8e667..0699104875a9 100644 --- a/src/instantsend/instantsend.h +++ b/src/instantsend/instantsend.h @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -17,7 +16,6 @@ #include #include -#include #include class CBlockIndex; @@ -30,6 +28,7 @@ struct LLMQParams; namespace util { struct DbWrapperParams; } // namespace util +typedef std::shared_ptr CTransactionRef; namespace instantsend { diff --git a/src/instantsend/signing.h b/src/instantsend/signing.h index 45e6174dcd9f..f7df067e4b9d 100644 --- a/src/instantsend/signing.h +++ b/src/instantsend/signing.h @@ -6,10 +6,9 @@ #define BITCOIN_INSTANTSEND_SIGNING_H #include -#include #include - -#include +#include +#include class CMasternodeSync; class CSporkManager;