LCOV - code coverage report
Current view: top level - src/instantsend - instantsend.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 279 307 90.9 %
Date: 2026-06-25 07:23:43 Functions: 38 39 97.4 %

          Line data    Source code
       1             : // Copyright (c) 2019-2025 The Dash Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <instantsend/instantsend.h>
       6             : 
       7             : #include <chainparams.h>
       8             : #include <consensus/validation.h>
       9             : #include <node/blockstorage.h>
      10             : #include <spork.h>
      11             : #include <stats/client.h>
      12             : 
      13             : using node::fImporting;
      14             : using node::fReindex;
      15             : 
      16             : namespace llmq {
      17        9189 : CInstantSendManager::CInstantSendManager(CSporkManager& sporkman, const util::DbWrapperParams& db_params) :
      18        3063 :     db{db_params},
      19        3063 :     spork_manager{sporkman}
      20        3063 : {
      21        3063 : }
      22             : 
      23        6126 : CInstantSendManager::~CInstantSendManager() = default;
      24             : 
      25        3141 : bool ShouldReportISLockTiming() { return g_stats_client->active() || LogAcceptDebug(BCLog::INSTANTSEND); }
      26             : 
      27         300 : void CInstantSendManager::EnqueueInstantSendLock(NodeId from, const uint256& hash,
      28             :                                                  std::shared_ptr<instantsend::InstantSendLock> islock)
      29             : {
      30         300 :     if (ShouldReportISLockTiming()) {
      31         600 :         auto time_diff = [&]() -> int64_t {
      32         300 :             LOCK(cs_timingsTxSeen);
      33         300 :             if (auto it = timingsTxSeen.find(islock->txid); it != timingsTxSeen.end()) {
      34             :                 // This is the normal case where we received the TX before the islock
      35         267 :                 auto diff = Ticks<std::chrono::milliseconds>(SteadyClock::now() - it->second);
      36         267 :                 timingsTxSeen.erase(it);
      37         267 :                 return diff;
      38             :             }
      39             :             // But if we received the islock and don't know when we got the tx, then say 0, to indicate we received the islock first.
      40          33 :             return 0;
      41         300 :         }();
      42         300 :         ::g_stats_client->timing("islock_ms", time_diff);
      43         300 :         LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- txid=%s, islock took %dms\n", __func__,
      44             :                  islock->txid.ToString(), time_diff);
      45         300 :     }
      46             : 
      47         300 :     LOCK(cs_pendingLocks);
      48         300 :     pendingInstantSendLocks.emplace(hash, instantsend::PendingISLockFromPeer{from, std::move(islock)});
      49         300 : }
      50             : 
      51      189053 : instantsend::PendingState CInstantSendManager::FetchPendingLocks()
      52             : {
      53      189053 :     instantsend::PendingState ret;
      54             : 
      55      189053 :     LOCK(cs_pendingLocks);
      56             :     // only process a max 32 locks at a time to avoid duplicate verification of recovered signatures which have been
      57             :     // verified by CSigningManager in parallel
      58      189053 :     const size_t maxCount = 32;
      59             :     // The keys of the removed values are temporaily stored here to avoid invalidating an iterator
      60      189053 :     std::vector<uint256> removed;
      61      189053 :     removed.reserve(std::min(maxCount, pendingInstantSendLocks.size()));
      62             : 
      63      189918 :     for (auto& [islockHash, pending] : pendingInstantSendLocks) {
      64             :         // Check if we've reached max count
      65         865 :         if (ret.m_pending_is.size() >= maxCount) {
      66           0 :             ret.m_pending_work = true;
      67           0 :             break;
      68             :         }
      69        2595 :         ret.m_pending_is.push_back(instantsend::PendingISLockEntry{std::move(pending), islockHash});
      70         865 :         removed.emplace_back(islockHash);
      71             :     }
      72             : 
      73      189918 :     for (const auto& islockHash : removed) {
      74         865 :         pendingInstantSendLocks.erase(islockHash);
      75             :     }
      76             : 
      77      189053 :     return ret;
      78      189053 : }
      79             : 
      80         865 : bool CInstantSendManager::PreVerifyIsLock(const uint256& hash, const instantsend::InstantSendLockPtr& islock, NodeId from) const
      81             : {
      82         865 :     if (db.KnownInstantSendLock(hash)) {
      83           0 :         return false;
      84             :     }
      85             : 
      86         865 :     if (const auto sameTxIsLock = db.GetInstantSendLockByTxid(islock->txid)) {
      87             :         // can happen, nothing to do
      88           0 :         return false;
      89             :     }
      90        3250 :     for (const auto& in : islock->inputs) {
      91        2385 :         const auto sameOutpointIsLock = db.GetInstantSendLockByInput(in);
      92        2385 :         if (sameOutpointIsLock != nullptr) {
      93           0 :             LogPrintf("CInstantSendManager::%s -- txid=%s, islock=%s: conflicting outpoint in islock. input=%s, other islock=%s, peer=%d\n", __func__,
      94             :                       islock->txid.ToString(), hash.ToString(), in.ToStringShort(), ::SerializeHash(*sameOutpointIsLock).ToString(), from);
      95           0 :         }
      96        2385 :     }
      97         865 :     return true;
      98         865 : }
      99             : 
     100         820 : void CInstantSendManager::WriteNewISLock(const uint256& hash, const instantsend::InstantSendLockPtr& islock,
     101             :                                          std::optional<int> minedHeight)
     102             : {
     103         820 :     db.WriteNewInstantSendLock(hash, islock);
     104         820 :     if (minedHeight.has_value()) {
     105         306 :         db.WriteInstantSendLockMined(hash, *minedHeight);
     106         306 :     }
     107         820 : }
     108             : 
     109          31 : void CInstantSendManager::AddPendingISLock(const uint256& hash, const instantsend::InstantSendLockPtr& islock, NodeId from)
     110             : {
     111             :     // put it in a separate pending map and try again later
     112          31 :     LOCK(cs_pendingLocks);
     113          31 :     pendingNoTxInstantSendLocks.try_emplace(hash, instantsend::PendingISLockFromPeer{from, islock});
     114          31 : }
     115             : 
     116        2311 : void CInstantSendManager::TransactionIsRemoved(const CTransactionRef& tx)
     117             : {
     118        2311 :     if (tx->vin.empty()) {
     119          52 :         return;
     120             :     }
     121             : 
     122        2259 :     instantsend::InstantSendLockPtr islock = GetInstantSendLockByTxid(tx->GetHash());
     123             : 
     124        2259 :     if (islock == nullptr) {
     125        2259 :         return;
     126             :     }
     127             : 
     128           0 :     LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- transaction %s was removed from mempool\n", __func__,
     129             :              tx->GetHash().ToString());
     130           0 :     RemoveConflictingLock(::SerializeHash(*islock), *islock);
     131        2311 : }
     132             : 
     133       66859 : void CInstantSendManager::WriteBlockISLocks(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex)
     134             : {
     135       66859 :     db.WriteBlockInstantSendLocks(pblock, pindex);
     136       66859 : }
     137             : 
     138       14229 : void CInstantSendManager::RemoveBlockISLocks(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex)
     139             : {
     140       14229 :     db.RemoveBlockInstantSendLocks(pblock, pindex);
     141       14229 : }
     142             : 
     143        4250 : instantsend::InstantSendLockPtr CInstantSendManager::AttachISLockToTx(const CTransactionRef& tx)
     144             : {
     145        4250 :     LOCK(cs_pendingLocks);
     146        4333 :     for (auto it = pendingNoTxInstantSendLocks.begin(); it != pendingNoTxInstantSendLocks.end(); ++it) {
     147         114 :         if (it->second.islock->txid != tx->GetHash()) continue;
     148             : 
     149             :         // we received an islock earlier, let's put it back into pending and verify/lock
     150          31 :         LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- txid=%s, islock=%s\n", __func__,
     151             :                  tx->GetHash().ToString(), it->first.ToString());
     152          31 :         auto islock = it->second.islock;
     153          31 :         pendingInstantSendLocks.try_emplace(it->first, it->second);
     154          31 :         pendingNoTxInstantSendLocks.erase(it);
     155          31 :         return islock;
     156          31 :     }
     157        4219 :     return nullptr;
     158        4250 : }
     159             : 
     160        2841 : void CInstantSendManager::AddNonLockedTx(const CTransactionRef& tx, const CBlockIndex* pindexMined)
     161             : {
     162             :     {
     163        2841 :         LOCK(cs_nonLocked);
     164        2841 :         auto [it, did_insert] = nonLockedTxs.emplace(tx->GetHash(), NonLockedTxInfo());
     165        2841 :         auto& nonLockedTxInfo = it->second;
     166        2841 :         nonLockedTxInfo.pindexMined = pindexMined;
     167             : 
     168        2841 :         if (did_insert) {
     169        2129 :             nonLockedTxInfo.tx = tx;
     170        8208 :             for (const auto& in : tx->vin) {
     171        6079 :                 nonLockedTxs[in.prevout.hash].children.emplace(tx->GetHash());
     172        6079 :                 nonLockedTxsByOutpoints.emplace(in.prevout, tx->GetHash());
     173             :             }
     174        2129 :         }
     175        2841 :     }
     176        2841 :     AttachISLockToTx(tx);
     177        2841 :     if (ShouldReportISLockTiming()) {
     178        2841 :         LOCK(cs_timingsTxSeen);
     179             :         // Only insert the time the first time we see the tx, as we sometimes try to resign
     180        2841 :         timingsTxSeen.try_emplace(tx->GetHash(), SteadyClock::now());
     181        2841 :     }
     182             : 
     183        2841 :     LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- txid=%s, pindexMined=%s\n", __func__,
     184             :              tx->GetHash().ToString(), pindexMined ? pindexMined->GetBlockHash().ToString() : "");
     185        2841 : }
     186             : 
     187        2423 : void CInstantSendManager::RemoveNonLockedTx(const uint256& txid, bool retryChildren)
     188             : {
     189        2423 :     LOCK(cs_nonLocked);
     190             : 
     191        2423 :     auto it = nonLockedTxs.find(txid);
     192        2423 :     if (it == nonLockedTxs.end()) {
     193         421 :         return;
     194             :     }
     195        2002 :     const auto& info = it->second;
     196             : 
     197        2002 :     size_t retryChildrenCount = 0;
     198        2002 :     if (retryChildren) {
     199             :         // TX got locked, so we can retry locking children
     200        1992 :         LOCK(cs_pendingRetry);
     201        2363 :         for (const auto& childTxid : info.children) {
     202         371 :             pendingRetryTxs.emplace(childTxid);
     203         371 :             retryChildrenCount++;
     204             :         }
     205        1992 :     }
     206             :     // don't try to lock it anymore
     207        4004 :     WITH_LOCK(cs_pendingRetry, pendingRetryTxs.erase(txid));
     208             : 
     209        2002 :     if (info.tx) {
     210        7932 :         for (const auto& in : info.tx->vin) {
     211        5930 :             if (auto jt = nonLockedTxs.find(in.prevout.hash); jt != nonLockedTxs.end()) {
     212        5495 :                 jt->second.children.erase(txid);
     213        5495 :                 if (!jt->second.tx && jt->second.children.empty()) {
     214        4532 :                     nonLockedTxs.erase(jt);
     215        4532 :                 }
     216        5495 :             }
     217        5930 :             nonLockedTxsByOutpoints.erase(in.prevout);
     218             :         }
     219        2002 :     }
     220             : 
     221        2002 :     nonLockedTxs.erase(it);
     222             : 
     223        2002 :     LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- txid=%s, retryChildren=%d, retryChildrenCount=%d\n",
     224             :              __func__, txid.ToString(), retryChildren, retryChildrenCount);
     225        2423 : }
     226             : 
     227      154880 : std::vector<CTransactionRef> CInstantSendManager::PrepareTxToRetry()
     228             : {
     229      154880 :     std::vector<CTransactionRef> txns{};
     230             : 
     231      154880 :     LOCK2(cs_nonLocked, cs_pendingRetry);
     232      154880 :     if (pendingRetryTxs.empty()) return txns;
     233         239 :     txns.reserve(pendingRetryTxs.size());
     234         478 :     for (const auto& txid : pendingRetryTxs) {
     235         239 :         if (auto it = nonLockedTxs.find(txid); it != nonLockedTxs.end()) {
     236         239 :             const auto& [_, tx_info] = *it;
     237         239 :             if (tx_info.tx) {
     238         239 :                 txns.push_back(tx_info.tx);
     239         239 :             }
     240         239 :         }
     241             :     }
     242         239 :     return txns;
     243      154880 : }
     244             : 
     245         538 : void CInstantSendManager::TryEmplacePendingLock(const uint256& hash, const NodeId id,
     246             :                                                 const instantsend::InstantSendLockPtr& islock)
     247             : {
     248         538 :     if (db.KnownInstantSendLock(hash)) return;
     249         538 :     LOCK(cs_pendingLocks);
     250         538 :     if (!pendingInstantSendLocks.count(hash)) {
     251         534 :         pendingInstantSendLocks.emplace(hash, instantsend::PendingISLockFromPeer{id, islock});
     252         534 :     }
     253         538 : }
     254             : 
     255         851 : std::unordered_map<const CBlockIndex*, Uint256HashMap<CTransactionRef>> CInstantSendManager::RetrieveISConflicts(
     256             :     const uint256& islockHash, const instantsend::InstantSendLock& islock)
     257             : {
     258             :     // Lets first collect all non-locked TXs which conflict with the given ISLOCK
     259         851 :     std::unordered_map<const CBlockIndex*, Uint256HashMap<CTransactionRef>> conflicts;
     260             :     {
     261         851 :         LOCK(cs_nonLocked);
     262        3208 :         for (const auto& in : islock.inputs) {
     263        2357 :             auto it = nonLockedTxsByOutpoints.find(in);
     264        2357 :             if (it != nonLockedTxsByOutpoints.end()) {
     265          26 :                 auto& conflictTxid = it->second;
     266          26 :                 if (conflictTxid == islock.txid) {
     267           0 :                     continue;
     268             :                 }
     269          26 :                 auto jt = nonLockedTxs.find(conflictTxid);
     270          26 :                 if (jt == nonLockedTxs.end()) {
     271           0 :                     continue;
     272             :                 }
     273          26 :                 auto& info = jt->second;
     274          26 :                 if (!info.pindexMined || !info.tx) {
     275          16 :                     continue;
     276             :                 }
     277          10 :                 LogPrintf("CInstantSendManager::%s -- txid=%s, islock=%s: mined TX %s with input %s and mined in block %s conflicts with islock\n", __func__,
     278             :                           islock.txid.ToString(), islockHash.ToString(), conflictTxid.ToString(), in.ToStringShort(), info.pindexMined->GetBlockHash().ToString());
     279          10 :                 conflicts[info.pindexMined].emplace(conflictTxid, info.tx);
     280          10 :             }
     281             :         }
     282         851 :     }
     283             : 
     284         851 :     return conflicts;
     285         851 : }
     286             : 
     287         851 : bool CInstantSendManager::HasTxForLock(const uint256& islockHash) const
     288             : {
     289         851 :     LOCK(cs_pendingLocks);
     290         851 :     return pendingNoTxInstantSendLocks.find(islockHash) == pendingNoTxInstantSendLocks.end();
     291         851 : }
     292             : 
     293          40 : void CInstantSendManager::RemoveConflictingLock(const uint256& islockHash, const instantsend::InstantSendLock& islock)
     294             : {
     295          40 :     LogPrintf("CInstantSendManager::%s -- txid=%s, islock=%s: Removing ISLOCK and its chained children\n", __func__,
     296             :               islock.txid.ToString(), islockHash.ToString());
     297          40 :     const int tipHeight = GetTipHeight();
     298             : 
     299          40 :     auto removedIslocks = db.RemoveChainedInstantSendLocks(islockHash, islock.txid, tipHeight);
     300         110 :     for (const auto& h : removedIslocks) {
     301          70 :         LogPrintf("CInstantSendManager::%s -- txid=%s, islock=%s: removed (child) ISLOCK %s\n", __func__,
     302             :                   islock.txid.ToString(), islockHash.ToString(), h.ToString());
     303             :     }
     304          40 : }
     305             : 
     306        1350 : bool CInstantSendManager::AlreadyHave(const CInv& inv) const
     307             : {
     308        1350 :     if (!IsInstantSendEnabled()) {
     309           0 :         return true;
     310             :     }
     311             : 
     312        3929 :     return WITH_LOCK(cs_pendingLocks, return pendingInstantSendLocks.count(inv.hash) != 0 ||
     313        1350 :                                              pendingNoTxInstantSendLocks.count(inv.hash) != 0) ||
     314        1229 :            db.KnownInstantSendLock(inv.hash);
     315        1350 : }
     316             : 
     317         333 : bool CInstantSendManager::GetInstantSendLockByHash(const uint256& hash, instantsend::InstantSendLock& ret) const
     318             : {
     319         333 :     if (!IsInstantSendEnabled()) {
     320           0 :         return false;
     321             :     }
     322             : 
     323         333 :     auto islock = db.GetInstantSendLockByHash(hash);
     324         333 :     if (!islock) {
     325           8 :         LOCK(cs_pendingLocks);
     326           8 :         auto it = pendingInstantSendLocks.find(hash);
     327           8 :         if (it != pendingInstantSendLocks.end()) {
     328           0 :             islock = it->second.islock;
     329           0 :         } else {
     330           8 :             auto itNoTx = pendingNoTxInstantSendLocks.find(hash);
     331           8 :             if (itNoTx != pendingNoTxInstantSendLocks.end()) {
     332           8 :                 islock = itNoTx->second.islock;
     333           8 :             } else {
     334           0 :                 return false;
     335             :             }
     336             :         }
     337           8 :     }
     338         333 :     ret = *islock;
     339         333 :     return true;
     340         333 : }
     341             : 
     342        2277 : instantsend::InstantSendLockPtr CInstantSendManager::GetInstantSendLockByTxid(const uint256& txid) const
     343             : {
     344        2277 :     if (!IsInstantSendEnabled()) {
     345        2157 :         return nullptr;
     346             :     }
     347             : 
     348         120 :     return db.GetInstantSendLockByTxid(txid);
     349        2277 : }
     350             : 
     351     2404202 : bool CInstantSendManager::IsLocked(const uint256& txHash) const
     352             : {
     353     2404202 :     if (!IsInstantSendEnabled()) {
     354     1722213 :         return false;
     355             :     }
     356             : 
     357      681989 :     return db.KnownInstantSendLock(db.GetInstantSendLockHashByTxid(txHash));
     358     2404202 : }
     359             : 
     360      131790 : bool CInstantSendManager::IsWaitingForTx(const uint256& txHash) const
     361             : {
     362      131790 :     if (!IsInstantSendEnabled()) {
     363      124660 :         return false;
     364             :     }
     365             : 
     366        7130 :     LOCK(cs_pendingLocks);
     367        7130 :     auto it = pendingNoTxInstantSendLocks.begin();
     368        7173 :     while (it != pendingNoTxInstantSendLocks.end()) {
     369         142 :         if (it->second.islock->txid == txHash) {
     370          99 :             LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- txid=%s, islock=%s\n", __func__, txHash.ToString(),
     371             :                      it->first.ToString());
     372          99 :             return true;
     373             :         }
     374          43 :         ++it;
     375             :     }
     376        7031 :     return false;
     377      131790 : }
     378             : 
     379      502713 : instantsend::InstantSendLockPtr CInstantSendManager::GetConflictingLock(const CTransaction& tx) const
     380             : {
     381      502713 :     if (!IsInstantSendEnabled()) {
     382      418719 :         return nullptr;
     383             :     }
     384             : 
     385      178601 :     for (const auto& in : tx.vin) {
     386       94740 :         auto otherIsLock = db.GetInstantSendLockByInput(in.prevout);
     387       94740 :         if (!otherIsLock) {
     388       92982 :             continue;
     389             :         }
     390             : 
     391        1758 :         if (otherIsLock->txid != tx.GetHash()) {
     392         133 :             return otherIsLock;
     393             :         }
     394       94740 :     }
     395       83861 :     return nullptr;
     396      502713 : }
     397             : 
     398        4238 : size_t CInstantSendManager::GetInstantSendLockCount() const
     399             : {
     400        4238 :     return db.GetInstantSendLockCount();
     401             : }
     402             : 
     403           0 : CInstantSendManager::Counts CInstantSendManager::GetCounts() const
     404             : {
     405           0 :     Counts ret;
     406           0 :     ret.m_verified = db.GetInstantSendLockCount();
     407             :     {
     408           0 :         LOCK(cs_pendingLocks);
     409           0 :         ret.m_unverified = pendingInstantSendLocks.size();
     410           0 :         ret.m_awaiting_tx = pendingNoTxInstantSendLocks.size();
     411           0 :     }
     412             :     {
     413           0 :         LOCK(cs_nonLocked);
     414           0 :         ret.m_unprotected_tx = nonLockedTxs.size();
     415           0 :     }
     416           0 :     return ret;
     417             : }
     418             : 
     419        1534 : void CInstantSendManager::CacheBlockHeight(const CBlockIndex* const block_index) const
     420             : {
     421        1534 :     LOCK(cs_height_cache);
     422        1534 :     m_cached_block_heights.insert(block_index->GetBlockHash(), block_index->nHeight);
     423        1534 : }
     424             : 
     425       14229 : void CInstantSendManager::CacheDisconnectBlock(const CBlockIndex* pindexDisconnected)
     426             : {
     427       14229 :     LOCK(cs_height_cache);
     428       14229 :     m_cached_block_heights.erase(pindexDisconnected->GetBlockHash());
     429       14229 : }
     430             : 
     431        6501 : std::optional<int> CInstantSendManager::GetCachedHeight(const uint256& hash) const
     432             : {
     433        6501 :     LOCK(cs_height_cache);
     434             : 
     435        6501 :     int cached_height{0};
     436        6501 :     if (m_cached_block_heights.get(hash, cached_height)) return cached_height;
     437             : 
     438        1534 :     return std::nullopt;
     439        6501 : }
     440             : 
     441      304631 : void CInstantSendManager::CacheTipHeight(const CBlockIndex* const tip) const
     442             : {
     443      304631 :     LOCK(cs_height_cache);
     444      304631 :     if (tip) {
     445      304631 :         m_cached_block_heights.insert(tip->GetBlockHash(), tip->nHeight);
     446      304631 :         m_cached_tip_height = tip->nHeight;
     447      304631 :     } else {
     448           0 :         m_cached_tip_height = -1;
     449             :     }
     450      304631 : }
     451             : 
     452        5921 : int CInstantSendManager::GetTipHeight() const
     453             : {
     454             :     // It returns the cached tip height which is updated through notification mechanism
     455             :     // If cached tip is not set by any reason, it's okay to return 0 because
     456             :     // chainstate is not fully loaded yet and tip is not set
     457        5921 :     LOCK(cs_height_cache);
     458        5921 :     if (m_cached_tip_height >= 0) {
     459        5921 :         return m_cached_tip_height;
     460             :     }
     461           0 :     return 0;
     462        5921 : }
     463             : 
     464     4088840 : bool CInstantSendManager::IsInstantSendEnabled() const
     465             : {
     466     4088840 :     return !fReindex && !fImporting && spork_manager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED);
     467             : }
     468             : 
     469       15581 : Uint256HashMap<instantsend::InstantSendLockPtr> CInstantSendManager::RemoveConfirmedInstantSendLocks(const CBlockIndex* pindex)
     470             : {
     471       15581 :     int nUntilHeight = pindex->nHeight;
     472       15581 :     auto removeISLocks = db.RemoveConfirmedInstantSendLocks(nUntilHeight);
     473             : 
     474       15581 :     db.RemoveArchivedInstantSendLocks(nUntilHeight - 100);
     475             : 
     476             :     // Find all previously unlocked TXs that got locked by this fully confirmed (ChainLock) block and remove them
     477             :     // from the nonLockedTxs map. Also collect all children of these TXs and mark them for retrying of IS locking.
     478       15581 :     std::vector<uint256> toRemove;
     479             :     {
     480       15581 :         LOCK(cs_nonLocked);
     481       21224 :         for (const auto& p : nonLockedTxs) {
     482        5643 :             const auto* pindexMined = p.second.pindexMined;
     483             : 
     484        5643 :             if (pindexMined && pindex->GetAncestor(pindexMined->nHeight) == pindexMined) {
     485        1106 :                 toRemove.emplace_back(p.first);
     486        1106 :             }
     487             :         }
     488       15581 :     }
     489       16687 :     for (const auto& txid : toRemove) {
     490             :         // This will also add children to pendingRetryTxs
     491        1106 :         RemoveNonLockedTx(txid, true);
     492             :     }
     493             : 
     494       15581 :     return removeISLocks;
     495       15581 : }
     496             : } // namespace llmq

Generated by: LCOV version 1.16