LCOV - code coverage report
Current view: top level - src/coinjoin - coinjoin.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 105 302 34.8 %
Date: 2026-06-25 07:23:51 Functions: 23 41 56.1 %

          Line data    Source code
       1             : // Copyright (c) 2014-2025 The Dash Core developers
       2             : // Distributed under the MIT/X11 software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <coinjoin/coinjoin.h>
       6             : 
       7             : #include <bls/bls.h>
       8             : #include <chainlock/chainlock.h>
       9             : #include <instantsend/instantsend.h>
      10             : #include <util/helpers.h>
      11             : 
      12             : #include <chain.h>
      13             : #include <chainparams.h>
      14             : #include <txmempool.h>
      15             : #include <util/moneystr.h>
      16             : #include <util/system.h>
      17             : #include <util/translation.h>
      18             : #include <validation.h>
      19             : #include <tinyformat.h>
      20             : 
      21             : #include <ranges>
      22             : #include <string>
      23             : 
      24             : constexpr static CAmount DEFAULT_MAX_RAW_TX_FEE{COIN / 10};
      25             : 
      26           0 : bool CCoinJoinEntry::AddScriptSig(const CTxIn& txin)
      27             : {
      28           0 :     for (auto& txdsin : vecTxDSIn) {
      29           0 :         if (txdsin.prevout == txin.prevout && txdsin.nSequence == txin.nSequence) {
      30           0 :             if (txdsin.fHasSig) return false;
      31             : 
      32           0 :             txdsin.scriptSig = txin.scriptSig;
      33           0 :             txdsin.fHasSig = true;
      34             : 
      35           0 :             return true;
      36             :         }
      37             :     }
      38             : 
      39           0 :     return false;
      40           0 : }
      41             : 
      42           4 : uint256 CCoinJoinQueue::GetSignatureHash() const
      43             : {
      44           4 :     return SerializeHash(*this, SER_GETHASH, PROTOCOL_VERSION);
      45             : }
      46           2 : uint256 CCoinJoinQueue::GetHash() const { return SerializeHash(*this, SER_NETWORK, PROTOCOL_VERSION); }
      47             : 
      48           1 : bool CCoinJoinQueue::CheckSignature(const CBLSPublicKey& blsPubKey) const
      49             : {
      50           1 :     if (!CBLSSignature(Span{vchSig}, false).VerifyInsecure(blsPubKey, GetSignatureHash(), false)) {
      51           0 :         LogPrint(BCLog::COINJOIN, "CCoinJoinQueue::CheckSignature -- VerifyInsecure() failed\n");
      52           0 :         return false;
      53             :     }
      54             : 
      55           1 :     return true;
      56           1 : }
      57             : 
      58          17 : bool CCoinJoinQueue::IsTimeOutOfBounds(int64_t current_time) const
      59             : {
      60          17 :     if (current_time < 0 || nTime < 0) return true;
      61          13 :     return current_time - nTime > COINJOIN_QUEUE_TIMEOUT ||
      62           9 :            nTime - current_time > COINJOIN_QUEUE_TIMEOUT;
      63          17 : }
      64             : 
      65           1 : [[nodiscard]] std::string CCoinJoinQueue::ToString() const
      66             : {
      67           1 :     return strprintf("nDenom=%d, nTime=%lld, fReady=%s, fTried=%s, masternode=%s",
      68           1 :         nDenom, nTime, util::to_string(fReady), util::to_string(fTried), masternodeOutpoint.ToStringShort());
      69           0 : }
      70             : 
      71           0 : uint256 CCoinJoinBroadcastTx::GetSignatureHash() const
      72             : {
      73           0 :     return SerializeHash(*this, SER_GETHASH, PROTOCOL_VERSION);
      74             : }
      75             : 
      76           0 : bool CCoinJoinBroadcastTx::CheckSignature(const CBLSPublicKey& blsPubKey) const
      77             : {
      78           0 :     if (!CBLSSignature(Span{vchSig}, false).VerifyInsecure(blsPubKey, GetSignatureHash(), false)) {
      79           0 :         LogPrint(BCLog::COINJOIN, "CCoinJoinBroadcastTx::CheckSignature -- VerifyInsecure() failed\n");
      80           0 :         return false;
      81             :     }
      82             : 
      83           0 :     return true;
      84           0 : }
      85             : 
      86           5 : bool CCoinJoinBroadcastTx::IsValidStructure() const
      87             : {
      88             :     // some trivial checks only
      89           5 :     if (masternodeOutpoint.IsNull() && m_protxHash.IsNull()) {
      90           1 :         return false;
      91             :     }
      92           4 :     if (tx->vin.size() != tx->vout.size()) {
      93           1 :         return false;
      94             :     }
      95           3 :     if (tx->vin.size() < size_t(CoinJoin::GetMinPoolParticipants())) {
      96           0 :         return false;
      97             :     }
      98           3 :     if (tx->vin.size() > CoinJoin::GetMaxPoolParticipants() * COINJOIN_ENTRY_MAX_SIZE) {
      99           0 :         return false;
     100             :     }
     101           8 :     return std::ranges::all_of(tx->vout, [](const auto& txOut) {
     102           5 :         return CoinJoin::IsDenominatedAmount(txOut.nValue) && txOut.scriptPubKey.IsPayToPublicKeyHash();
     103             :     });
     104           5 : }
     105             : 
     106           0 : void CCoinJoinBaseSession::SetNull()
     107             : {
     108             :     // Both sides
     109           0 :     AssertLockHeld(cs_coinjoin);
     110           0 :     nState = POOL_STATE_IDLE;
     111           0 :     nSessionID = 0;
     112           0 :     nSessionDenom = 0;
     113           0 :     vecEntries.clear();
     114           0 :     finalMutableTransaction.vin.clear();
     115           0 :     finalMutableTransaction.vout.clear();
     116           0 :     nTimeLastSuccessfulStep = GetTime();
     117           0 : }
     118             : 
     119           0 : void CoinJoinQueueManager::SetNull()
     120             : {
     121           0 :     LOCK(cs_vecqueue);
     122           0 :     vecCoinJoinQueue.clear();
     123           0 : }
     124             : 
     125           1 : void CoinJoinQueueManager::CheckQueue()
     126             : {
     127           1 :     TRY_LOCK(cs_vecqueue, lockDS);
     128           1 :     if (!lockDS) return; // it's ok to fail here, we run this quite frequently
     129             : 
     130             :     // check mixing queue objects for timeouts
     131           1 :     auto it = vecCoinJoinQueue.begin();
     132           3 :     while (it != vecCoinJoinQueue.end()) {
     133           2 :         if (it->IsTimeOutOfBounds()) {
     134           1 :             LogPrint(BCLog::COINJOIN, "CoinJoinQueueManager::%s -- Removing a queue (%s)\n", __func__, it->ToString());
     135           1 :             it = vecCoinJoinQueue.erase(it);
     136           1 :         } else {
     137           1 :             ++it;
     138             :         }
     139             :     }
     140           1 : }
     141             : 
     142           0 : std::optional<bool> CoinJoinQueueManager::TryHasQueueFromMasternode(const COutPoint& outpoint) const
     143             : {
     144           0 :     TRY_LOCK(cs_vecqueue, lockDS);
     145           0 :     if (!lockDS) return std::nullopt;
     146           0 :     return std::ranges::any_of(vecCoinJoinQueue, [&outpoint](const auto& q) { return q.masternodeOutpoint == outpoint; });
     147           0 : }
     148             : 
     149           0 : std::optional<bool> CoinJoinQueueManager::TryCheckDuplicate(const CCoinJoinQueue& dsq) const
     150             : {
     151           0 :     TRY_LOCK(cs_vecqueue, lockDS);
     152           0 :     if (!lockDS) return std::nullopt;
     153           0 :     for (const auto& q : vecCoinJoinQueue) {
     154           0 :         if (q == dsq) return true;
     155           0 :         if (q.fReady == dsq.fReady && q.masternodeOutpoint == dsq.masternodeOutpoint) return true;
     156             :     }
     157           0 :     return false;
     158           0 : }
     159             : 
     160           0 : bool CoinJoinQueueManager::TryAddQueue(CCoinJoinQueue dsq)
     161             : {
     162           0 :     TRY_LOCK(cs_vecqueue, lockDS);
     163           0 :     if (!lockDS) return false;
     164           0 :     vecCoinJoinQueue.push_back(std::move(dsq));
     165           0 :     return true;
     166           0 : }
     167             : 
     168           2 : bool CoinJoinQueueManager::GetQueueItemAndTry(CCoinJoinQueue& dsqRet)
     169             : {
     170           2 :     TRY_LOCK(cs_vecqueue, lockDS);
     171           2 :     if (!lockDS) return false; // it's ok to fail here, we run this quite frequently
     172             : 
     173           3 :     for (auto& dsq : vecCoinJoinQueue) {
     174             :         // only try each queue once
     175           2 :         if (dsq.fTried || dsq.IsTimeOutOfBounds()) continue;
     176           1 :         dsq.fTried = true;
     177           1 :         dsqRet = dsq;
     178           1 :         return true;
     179             :     }
     180             : 
     181           1 :     return false;
     182           2 : }
     183             : 
     184           0 : std::string CCoinJoinBaseSession::GetStateString() const
     185             : {
     186           0 :     switch (nState) {
     187             :     case POOL_STATE_IDLE:
     188           0 :         return "IDLE";
     189             :     case POOL_STATE_QUEUE:
     190           0 :         return "QUEUE";
     191             :     case POOL_STATE_ACCEPTING_ENTRIES:
     192           0 :         return "ACCEPTING_ENTRIES";
     193             :     case POOL_STATE_SIGNING:
     194           0 :         return "SIGNING";
     195             :     case POOL_STATE_ERROR:
     196           0 :         return "ERROR";
     197             :     default:
     198           0 :         return "UNKNOWN";
     199             :     }
     200           0 : }
     201             : 
     202           0 : bool CCoinJoinBaseSession::IsValidInOuts(CChainState& active_chainstate, const llmq::CInstantSendManager& isman,
     203             :                                          const CTxMemPool& mempool, const std::vector<CTxIn>& vin,
     204             :                                          const std::vector<CTxOut>& vout, PoolMessage& nMessageIDRet,
     205             :                                          bool* fConsumeCollateralRet) const
     206             : {
     207           0 :     std::set<CScript> setScripPubKeys;
     208           0 :     nMessageIDRet = MSG_NOERR;
     209           0 :     if (fConsumeCollateralRet) *fConsumeCollateralRet = false;
     210             : 
     211           0 :     if (vin.size() != vout.size()) {
     212           0 :         LogPrint(BCLog::COINJOIN, "CCoinJoinBaseSession::%s -- ERROR: inputs vs outputs size mismatch! %d vs %d\n", __func__, vin.size(), vout.size());
     213           0 :         nMessageIDRet = ERR_SIZE_MISMATCH;
     214           0 :         if (fConsumeCollateralRet) *fConsumeCollateralRet = true;
     215           0 :         return false;
     216             :     }
     217             : 
     218           0 :     auto checkTxOut = [&](const CTxOut& txout) {
     219           0 :         if (int nDenom = CoinJoin::AmountToDenomination(txout.nValue); nDenom != nSessionDenom) {
     220           0 :             LogPrint(BCLog::COINJOIN, "CCoinJoinBaseSession::IsValidInOuts -- ERROR: incompatible denom %d (%s) != nSessionDenom %d (%s)\n",
     221             :                     nDenom, CoinJoin::DenominationToString(nDenom), nSessionDenom, CoinJoin::DenominationToString(nSessionDenom));
     222           0 :             nMessageIDRet = ERR_DENOM;
     223           0 :             if (fConsumeCollateralRet) *fConsumeCollateralRet = true;
     224           0 :             return false;
     225             :         }
     226           0 :         if (!txout.scriptPubKey.IsPayToPublicKeyHash()) {
     227           0 :             LogPrint(BCLog::COINJOIN, "CCoinJoinBaseSession::IsValidInOuts -- ERROR: invalid script! scriptPubKey=%s\n", ScriptToAsmStr(txout.scriptPubKey));
     228           0 :             nMessageIDRet = ERR_INVALID_SCRIPT;
     229           0 :             if (fConsumeCollateralRet) *fConsumeCollateralRet = true;
     230           0 :             return false;
     231             :         }
     232           0 :         if (!setScripPubKeys.insert(txout.scriptPubKey).second) {
     233           0 :             LogPrint(BCLog::COINJOIN, "CCoinJoinBaseSession::IsValidInOuts -- ERROR: already have this script! scriptPubKey=%s\n", ScriptToAsmStr(txout.scriptPubKey));
     234           0 :             nMessageIDRet = ERR_ALREADY_HAVE;
     235           0 :             if (fConsumeCollateralRet) *fConsumeCollateralRet = true;
     236           0 :             return false;
     237             :         }
     238             :         // IsPayToPublicKeyHash() above already checks for scriptPubKey size,
     239             :         // no need to double-check, hence no usage of ERR_NON_STANDARD_PUBKEY
     240           0 :         return true;
     241           0 :     };
     242             : 
     243           0 :     CAmount nFees{0};
     244             : 
     245           0 :     for (const auto& txout : vout) {
     246           0 :         if (!checkTxOut(txout)) {
     247           0 :             return false;
     248             :         }
     249           0 :         nFees -= txout.nValue;
     250             :     }
     251             : 
     252           0 :     CCoinsViewMemPool viewMemPool(WITH_LOCK(::cs_main, return &active_chainstate.CoinsTip()), mempool);
     253             : 
     254           0 :     for (const auto& txin : vin) {
     255           0 :         LogPrint(BCLog::COINJOIN, "CCoinJoinBaseSession::%s -- txin=%s\n", __func__, txin.ToString());
     256             : 
     257           0 :         if (txin.prevout.IsNull()) {
     258           0 :             LogPrint(BCLog::COINJOIN, "CCoinJoinBaseSession::%s -- ERROR: invalid input!\n", __func__);
     259           0 :             nMessageIDRet = ERR_INVALID_INPUT;
     260           0 :             if (fConsumeCollateralRet) *fConsumeCollateralRet = true;
     261           0 :             return false;
     262             :         }
     263             : 
     264           0 :         Coin coin;
     265           0 :         if (!viewMemPool.GetCoin(txin.prevout, coin) || coin.IsSpent() ||
     266           0 :             (coin.nHeight == MEMPOOL_HEIGHT && !isman.IsLocked(txin.prevout.hash))) {
     267           0 :             LogPrint(BCLog::COINJOIN, "CCoinJoinBaseSession::%s -- ERROR: missing, spent or non-locked mempool input! txin=%s\n", __func__, txin.ToString());
     268           0 :             nMessageIDRet = ERR_MISSING_TX;
     269           0 :             return false;
     270             :         }
     271             : 
     272           0 :         if (!checkTxOut(coin.out)) {
     273           0 :             return false;
     274             :         }
     275             : 
     276           0 :         nFees += coin.out.nValue;
     277           0 :     }
     278             : 
     279             :     // The same size and denom for inputs and outputs ensures their total value is also the same,
     280             :     // no need to double-check. If not, we are doing something wrong, bail out.
     281           0 :     if (nFees != 0) {
     282           0 :         LogPrint(BCLog::COINJOIN, "CCoinJoinBaseSession::%s -- ERROR: non-zero fees! fees: %lld\n", __func__, nFees);
     283           0 :         nMessageIDRet = ERR_FEES;
     284           0 :         return false;
     285             :     }
     286             : 
     287           0 :     return true;
     288           0 : }
     289             : 
     290             : // Responsibility for checking fee sanity is moved from the mempool to the client (BroadcastTransaction)
     291             : // but CoinJoin still requires ATMP with fee sanity checks so we need to implement them separately
     292           0 : bool ATMPIfSaneFee(ChainstateManager& chainman, const CTransactionRef& tx, bool test_accept)
     293             : {
     294           0 :     AssertLockHeld(::cs_main);
     295             : 
     296           0 :     const MempoolAcceptResult result = chainman.ProcessTransaction(tx, /*test_accept=*/true);
     297           0 :     if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
     298             :         /* Fetch fee and fast-fail if ATMP fails regardless */
     299           0 :         return false;
     300           0 :     } else if (result.m_base_fees.value() > DEFAULT_MAX_RAW_TX_FEE) {
     301             :         /* Check fee against fixed upper limit */
     302           0 :         return false;
     303           0 :     } else if (test_accept) {
     304             :         /* Don't re-run ATMP if only doing test run */
     305           0 :         return true;
     306             :     }
     307           0 :     return chainman.ProcessTransaction(tx, test_accept).m_result_type == MempoolAcceptResult::ResultType::VALID;
     308           0 : }
     309             : 
     310             : // check to make sure the collateral provided by the client is valid
     311           0 : bool CoinJoin::IsCollateralValid(ChainstateManager& chainman, const llmq::CInstantSendManager& isman,
     312             :                                  const CTxMemPool& mempool, const CTransaction& txCollateral)
     313             : {
     314           0 :     if (txCollateral.vout.empty()) return false;
     315           0 :     if (txCollateral.nLockTime != 0) return false;
     316             : 
     317           0 :     CAmount nValueIn = 0;
     318           0 :     CAmount nValueOut = 0;
     319             : 
     320           0 :     for (const auto& txout : txCollateral.vout) {
     321           0 :         nValueOut += txout.nValue;
     322             : 
     323           0 :         if (!txout.scriptPubKey.IsPayToPublicKeyHash() && !txout.scriptPubKey.IsUnspendable()) {
     324           0 :             LogPrint(BCLog::COINJOIN, "CoinJoin::IsCollateralValid -- Invalid Script, txCollateral=%s", txCollateral.ToString()); /* Continued */
     325           0 :             return false;
     326             :         }
     327             :     }
     328             : 
     329           0 :     LOCK(::cs_main);
     330           0 :     CCoinsViewMemPool viewMemPool(&chainman.ActiveChainstate().CoinsTip(), mempool);
     331             : 
     332           0 :     for (const auto& txin : txCollateral.vin) {
     333           0 :         Coin coin;
     334           0 :         if (!viewMemPool.GetCoin(txin.prevout, coin) || coin.IsSpent() ||
     335           0 :             (coin.nHeight == MEMPOOL_HEIGHT && !isman.IsLocked(txin.prevout.hash))) {
     336           0 :             LogPrint(BCLog::COINJOIN, "CoinJoin::IsCollateralValid -- missing, spent or non-locked mempool input! txin=%s\n", txin.ToString());
     337           0 :             return false;
     338             :         }
     339           0 :         nValueIn += coin.out.nValue;
     340           0 :     }
     341             : 
     342             :     //collateral transactions are required to pay out a small fee to the miners
     343           0 :     if (nValueIn - nValueOut < GetCollateralAmount()) {
     344           0 :         LogPrint(BCLog::COINJOIN, "CoinJoin::IsCollateralValid -- did not include enough fees in transaction: fees: %d, txCollateral=%s", nValueOut - nValueIn, txCollateral.ToString()); /* Continued */
     345           0 :         return false;
     346             :     }
     347             : 
     348           0 :     LogPrint(BCLog::COINJOIN, "CoinJoin::IsCollateralValid -- %s", txCollateral.ToString()); /* Continued */
     349             : 
     350           0 :     if (!ATMPIfSaneFee(chainman, MakeTransactionRef(txCollateral), /*test_accept=*/true)) {
     351           0 :         LogPrint(BCLog::COINJOIN, "CoinJoin::IsCollateralValid -- didn't pass ATMPIfSaneFee()\n");
     352           0 :         return false;
     353             :     }
     354             : 
     355           0 :     return true;
     356           0 : }
     357             : 
     358           0 : bilingual_str CoinJoin::GetMessageByID(PoolMessage nMessageID)
     359             : {
     360           0 :     switch (nMessageID) {
     361             :     case ERR_ALREADY_HAVE:
     362           0 :         return _("Already have that input.");
     363             :     case ERR_DENOM:
     364           0 :         return _("No matching denominations found for mixing.");
     365             :     case ERR_ENTRIES_FULL:
     366           0 :         return _("Entries are full.");
     367             :     case ERR_EXISTING_TX:
     368           0 :         return _("Not compatible with existing transactions.");
     369             :     case ERR_FEES:
     370           0 :         return _("Transaction fees are too high.");
     371             :     case ERR_INVALID_COLLATERAL:
     372           0 :         return _("Collateral not valid.");
     373             :     case ERR_INVALID_INPUT:
     374           0 :         return _("Input is not valid.");
     375             :     case ERR_INVALID_SCRIPT:
     376           0 :         return _("Invalid script detected.");
     377             :     case ERR_INVALID_TX:
     378           0 :         return _("Transaction not valid.");
     379             :     case ERR_MAXIMUM:
     380           0 :         return _("Entry exceeds maximum size.");
     381             :     case ERR_MN_LIST:
     382           0 :         return _("Not in the Masternode list.");
     383             :     case ERR_MODE:
     384           0 :         return _("Incompatible mode.");
     385             :     case ERR_QUEUE_FULL:
     386           0 :         return _("Masternode queue is full.");
     387             :     case ERR_RECENT:
     388           0 :         return _("Last queue was created too recently.");
     389             :     case ERR_SESSION:
     390           0 :         return _("Session not complete!");
     391             :     case ERR_MISSING_TX:
     392           0 :         return _("Missing input transaction information.");
     393             :     case ERR_VERSION:
     394           0 :         return _("Incompatible version.");
     395             :     case MSG_NOERR:
     396           0 :         return _("No errors detected.");
     397             :     case MSG_SUCCESS:
     398           0 :         return _("Transaction created successfully.");
     399             :     case MSG_ENTRIES_ADDED:
     400           0 :         return _("Your entries added successfully.");
     401             :     case ERR_SIZE_MISMATCH:
     402           0 :         return _("Inputs vs outputs size mismatch.");
     403             :     case ERR_NON_STANDARD_PUBKEY:
     404           0 :     case ERR_NOT_A_MN:
     405             :     default:
     406           0 :         return _("Unknown response.");
     407             :     }
     408           0 : }
     409             : 
     410         534 : CDSTXManager::CDSTXManager(const chainlock::Chainlocks& chainlocks) :
     411         178 :     m_chainlocks{chainlocks}
     412         178 : {
     413         356 : }
     414         356 : CDSTXManager::~CDSTXManager() = default;
     415             : 
     416           3 : void CDSTXManager::AddDSTX(const CCoinJoinBroadcastTx& dstx)
     417             : {
     418           3 :     AssertLockNotHeld(cs_mapdstx);
     419           3 :     LOCK(cs_mapdstx);
     420           3 :     mapDSTX.insert(std::make_pair(dstx.tx->GetHash(), dstx));
     421           3 : }
     422             : 
     423           7 : CCoinJoinBroadcastTx CDSTXManager::GetDSTX(const uint256& hash)
     424             : {
     425           7 :     AssertLockNotHeld(cs_mapdstx);
     426           7 :     LOCK(cs_mapdstx);
     427           7 :     auto it = mapDSTX.find(hash);
     428           7 :     return (it == mapDSTX.end()) ? CCoinJoinBroadcastTx() : it->second;
     429           7 : }
     430             : 
     431           2 : bool CDSTXManager::IsTxExpired(const CCoinJoinBroadcastTx& tx, const CBlockIndex* pindex) const
     432             : {
     433             :     // expire confirmed DSTXes after ~1h since confirmation or chainlocked
     434           2 :     const auto& opt_confirmed_height = tx.GetConfirmedHeight();
     435           2 :     if (!opt_confirmed_height.has_value() || pindex->nHeight < *opt_confirmed_height) return false; // not mined yet
     436           2 :     return (pindex->nHeight - *opt_confirmed_height > 24) ||
     437           1 :            m_chainlocks.HasChainLock(pindex->nHeight, *pindex->phashBlock); // mined more than an hour ago or chainlocked
     438           2 : }
     439             : 
     440           2 : void CDSTXManager::CheckDSTXes(const CBlockIndex* pindex)
     441             : {
     442           2 :     AssertLockNotHeld(cs_mapdstx);
     443           2 :     LOCK(cs_mapdstx);
     444           2 :     auto it = mapDSTX.begin();
     445           4 :     while (it != mapDSTX.end()) {
     446           2 :         if (IsTxExpired(it->second, pindex)) {
     447           1 :             mapDSTX.erase(it++);
     448           1 :         } else {
     449           1 :             ++it;
     450             :         }
     451             :     }
     452           2 :     LogPrint(BCLog::COINJOIN, "CoinJoin::CheckDSTXes -- mapDSTX.size()=%llu\n", mapDSTX.size());
     453           2 : }
     454             : 
     455           2 : void CDSTXManager::UpdatedBlockTip(const CBlockIndex* pindex)
     456             : {
     457           2 :     if (pindex) {
     458           2 :         CheckDSTXes(pindex);
     459           2 :     }
     460           2 : }
     461             : 
     462           0 : void CDSTXManager::NotifyChainLock(const CBlockIndex* pindex)
     463             : {
     464           0 :     if (pindex) {
     465           0 :         CheckDSTXes(pindex);
     466           0 :     }
     467           0 : }
     468             : 
     469           2 : void CDSTXManager::UpdateDSTXConfirmedHeight(const CTransactionRef& tx, std::optional<int> nHeight)
     470             : {
     471           2 :     AssertLockHeld(cs_mapdstx);
     472             : 
     473           2 :     auto it = mapDSTX.find(tx->GetHash());
     474           2 :     if (it == mapDSTX.end()) {
     475           0 :         return;
     476             :     }
     477             : 
     478           2 :     it->second.SetConfirmedHeight(nHeight);
     479           2 :     LogPrint(BCLog::COINJOIN, "CDSTXManager::%s -- txid=%s, nHeight=%d\n", __func__, tx->GetHash().ToString(), nHeight.value_or(-1));
     480           2 : }
     481             : 
     482           0 : void CDSTXManager::TransactionAddedToMempool(const CTransactionRef& tx)
     483             : {
     484           0 :     AssertLockNotHeld(cs_mapdstx);
     485           0 :     LOCK(cs_mapdstx);
     486           0 :     UpdateDSTXConfirmedHeight(tx, std::nullopt);
     487           0 : }
     488             : 
     489           1 : void CDSTXManager::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex)
     490             : {
     491           1 :     AssertLockNotHeld(cs_mapdstx);
     492           1 :     LOCK(cs_mapdstx);
     493             : 
     494           2 :     for (const auto& tx : pblock->vtx) {
     495           1 :         UpdateDSTXConfirmedHeight(tx, pindex->nHeight);
     496             :     }
     497           1 : }
     498             : 
     499           1 : void CDSTXManager::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex*)
     500             : {
     501           1 :     AssertLockNotHeld(cs_mapdstx);
     502           1 :     LOCK(cs_mapdstx);
     503           2 :     for (const auto& tx : pblock->vtx) {
     504           1 :         UpdateDSTXConfirmedHeight(tx, std::nullopt);
     505             :     }
     506           1 : }
     507             : 
     508           5 : int CoinJoin::GetMinPoolParticipants() { return Params().PoolMinParticipants(); }
     509           3 : int CoinJoin::GetMaxPoolParticipants() { return Params().PoolMaxParticipants(); }

Generated by: LCOV version 1.16