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/dsnotificationinterface.cpp b/src/dsnotificationinterface.cpp index f2566970adc3..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); } @@ -85,25 +75,16 @@ 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); } -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); } @@ -118,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 fdb3e5af3998..f2962ff240df 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -97,6 +97,7 @@ #include #include #include +#include #include #include #include @@ -2175,7 +2176,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()); @@ -2207,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.llmq_ctx->qman, chainman.ActiveChainstate())); + 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 0726da6911e3..ed0133047f08 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -4,59 +4,21 @@ #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::fImporting; using node::fReindex; -using node::GetTransaction; 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, CChainState& chainstate, - CSigningManager& _sigman, CSporkManager& sporkman, CTxMemPool& _mempool, - 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}, - m_chainstate{chainstate}, - sigman{_sigman}, spork_manager{sporkman}, - mempool{_mempool}, m_mn_sync{mn_sync} { } @@ -118,22 +80,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,106 +97,32 @@ std::variant CInstantSendManager::Proc islock->txid.ToString(), hash.ToString(), in.ToStringShort(), ::SerializeHash(*sameOutpointIsLock).ToString(), from); } } - - 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); - } - - if (found_transaction) { - return tx; - } - return islock->txid; + return true; } -void CInstantSendManager::TransactionAddedToMempool(const CTransactionRef& tx) +void CInstantSendManager::WriteNewISLock(const uint256& hash, const instantsend::InstantSendLockPtr& islock, + std::optional minedHeight) { - 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; - } + db.WriteNewInstantSendLock(hash, islock); + if (minedHeight.has_value()) { + db.WriteInstantSendLockMined(hash, *minedHeight); } +} - 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::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::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; @@ -252,49 +133,34 @@ 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, pindex); +} - 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) @@ -313,22 +179,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 @@ -397,25 +248,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) { @@ -426,103 +258,8 @@ 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()) { - 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); - } -} - -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) +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; @@ -550,65 +287,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) @@ -737,43 +422,33 @@ CInstantSendManager::Counts CInstantSendManager::GetCounts() const return ret; } -void CInstantSendManager::CacheBlockHeightInternal(const CBlockIndex* const block_index) const +void CInstantSendManager::CacheBlockHeight(const CBlockIndex* const block_index) const { - AssertLockHeld(cs_height_cache); + LOCK(cs_height_cache); m_cached_block_heights.insert(block_index->GetBlockHash(), block_index->nHeight); } -void CInstantSendManager::CacheBlockHeight(const CBlockIndex* const block_index) const +void CInstantSendManager::CacheDisconnectBlock(const CBlockIndex* pindexDisconnected) { LOCK(cs_height_cache); - CacheBlockHeightInternal(block_index); + 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; @@ -782,17 +457,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 @@ -811,4 +483,32 @@ bool CInstantSendManager::RejectConflictingBlocks() const } return true; } + +Uint256HashMap CInstantSendManager::RemoveConfirmedInstantSendLocks(const CBlockIndex* pindex) +{ + int nUntilHeight = pindex->nHeight; + auto removeISLocks = db.RemoveConfirmedInstantSendLocks(nUntilHeight); + + db.RemoveArchivedInstantSendLocks(nUntilHeight - 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); + } + + return removeISLocks; +} } // namespace llmq diff --git a/src/instantsend/instantsend.h b/src/instantsend/instantsend.h index dde3739d6030..0699104875a9 100644 --- a/src/instantsend/instantsend.h +++ b/src/instantsend/instantsend.h @@ -7,42 +7,30 @@ #include #include -#include #include -#include #include +#include #include #include #include -#include #include -#include -#include #include -#include - class CBlockIndex; -class CChainState; class CDataStream; class CMasternodeSync; class CSporkManager; -class CTxMemPool; namespace Consensus { struct LLMQParams; } // namespace Consensus namespace util { struct DbWrapperParams; } // namespace util - -namespace chainlock { -class Chainlocks; -} +typedef std::shared_ptr CTransactionRef; namespace instantsend { -class InstantSendSigner; struct PendingISLockFromPeer { NodeId node_id; @@ -60,22 +48,14 @@ struct PendingState { } // namespace instantsend namespace llmq { -class CSigningManager; -class CInstantSendManager final : public instantsend::InstantSendSignerParent +class CInstantSendManager { private: instantsend::CInstantSendDb db; - - const chainlock::Chainlocks& m_chainlocks; - CChainState& m_chainstate; - CSigningManager& sigman; CSporkManager& spork_manager; - CTxMemPool& mempool; 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); @@ -106,48 +86,29 @@ 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, CTxMemPool& _mempool, - const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params); + explicit CInstantSendManager(CSporkManager& sporkman, 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); } - -private: void AddNonLockedTx(const CTransactionRef& tx, const CBlockIndex* pindexMined) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks, !cs_timingsTxSeen); void RemoveNonLockedTx(const uint256& txid, bool retryChildren) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); - void RemoveConflictedTx(const CTransaction& tx) - EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); - 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); + instantsend::InstantSendLockPtr AttachISLockToTx(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); - void HandleFullyConfirmedBlock(const CBlockIndex* pindex) - EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); + 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); -public: - 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 @@ -156,32 +117,24 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingLocks); [[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); - - void TransactionAddedToMempool(const CTransactionRef& tx) - EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingLocks, !cs_pendingRetry, !cs_timingsTxSeen); - 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 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; 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 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; @@ -195,17 +148,20 @@ 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); - std::optional GetBlockHeight(const uint256& hash) const override EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); + void CacheDisconnectBlock(const CBlockIndex* pindexDisconnected) 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 * allows ChainLocks to continue even while this spork is disabled. */ bool RejectConflictingBlocks() const; + Uint256HashMap RemoveConfirmedInstantSendLocks(const CBlockIndex* pindex) + EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); }; } // namespace llmq diff --git a/src/instantsend/net_instantsend.cpp b/src/instantsend/net_instantsend.cpp index 64fcfd0fd8ee..628db7a61b3d 100644 --- a/src/instantsend/net_instantsend.cpp +++ b/src/instantsend/net_instantsend.cpp @@ -5,19 +5,30 @@ #include #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 { +CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const m_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}; @@ -26,6 +37,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; @@ -45,7 +73,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; } @@ -98,12 +126,12 @@ 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; } - 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; @@ -132,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)); } @@ -172,27 +200,18 @@ 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); 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); } } } @@ -200,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) { @@ -282,7 +322,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 @@ -311,6 +350,70 @@ void NetInstantSend::ProcessPendingISLocks(std::vectortxid.ToString(), hash.ToString(), from); + + if (m_signer) { + m_signer->ClearLockFromQueue(islock); + } + if (!m_is_manager.PreVerifyIsLock(hash, islock, from)) return; + + 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 = GetBlockHeight(m_is_manager, m_chainstate, 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_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; + } + 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 + TruncateRecoveredSigsForInputs(*islock); + ResolveBlockConflicts(hash, *islock); + + if (found_transaction) { + 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 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) { + m_peer_manager->PeerRelayInvFiltered(inv, *tx); + } else { + m_peer_manager->PeerRelayInvFiltered(inv, islock->txid); + m_peer_manager->PeerAskPeersForTransaction(islock->txid); + } +} + void NetInstantSend::WorkThreadMain() { while (!workInterrupt) { @@ -321,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; }(); @@ -332,3 +435,223 @@ 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 (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); + } else { + RemoveMempoolConflictsForLock(::SerializeHash(*islock), *islock); + } +} + +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; + + { + 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); + } + } + ClearConflicting(toDelete); +} + +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_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 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_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 (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); + } 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) +{ + 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_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; + ClearConflicting(p.second); + + 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); + } + } +} + +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_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); + 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_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); + } +} diff --git a/src/instantsend/net_instantsend.h b/src/instantsend/net_instantsend.h index 5601cbb5f1c2..4d0b64609b5c 100644 --- a/src/instantsend/net_instantsend.h +++ b/src/instantsend/net_instantsend.h @@ -6,9 +6,8 @@ #define BITCOIN_INSTANTSEND_NET_INSTANTSEND_H #include -#include - #include +#include #include #include @@ -18,24 +17,42 @@ namespace Consensus { struct LLMQParams; } // namespace Consensus + +namespace chainlock { +class Chainlocks; +} // namespace chainlock + +class CMasternodeSync; +class CTxMemPool; + namespace instantsend { struct InstantSendLock; +using InstantSendLockPtr = std::shared_ptr; struct PendingISLockEntry; +class InstantSendSigner; } // namespace instantsend namespace llmq { +class CSigningManager; 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) : + NetInstantSend(PeerManagerInternal* peer_manager, llmq::CInstantSendManager& is_manager, + instantsend::InstantSendSigner* signer, llmq::CSigningManager& sigman, llmq::CQuorumManager& qman, + 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_chainstate{chainstate} + m_chainlocks{chainlocks}, + m_chainstate{chainstate}, + m_mempool{mempool}, + m_mn_sync{mn_sync} { workInterrupt.reset(); } @@ -47,6 +64,18 @@ class NetInstantSend final : public NetHandler void WorkThreadMain(); +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, + 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; @@ -63,13 +92,28 @@ class NetInstantSend final : public NetHandler const std::vector& pend); 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, const std::vector& pend); + + 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::CSigningManager& m_sigman; llmq::CQuorumManager& m_qman; - const CChainState& m_chainstate; + const chainlock::Chainlocks& m_chainlocks; + CChainState& m_chainstate; + CTxMemPool& m_mempool; + const CMasternodeSync& m_mn_sync; std::thread workThread; CThreadInterrupt workInterrupt; diff --git a/src/instantsend/signing.cpp b/src/instantsend/signing.cpp index 40dcc58d7589..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}, @@ -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..f7df067e4b9d 100644 --- a/src/instantsend/signing.h +++ b/src/instantsend/signing.h @@ -7,8 +7,8 @@ #include #include - -#include +#include +#include class CMasternodeSync; class CSporkManager; @@ -29,25 +29,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 GetBlockHeight(const uint256& hash) const = 0; - virtual int GetTipHeight() 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; @@ -79,7 +67,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/src/llmq/context.cpp b/src/llmq/context.cpp index cc43675a29ef..1a6087783a88 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) : + 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, @@ -23,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, - mempool, 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 677d5b6a76c9..2a03ffea7be6 100644 --- a/src/llmq/context.h +++ b/src/llmq/context.h @@ -15,13 +15,8 @@ class CDeterministicMNManager; class CEvoDB; class CMasternodeSync; class CSporkManager; -class CTxMemPool; class PeerManager; -namespace chainlock { -class Chainlocks; -} - namespace llmq { class CInstantSendManager; class CQuorumBlockProcessor; @@ -39,9 +34,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); + 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/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; } diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index aee964546334..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, *mempool, 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()); diff --git a/test/lint/lint-circular-dependencies.py b/test/lint/lint-circular-dependencies.py index db7472d9eaef..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 -> 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",