LCOV - code coverage report
Current view: top level - src/llmq - quorums_blockprocessor.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 202 225 89.8 %
Date: 2025-02-23 09:33:43 Functions: 23 25 92.0 %

          Line data    Source code
       1             : // Copyright (c) 2018-2021 The Dash Core developers
       2             : // Copyright (c) 2021-2022 The PIVX Core developers
       3             : // Distributed under the MIT/X11 software license, see the accompanying
       4             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #include "llmq/quorums_blockprocessor.h"
       7             : 
       8             : #include "bls/key_io.h"
       9             : #include "chain.h"
      10             : #include "chainparams.h"
      11             : #include "consensus/validation.h"
      12             : #include "evo/evodb.h"
      13             : #include "evo/specialtx_validation.h"
      14             : #include "llmq/quorums_utils.h"
      15             : #include "net.h"
      16             : #include "primitives/block.h"
      17             : #include "quorums_debug.h"
      18             : #include "spork.h"
      19             : #include "validation.h"
      20             : 
      21             : namespace llmq
      22             : {
      23             : std::unique_ptr<CQuorumBlockProcessor> quorumBlockProcessor{nullptr};
      24             : 
      25             : static const std::string DB_MINED_COMMITMENT = "q_mc";
      26             : static const std::string DB_MINED_COMMITMENT_BY_INVERSED_HEIGHT = "q_mcih";
      27             : 
      28         475 : CQuorumBlockProcessor::CQuorumBlockProcessor(CEvoDB &_evoDb) :
      29         475 :     evoDb(_evoDb)
      30             : {
      31         475 :     utils::InitQuorumsCache(mapHasMinedCommitmentCache);
      32         475 : }
      33             : 
      34             : template<typename... Args>
      35           2 : static int LogMisbehaving(CNode* pfrom, int nDoS, const char* fmt, const Args&... args)
      36             : {
      37             :     try {
      38           2 :         LogPrint(BCLog::LLMQ, "Invalid QFCOMMITMENT message from peer=%d (reason: %s)\n",
      39             :                 pfrom->GetId(), tfm::format(fmt, args...));
      40           0 :     } catch (tinyformat::format_error &e) {
      41           0 :         LogPrintf("Error (%s) while formatting message %s\n", std::string(e.what()), fmt);
      42             :     }
      43           2 :     return nDoS;
      44             : }
      45             : 
      46         102 : void CQuorumBlockProcessor::ProcessMessage(CNode* pfrom, CDataStream& vRecv, int& retMisbehavingScore)
      47             : {
      48         102 :     AssertLockNotHeld(cs_main);
      49         201 :     CFinalCommitment qc;
      50         102 :     vRecv >> qc;
      51             : 
      52         102 :     uint256 qfc_hash{::SerializeHash(qc)};
      53         102 :     {
      54         102 :         LOCK(cs_main);
      55         102 :         g_connman->RemoveAskFor(qfc_hash, MSG_QUORUM_FINAL_COMMITMENT);
      56             :     }
      57             : 
      58         102 :     if (qc.IsNull()) {
      59           0 :         retMisbehavingScore = LogMisbehaving(pfrom, 100, "null commitment");
      60           3 :         return;
      61             :     }
      62             : 
      63             :     // Check if we already got a better one locally
      64             :     // We do this before verifying the commitment to avoid DoS
      65         102 :     if (HasBetterMinableCommitment(qc)) {
      66             :         return;
      67             :     }
      68             : 
      69         200 :     CValidationState state;
      70         404 :     if (!WITH_LOCK(cs_main, return VerifyLLMQCommitment(qc, chainActive.Tip(), state); )) {
      71             :         // Not punishable reject reasons
      72           2 :         static std::set<std::string> not_punishable_reasons = {
      73             :                 // can't really punish the node for "bad-qc-quorum-hash" here, as we might simply be
      74             :                 // the one that is on the wrong chain or not fully synced
      75             :                 "bad-qc-quorum-hash-not-found", "bad-qc-quorum-hash-not-active-chain"
      76           4 :         };
      77             : 
      78           6 :         int dos = (not_punishable_reasons.count(state.GetRejectReason()) ? 0 : state.GetDoSScore());
      79           4 :         retMisbehavingScore = LogMisbehaving(pfrom, dos, "invalid commtiment for quorum %s: %s",
      80           4 :                                              qc.quorumHash.ToString(), state.GetRejectReason());
      81           2 :         return;
      82             :     }
      83             : 
      84         198 :     LogPrintf("%s :received commitment for quorum %s:%d, validMembers=%d, signers=%d, peer=%d\n", __func__,
      85          99 :               qc.quorumHash.ToString(), qc.llmqType, qc.CountValidMembers(), qc.CountSigners(), pfrom->GetId());
      86             : 
      87          99 :     AddAndRelayMinableCommitment(qc, &qfc_hash);
      88             : }
      89             : 
      90       56426 : bool CQuorumBlockProcessor::ProcessBlock(const CBlock& block, const CBlockIndex* pindex, CValidationState& state, bool fJustCheck)
      91             : {
      92      112852 :     LOCK(cs_main);
      93       56426 :     const auto& consensus = Params().GetConsensus();
      94             : 
      95       56426 :     bool fDIP3Active = consensus.NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_V6_0);
      96       56426 :     if (!fDIP3Active) {
      97             :         return true;
      98             :     }
      99             : 
     100       66439 :     std::map<Consensus::LLMQType, CFinalCommitment> qcs;
     101       10013 :     if (!GetCommitmentsFromBlock(block, pindex, qcs, state)) {
     102             :         return false;
     103             :     }
     104             : 
     105             :     // The following checks make sure that there is always a (possibly null) commitment while in the mining phase
     106             :     // until the first non-null commitment has been mined. After the non-null commitment, no other commitments are
     107             :     // allowed, including null commitments.
     108             :     // Note: must only check quorums that were enabled at the _previous_ block height to match mining logic
     109       20021 :     for (const auto& llmq : consensus.llmqs) {
     110             :         // skip these checks when replaying blocks after the crash
     111       30036 :         if (WITH_LOCK(cs_main, return chainActive.Tip(); ) == nullptr) {
     112             :             break;
     113             :         }
     114             : 
     115             :         // does the currently processed block contain a (possibly null) commitment for the current session?
     116       10012 :         Consensus::LLMQType type = llmq.first;
     117       10012 :         bool hasCommitmentInNewBlock = qcs.count(type) != 0;
     118       10012 :         bool isCommitmentRequired = IsCommitmentRequired(type, pindex->nHeight);
     119             : 
     120       10012 :         if (hasCommitmentInNewBlock && !isCommitmentRequired) {
     121             :             // If we're either not in the mining phase or a non-null commitment was mined already, reject the block
     122           6 :             return state.DoS(100, false, REJECT_INVALID, "bad-qc-not-allowed");
     123             :         }
     124             : 
     125       10010 :         if (!hasCommitmentInNewBlock && isCommitmentRequired) {
     126             :             // If no non-null commitment was mined for the mining phase yet and the new block does not include
     127             :             // a (possibly null) commitment, the block should be rejected.
     128           2 :             return state.DoS(100, false, REJECT_INVALID, "bad-qc-missing");
     129             :         }
     130             :     }
     131             : 
     132       10009 :     const uint256& blockHash = block.GetHash();
     133             : 
     134       12538 :     for (const auto& p : qcs) {
     135        2529 :         const auto& qc = p.second;
     136        2529 :         if (!ProcessCommitment(pindex->nHeight, blockHash, qc, state, fJustCheck)) {
     137           0 :             return false;
     138             :         }
     139             :     }
     140             : 
     141       10009 :     return true;
     142             : }
     143             : 
     144             : // We store a mapping from minedHeight->quorumHeight in the DB
     145             : // minedHeight is inversed so that entries are traversable in reversed order
     146       20979 : static std::tuple<std::string, uint8_t, uint32_t> BuildInversedHeightKey(Consensus::LLMQType llmqType, int nMinedHeight)
     147             : {
     148             :     // nMinedHeight must be converted to big endian to make it comparable when serialized
     149       20979 :     return std::make_tuple(DB_MINED_COMMITMENT_BY_INVERSED_HEIGHT, static_cast<uint8_t>(llmqType), htobe32(std::numeric_limits<uint32_t>::max() - nMinedHeight));
     150             : }
     151             : 
     152        2529 : bool CQuorumBlockProcessor::ProcessCommitment(int nHeight, const uint256& blockHash, const CFinalCommitment& qc, CValidationState& state, bool fJustCheck)
     153             : {
     154             :     // skip `bad-qc-block` checks below when replaying blocks after a crash
     155        7587 :     const uint256& quorumHash = WITH_LOCK(cs_main, return chainActive.Tip(); ) != nullptr
     156        2529 :                               ? GetQuorumBlockHash((Consensus::LLMQType)qc.llmqType, nHeight)
     157        2529 :                               : qc.quorumHash;
     158             : 
     159        5058 :     if (quorumHash.IsNull()) {
     160           0 :         return state.DoS(100, false, REJECT_INVALID, "bad-qc-null-quorumhash");
     161             :     }
     162        2529 :     if (quorumHash != qc.quorumHash) {
     163           0 :         return state.DoS(100, false, REJECT_INVALID, "bad-qc-block");
     164             :     }
     165             : 
     166             :     // index of quorumHash (and commitment signature) already checked
     167        2529 :     const CBlockIndex* quorumIndex = mapBlockIndex.at(quorumHash);
     168             : 
     169        2529 :     if (fJustCheck || qc.IsNull()) {
     170        2380 :         return true;
     171             :     }
     172             : 
     173             :     // Store commitment in DB
     174         149 :     auto cacheKey = std::make_pair(qc.llmqType, quorumHash);
     175         298 :     evoDb.Write(std::make_pair(DB_MINED_COMMITMENT, cacheKey), std::make_pair(qc, blockHash));
     176         149 :     evoDb.Write(BuildInversedHeightKey((Consensus::LLMQType)qc.llmqType, nHeight), quorumIndex->nHeight);
     177             : 
     178         149 :     {
     179         149 :         LOCK(minableCommitmentsCs);
     180         149 :         mapHasMinedCommitmentCache.at((Consensus::LLMQType)qc.llmqType).erase(qc.quorumHash);
     181         149 :         minableCommitmentsByQuorum.erase(cacheKey);
     182         298 :         minableCommitments.erase(::SerializeHash(qc));
     183             :     }
     184             : 
     185         298 :     LogPrintf("%s: processed commitment from block. type=%d, quorumHash=%s, signers=%s, validMembers=%d, quorumPublicKey=%s\n", __func__,
     186         298 :               qc.llmqType, quorumHash.ToString(), qc.CountSigners(), qc.CountValidMembers(), bls::EncodePublic(Params(), qc.quorumPublicKey));
     187             : 
     188         149 :     return true;
     189             : }
     190             : 
     191        1457 : bool CQuorumBlockProcessor::UndoBlock(const CBlock& block, const CBlockIndex* pindex)
     192             : {
     193        2914 :     std::map<Consensus::LLMQType, CFinalCommitment> qcs;
     194        2914 :     CValidationState dummy;
     195        1457 :     if (!GetCommitmentsFromBlock(block, pindex, qcs, dummy)) {
     196             :         return false;
     197             :     }
     198             : 
     199        1467 :     for (const auto& p : qcs) {
     200          10 :         const auto& qc = p.second;
     201          10 :         if (qc.IsNull()) {
     202          10 :             continue;
     203             :         }
     204             : 
     205           0 :         evoDb.Erase(std::make_pair(DB_MINED_COMMITMENT, std::make_pair(static_cast<uint8_t>(qc.llmqType), qc.quorumHash)));
     206           0 :         evoDb.Erase(BuildInversedHeightKey((Consensus::LLMQType)qc.llmqType, pindex->nHeight));
     207           0 :         {
     208           0 :             LOCK(minableCommitmentsCs);
     209           0 :             mapHasMinedCommitmentCache.at((Consensus::LLMQType)qc.llmqType).erase(qc.quorumHash);
     210             :         }
     211             : 
     212             :         // if a reorg happened, we should allow to mine this commitment later
     213           0 :         if (!HasBetterMinableCommitment(qc)) {
     214           0 :             AddAndRelayMinableCommitment(qc);
     215             :         }
     216             :     }
     217             : 
     218        1457 :     return true;
     219             : }
     220             : 
     221       11470 : bool CQuorumBlockProcessor::GetCommitmentsFromBlock(const CBlock& block, const CBlockIndex* pindex, std::map<Consensus::LLMQType, CFinalCommitment>& ret, CValidationState& state)
     222             : {
     223       11470 :     ret.clear();
     224             : 
     225      141256 :     for (const auto& tx : block.vtx) {
     226      129787 :         if (!tx->IsQuorumCommitmentTx()) {
     227      127244 :             continue;
     228             :         }
     229        5085 :         LLMQCommPL pl;
     230        2543 :         if (!GetTxPayload(*tx, pl)) {
     231             :             // should not happen as it was verified before processing the block
     232           0 :             return state.DoS(100, false, REJECT_INVALID, "bad-qc-payload");
     233             :         }
     234             : 
     235             :         // only allow one commitment per type and per block
     236        2543 :         if (ret.count((Consensus::LLMQType)pl.commitment.llmqType)) {
     237           2 :             return state.DoS(100, false, REJECT_INVALID, "bad-qc-dup");
     238             :         }
     239             : 
     240        2542 :         ret.emplace((Consensus::LLMQType)pl.commitment.llmqType, std::move(pl.commitment));
     241             :     }
     242             : 
     243       11469 :     return true;
     244             : }
     245             : 
     246       11125 : bool CQuorumBlockProcessor::IsMiningPhase(Consensus::LLMQType llmqType, int nHeight)
     247             : {
     248       11125 :     const auto& params = Params().GetConsensus().llmqs.at(llmqType);
     249       11125 :     int phaseIndex = nHeight % params.dkgInterval;
     250       11125 :     return phaseIndex >= params.dkgMiningWindowStart && phaseIndex <= params.dkgMiningWindowEnd;
     251             : }
     252             : 
     253       11686 : bool CQuorumBlockProcessor::IsCommitmentRequired(Consensus::LLMQType llmqType, int nHeight)
     254             : {
     255       11686 :     const uint256& quorumHash = GetQuorumBlockHash(llmqType, nHeight);
     256             : 
     257             :     // perform extra check for quorumHash.IsNull as the quorum hash is unknown for the first block of a session
     258             :     // this is because the currently processed block's hash will be the quorumHash of this session
     259       40763 :     bool isMiningPhase = !quorumHash.IsNull() && IsMiningPhase(llmqType, nHeight);
     260             : 
     261             :     // did we already mine a non-null commitment for this session?
     262       23372 :     bool hasMinedCommitment = !quorumHash.IsNull() && HasMinedCommitment(llmqType, quorumHash);
     263             : 
     264       11686 :     return isMiningPhase && !hasMinedCommitment;
     265             : }
     266             : 
     267             : // This method returns UINT256_ZERO on the first block of the DKG interval (because the block hash is not known yet)
     268       14620 : uint256 CQuorumBlockProcessor::GetQuorumBlockHash(Consensus::LLMQType llmqType, int nHeight)
     269             : {
     270       14620 :     auto& params = Params().GetConsensus().llmqs.at(llmqType);
     271       14620 :     int quorumStartHeight = nHeight - (nHeight % params.dkgInterval);
     272             : 
     273       14620 :     LOCK(cs_main);
     274       14620 :     if (quorumStartHeight > chainActive.Height()) {
     275         561 :         return UINT256_ZERO;
     276             :     }
     277       28679 :     return chainActive[quorumStartHeight]->GetBlockHash();
     278             : }
     279             : 
     280       28454 : bool CQuorumBlockProcessor::HasMinedCommitment(Consensus::LLMQType llmqType, const uint256& quorumHash)
     281             : {
     282       28454 :     bool fExists;
     283       28454 :     {
     284       28454 :         LOCK(minableCommitmentsCs);
     285       28454 :         if (mapHasMinedCommitmentCache.at((Consensus::LLMQType)llmqType).get(quorumHash, fExists)) {
     286       55534 :             return fExists;
     287             :         }
     288             :     }
     289             : 
     290         687 :     fExists = evoDb.Exists(std::make_pair(DB_MINED_COMMITMENT, std::make_pair(static_cast<uint8_t>(llmqType), quorumHash)));
     291             : 
     292       29141 :     LOCK(minableCommitmentsCs);
     293         687 :     mapHasMinedCommitmentCache.at((Consensus::LLMQType)llmqType).insert(quorumHash, fExists);
     294             : 
     295         687 :     return fExists;
     296             : }
     297             : 
     298         164 : bool CQuorumBlockProcessor::GetMinedCommitment(Consensus::LLMQType llmqType, const uint256& quorumHash, CFinalCommitment& retQc, uint256& retMinedBlockHash)
     299             : {
     300         328 :     auto key = std::make_pair(DB_MINED_COMMITMENT, std::make_pair(static_cast<uint8_t>(llmqType), quorumHash));
     301         328 :     std::pair<CFinalCommitment, uint256> p;
     302         164 :     if (!evoDb.Read(key, p)) {
     303             :         return false;
     304             :     }
     305         163 :     retQc = std::move(p.first);
     306         163 :     retMinedBlockHash = p.second;
     307         163 :     return true;
     308             : }
     309             : 
     310             : // The returned quorums are in reversed order, so the most recent one is at index 0
     311       10415 : std::vector<const CBlockIndex*> CQuorumBlockProcessor::GetMinedCommitmentsUntilBlock(Consensus::LLMQType llmqType, const CBlockIndex* pindex, size_t maxCount)
     312             : {
     313       10415 :     LOCK(evoDb.cs);
     314             : 
     315       20830 :     auto dbIt = evoDb.GetCurTransaction().NewIteratorUniquePtr();
     316             : 
     317       20830 :     auto firstKey = BuildInversedHeightKey(llmqType, pindex->nHeight);
     318       20830 :     auto lastKey = BuildInversedHeightKey(llmqType, 0);
     319             : 
     320       10415 :     dbIt->Seek(firstKey);
     321             : 
     322       10415 :     std::vector<const CBlockIndex*> ret;
     323       10415 :     ret.reserve(maxCount);
     324             : 
     325       23400 :     while (dbIt->Valid() && ret.size() < maxCount) {
     326       29581 :         decltype(firstKey) curKey;
     327       16596 :         int quorumHeight;
     328       16596 :         if (!dbIt->GetKey(curKey) || curKey >= lastKey) {
     329             :             break;
     330             :         }
     331       16596 :         if (std::get<0>(curKey) != DB_MINED_COMMITMENT_BY_INVERSED_HEIGHT || std::get<1>(curKey) != static_cast<uint8_t>(llmqType)) {
     332             :             break;
     333             :         }
     334             : 
     335       12985 :         uint32_t nMinedHeight = std::numeric_limits<uint32_t>::max() - be32toh(std::get<2>(curKey));
     336       12985 :         if (nMinedHeight > (uint32_t) pindex->nHeight) {
     337             :             break;
     338             :         }
     339             : 
     340       12985 :         if (!dbIt->GetValue(quorumHeight)) {
     341             :             break;
     342             :         }
     343             : 
     344       12985 :         auto quorumIndex = pindex->GetAncestor(quorumHeight);
     345       12985 :         assert(quorumIndex);
     346       12985 :         ret.emplace_back(quorumIndex);
     347             : 
     348       12985 :         dbIt->Next();
     349             :     }
     350             : 
     351       20830 :     return ret;
     352             : }
     353             : 
     354             : // The returned quorums are in reversed order, so the most recent one is at index 0
     355           0 : std::map<Consensus::LLMQType, std::vector<const CBlockIndex*>> CQuorumBlockProcessor::GetMinedAndActiveCommitmentsUntilBlock(const CBlockIndex* pindex)
     356             : {
     357           0 :     std::map<Consensus::LLMQType, std::vector<const CBlockIndex*>> ret;
     358             : 
     359           0 :     for (const auto& p : Params().GetConsensus().llmqs) {
     360           0 :         auto& v = ret[p.second.type];
     361           0 :         v.reserve(p.second.signingActiveQuorumCount);
     362           0 :         auto commitments = GetMinedCommitmentsUntilBlock(p.second.type, pindex, p.second.signingActiveQuorumCount);
     363           0 :         for (auto& c : commitments) {
     364           0 :             v.emplace_back(c);
     365             :         }
     366             :     }
     367             : 
     368           0 :     return ret;
     369             : }
     370             : 
     371        1691 : bool CQuorumBlockProcessor::HasMinableCommitment(const uint256& hash)
     372             : {
     373        1691 :     LOCK(minableCommitmentsCs);
     374        3382 :     return minableCommitments.count(hash) != 0;
     375             : }
     376             : 
     377         102 : bool CQuorumBlockProcessor::HasBetterMinableCommitment(const CFinalCommitment& qc)
     378             : {
     379         102 :     LOCK(minableCommitmentsCs);
     380         102 :     auto it = minableCommitmentsByQuorum.find(std::make_pair(qc.llmqType, qc.quorumHash));
     381         102 :     if (it != minableCommitmentsByQuorum.end()) {
     382           2 :         auto jt = minableCommitments.find(it->second);
     383           2 :         return jt != minableCommitments.end() && jt->second.CountSigners() >= qc.CountSigners();
     384             :     }
     385             :     return false;
     386             : }
     387             : 
     388         156 : void CQuorumBlockProcessor::AddAndRelayMinableCommitment(const CFinalCommitment& fqc, uint256* cached_fqc_hash)
     389             : {
     390         156 :     const uint256& commitmentHash = cached_fqc_hash ? *cached_fqc_hash : ::SerializeHash(fqc);
     391         156 :     {
     392         156 :         LOCK(minableCommitmentsCs);
     393         156 :         auto k = std::make_pair(fqc.llmqType, fqc.quorumHash);
     394         156 :         auto ins = minableCommitmentsByQuorum.emplace(k, commitmentHash);
     395         156 :         if (!ins.second) {
     396             :             // remove old commitment
     397           3 :             minableCommitments.erase(ins.first->second);
     398             :             // update commitment hash to the new one
     399           3 :             ins.first->second = commitmentHash;
     400             :         }
     401             :         // add new commitment
     402         156 :         minableCommitments.emplace(commitmentHash, fqc);
     403         312 :         quorumDKGDebugManager->UpdateLocalSessionStatus((Consensus::LLMQType)fqc.llmqType, [&](CDKGDebugSessionStatus& status) {
     404          59 :             if (status.quorumHash != fqc.quorumHash || status.receivedFinalCommitment) {
     405             :                 return false;
     406             :             }
     407          57 :             status.receivedFinalCommitment = true;
     408          57 :             return true;
     409             :         });
     410             :     }
     411             :     // relay commitment inv (if DKG is not in maintenance)
     412         156 :     if (!sporkManager.IsSporkActive(SPORK_22_LLMQ_DKG_MAINTENANCE)) {
     413         153 :         CInv inv(MSG_QUORUM_FINAL_COMMITMENT, commitmentHash);
     414         153 :         g_connman->RelayInv(inv);
     415             :     }
     416         156 : }
     417             : 
     418          96 : bool CQuorumBlockProcessor::GetMinableCommitmentByHash(const uint256& commitmentHash, llmq::CFinalCommitment& ret)
     419             : {
     420         192 :     LOCK(minableCommitmentsCs);
     421          96 :     auto it = minableCommitments.find(commitmentHash);
     422          96 :     if (it == minableCommitments.end()) {
     423             :         return false;
     424             :     }
     425          96 :     ret = it->second;
     426             :     return true;
     427             : }
     428             : 
     429             : // Will return false if no commitment should be mined
     430             : // Will return true and a null commitment if no minable commitment is known and none was mined yet
     431        1674 : bool CQuorumBlockProcessor::GetMinableCommitment(Consensus::LLMQType llmqType, int nHeight, CFinalCommitment& ret)
     432             : {
     433        1674 :     if (!IsCommitmentRequired(llmqType, nHeight)) {
     434             :         // no commitment required
     435             :         return false;
     436             :     }
     437             : 
     438         405 :     uint256 quorumHash = GetQuorumBlockHash(llmqType, nHeight);
     439         810 :     if (quorumHash.IsNull()) {
     440             :         return false;
     441             :     }
     442             : 
     443         405 :     if (sporkManager.IsSporkActive(SPORK_22_LLMQ_DKG_MAINTENANCE)) {
     444             :         // null commitment required
     445          16 :         ret = CFinalCommitment(Params().GetConsensus().llmqs.at(llmqType), quorumHash);
     446          16 :         return true;
     447             :     }
     448             : 
     449        2063 :     LOCK(minableCommitmentsCs);
     450             : 
     451         389 :     auto k = std::make_pair(static_cast<uint8_t>(llmqType), quorumHash);
     452         389 :     auto it = minableCommitmentsByQuorum.find(k);
     453         389 :     if (it == minableCommitmentsByQuorum.end()) {
     454             :         // null commitment required
     455         318 :         ret = CFinalCommitment(Params().GetConsensus().llmqs.at(llmqType), quorumHash);
     456         318 :         return true;
     457             :     }
     458             : 
     459          71 :     ret = minableCommitments.at(it->second);
     460             : 
     461             :     return true;
     462             : }
     463             : 
     464        1368 : bool CQuorumBlockProcessor::GetMinableCommitmentTx(Consensus::LLMQType llmqType, int nHeight, CTransactionRef& ret)
     465             : {
     466        2736 :     LLMQCommPL pl;
     467        1368 :     if (!GetMinableCommitment(llmqType, nHeight, pl.commitment)) {
     468             :         return false;
     469             :     }
     470             : 
     471         354 :     pl.nHeight = nHeight;
     472             : 
     473         354 :     CMutableTransaction tx;
     474         354 :     tx.nVersion = CTransaction::TxVersion::SAPLING;
     475         354 :     tx.nType = CTransaction::TxType::LLMQCOMM;
     476         354 :     SetTxPayload(tx, pl);
     477             : 
     478         354 :     ret = MakeTransactionRef(tx);
     479             : 
     480         354 :     return true;
     481             : }
     482             : 
     483             : } // namespace llmq

Generated by: LCOV version 1.14