LCOV - code coverage report
Current view: top level - src/llmq - quorumsman.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 52 358 14.5 %
Date: 2026-06-25 07:23:51 Functions: 8 37 21.6 %

          Line data    Source code
       1             : // Copyright (c) 2018-2026 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 <llmq/quorumsman.h>
       6             : 
       7             : #include <bls/bls.h>
       8             : #include <bls/bls_ies.h>
       9             : #include <evo/deterministicmns.h>
      10             : #include <evo/evodb.h>
      11             : #include <llmq/blockprocessor.h>
      12             : #include <llmq/commitment.h>
      13             : #include <llmq/dkgsessionmgr.h>
      14             : #include <llmq/options.h>
      15             : #include <llmq/params.h>
      16             : #include <llmq/signhash.h>
      17             : #include <llmq/utils.h>
      18             : #include <util/helpers.h>
      19             : #include <util/std23.h>
      20             : 
      21             : #include <chainparams.h>
      22             : #include <dbwrapper.h>
      23             : #include <logging.h>
      24             : #include <util/thread.h>
      25             : #include <util/time.h>
      26             : #include <validation.h>
      27             : 
      28             : #include <cxxtimer.hpp>
      29             : 
      30             : namespace llmq {
      31         360 : CQuorumManager::CQuorumManager(CBLSWorker& _blsWorker, CDeterministicMNManager& dmnman, CEvoDB& _evoDb,
      32             :                                CQuorumBlockProcessor& _quorumBlockProcessor, CQuorumSnapshotManager& qsnapman,
      33             :                                const ChainstateManager& chainman, const util::DbWrapperParams& db_params) :
      34         180 :     blsWorker{_blsWorker},
      35         180 :     m_dmnman{dmnman},
      36         180 :     quorumBlockProcessor{_quorumBlockProcessor},
      37         180 :     m_qsnapman{qsnapman},
      38         180 :     m_chainman{chainman},
      39             :     db{util::MakeDbWrapper({db_params.path / "llmq" / "quorumdb", db_params.memory, db_params.wipe, /*cache_size=*/1 << 20})}
      40         180 : {
      41             :     utils::InitQuorumsCache(mapQuorumsCache, m_chainman.GetConsensus(), /*limit_by_connections=*/false);
      42             :     m_cache_interrupt.reset();
      43         180 :     m_cache_thread = std::thread(&util::TraceThread, "q-cache", [this] { CacheWarmingThreadMain(); });
      44             :     MigrateOldQuorumDB(_evoDb);
      45         180 : }
      46             : 
      47         360 : CQuorumManager::~CQuorumManager()
      48         180 : {
      49         180 :     if (m_cache_thread.joinable()) {
      50         180 :         m_cache_interrupt();
      51         180 :         m_cache_thread.join();
      52         180 :     }
      53         360 : }
      54             : 
      55           0 : bool CQuorumManager::GetEncryptedContributions(Consensus::LLMQType llmq_type, const CBlockIndex* block_index,
      56             :                                                const std::vector<bool>& valid_members, const uint256& protx_hash,
      57             :                                                std::vector<CBLSIESEncryptedObject<CBLSSecretKey>>& vec_enc) const
      58             : {
      59           0 :     if (m_qdkgsman) {
      60           0 :         return m_qdkgsman->GetEncryptedContributions(llmq_type, block_index, valid_members, protx_hash, vec_enc);
      61             :     }
      62           0 :     return false;
      63           0 : }
      64             : 
      65           0 : CQuorumPtr CQuorumManager::BuildQuorumFromCommitment(const Consensus::LLMQType llmqType, gsl::not_null<const CBlockIndex*> pQuorumBaseBlockIndex, bool populate_cache) const
      66             : {
      67           0 :     const uint256& quorumHash{pQuorumBaseBlockIndex->GetBlockHash()};
      68             : 
      69           0 :     auto [qc, minedBlockHash] = quorumBlockProcessor.GetMinedCommitment(llmqType, quorumHash);
      70           0 :     if (minedBlockHash == uint256::ZERO) {
      71           0 :         LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- No mined commitment for llmqType[%d] nHeight[%d] quorumHash[%s]\n", __func__, std23::to_underlying(llmqType), pQuorumBaseBlockIndex->nHeight, pQuorumBaseBlockIndex->GetBlockHash().ToString());
      72           0 :         return nullptr;
      73             :     }
      74           0 :     assert(qc.quorumHash == pQuorumBaseBlockIndex->GetBlockHash());
      75             : 
      76           0 :     const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
      77           0 :     assert(llmq_params_opt.has_value());
      78           0 :     auto members = utils::GetAllQuorumMembers(qc.llmqType, {m_dmnman, m_qsnapman, m_chainman, pQuorumBaseBlockIndex});
      79             : 
      80           0 :     auto quorum = std::make_shared<CQuorum>(llmq_params_opt.value(), blsWorker,
      81           0 :         std::make_unique<CFinalCommitment>(std::move(qc)), pQuorumBaseBlockIndex, minedBlockHash, members);
      82             : 
      83           0 :     if (populate_cache && llmq_params_opt->size == 1) {
      84           0 :         WITH_LOCK(m_cs_maps, mapQuorumsCache[llmqType].insert(quorumHash, quorum));
      85             : 
      86           0 :         return quorum;
      87             :     }
      88             : 
      89           0 :     bool hasValidVvec = false;
      90           0 :     if (WITH_LOCK(cs_db, return quorum->ReadContributions(*db))) {
      91           0 :         hasValidVvec = true;
      92           0 :     } else {
      93           0 :         if (BuildQuorumContributions(quorum->qc, quorum)) {
      94           0 :             WITH_LOCK(cs_db, quorum->WriteContributions(*db));
      95           0 :             hasValidVvec = true;
      96           0 :         } else {
      97           0 :             LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- llmqType[%d] quorumIndex[%d] quorum.ReadContributions and BuildQuorumContributions for quorumHash[%s] failed\n", __func__, std23::to_underlying(llmqType), quorum->qc->quorumIndex, quorum->qc->quorumHash.ToString());
      98             :         }
      99             :     }
     100             : 
     101           0 :     if (hasValidVvec && populate_cache) {
     102             :         // pre-populate caches in the background
     103             :         // recovering public key shares is quite expensive and would result in serious lags for the first few signing
     104             :         // sessions if the shares would be calculated on-demand
     105           0 :         QueueQuorumForWarming(quorum);
     106           0 :     }
     107             : 
     108           0 :     WITH_LOCK(m_cs_maps, mapQuorumsCache[llmqType].insert(quorumHash, quorum));
     109             : 
     110           0 :     return quorum;
     111           0 : }
     112             : 
     113           0 : bool CQuorumManager::BuildQuorumContributions(const CFinalCommitmentPtr& fqc, const std::shared_ptr<CQuorum>& quorum) const
     114             : {
     115           0 :     std::vector<uint16_t> memberIndexes;
     116           0 :     std::vector<BLSVerificationVectorPtr> vvecs;
     117           0 :     std::vector<CBLSSecretKey> skContributions;
     118           0 :     if (!m_qdkgsman ||
     119           0 :         !m_qdkgsman->GetVerifiedContributions((Consensus::LLMQType)fqc->llmqType, quorum->m_quorum_base_block_index,
     120           0 :                                             fqc->validMembers, memberIndexes, vvecs, skContributions)) {
     121           0 :         return false;
     122             :     }
     123             : 
     124           0 :     cxxtimer::Timer t2(true);
     125           0 :     quorum->SetVerificationVector(blsWorker.BuildQuorumVerificationVector(vvecs));
     126           0 :     if (!quorum->HasVerificationVector()) {
     127           0 :         LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- failed to build quorumVvec\n", __func__);
     128             :         // without the quorum vvec, there can't be a skShare, so we fail here. Failure is not fatal here, as it still
     129             :         // allows to use the quorum as a non-member (verification through the quorum pub key)
     130           0 :         return false;
     131             :     }
     132           0 :     if (!m_handler || !m_handler->SetQuorumSecretKeyShare(*quorum, skContributions)) {
     133           0 :         LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- failed to build skShare\n", __func__);
     134             :         // We don't bail out here as this is not a fatal error and still allows us to recover public key shares (as we
     135             :         // have a valid quorum vvec at this point)
     136           0 :     }
     137           0 :     t2.stop();
     138             : 
     139           0 :     LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- built quorum vvec and skShare. time=%d\n", __func__, t2.count());
     140             : 
     141           0 :     return true;
     142           0 : }
     143             : 
     144           0 : bool CQuorumManager::HasQuorum(Consensus::LLMQType llmqType, const CQuorumBlockProcessor& quorum_block_processor, const uint256& quorumHash)
     145             : {
     146           0 :     return quorum_block_processor.HasMinedCommitment(llmqType, quorumHash);
     147             : }
     148             : 
     149           0 : std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType, size_t nCountRequested) const
     150             : {
     151           0 :     const CBlockIndex* pindex = WITH_LOCK(::cs_main, return m_chainman.ActiveTip());
     152           0 :     return ScanQuorums(llmqType, pindex, nCountRequested);
     153             : }
     154             : 
     155           0 : std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType,
     156             :                                                      gsl::not_null<const CBlockIndex*> pindexStart,
     157             :                                                      size_t nCountRequested) const
     158             : {
     159           0 :     if (nCountRequested == 0 || !m_chainman.IsQuorumTypeEnabled(llmqType, pindexStart)) {
     160           0 :         return {};
     161             :     }
     162             : 
     163           0 :     gsl::not_null<const CBlockIndex*> pindexStore{pindexStart};
     164           0 :     const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
     165           0 :     assert(llmq_params_opt.has_value());
     166             : 
     167             :     // Quorum sets can only change during the mining phase of DKG.
     168             :     // Find the closest known block index.
     169           0 :     const int quorumCycleStartHeight = pindexStart->nHeight - (pindexStart->nHeight % llmq_params_opt->dkgInterval);
     170           0 :     const int quorumCycleMiningStartHeight = quorumCycleStartHeight + llmq_params_opt->dkgMiningWindowStart;
     171           0 :     const int quorumCycleMiningEndHeight = quorumCycleStartHeight + llmq_params_opt->dkgMiningWindowEnd;
     172             : 
     173           0 :     if (pindexStart->nHeight < quorumCycleMiningStartHeight) {
     174             :         // too early for this cycle, use the previous one
     175             :         // bail out if it's below genesis block
     176           0 :         if (quorumCycleMiningEndHeight < llmq_params_opt->dkgInterval) return {};
     177           0 :         pindexStore = pindexStart->GetAncestor(quorumCycleMiningEndHeight - llmq_params_opt->dkgInterval);
     178           0 :     } else if (pindexStart->nHeight > quorumCycleMiningEndHeight) {
     179             :         // we are past the mining phase of this cycle, use it
     180           0 :         pindexStore = pindexStart->GetAncestor(quorumCycleMiningEndHeight);
     181           0 :     }
     182             :     // everything else is inside the mining phase of this cycle, no pindexStore adjustment needed
     183             : 
     184           0 :     gsl::not_null<const CBlockIndex*> pIndexScanCommitments{pindexStore};
     185           0 :     size_t nScanCommitments{nCountRequested};
     186           0 :     std::vector<CQuorumCPtr> vecResultQuorums;
     187             : 
     188             :     {
     189           0 :         LOCK(m_cs_maps);
     190           0 :         if (scanQuorumsCache.empty()) {
     191           0 :             for (const auto& llmq : Params().GetConsensus().llmqs) {
     192             :                 // NOTE: We store it for each block hash in the DKG mining phase here
     193             :                 // and not for a single quorum hash per quorum like we do for other caches.
     194             :                 // And we only do this for max_cycles() of the most recent quorums
     195             :                 // because signing by old quorums requires the exact quorum hash to be specified
     196             :                 // and quorum scanning isn't needed there.
     197           0 :                 scanQuorumsCache.try_emplace(llmq.type, llmq.max_cycles(llmq.keepOldConnections) * (llmq.dkgMiningWindowEnd - llmq.dkgMiningWindowStart));
     198             :             }
     199           0 :         }
     200           0 :         auto& cache = scanQuorumsCache[llmqType];
     201           0 :         bool fCacheExists = cache.get(pindexStore->GetBlockHash(), vecResultQuorums);
     202           0 :         if (fCacheExists) {
     203             :             // We have exactly what requested so just return it
     204           0 :             if (vecResultQuorums.size() == nCountRequested) {
     205           0 :                 return vecResultQuorums;
     206             :             }
     207             :             // If we have more cached than requested return only a subvector
     208           0 :             if (vecResultQuorums.size() > nCountRequested) {
     209           0 :                 return {vecResultQuorums.begin(), vecResultQuorums.begin() + nCountRequested};
     210             :             }
     211             :             // If we have cached quorums but not enough, subtract what we have from the count and the set correct index where to start
     212             :             // scanning for the rests
     213           0 :             if (!vecResultQuorums.empty()) {
     214           0 :                 nScanCommitments -= vecResultQuorums.size();
     215             :                 // bail out if it's below genesis block
     216           0 :                 if (vecResultQuorums.back()->m_quorum_base_block_index->pprev == nullptr) return {};
     217           0 :                 pIndexScanCommitments = vecResultQuorums.back()->m_quorum_base_block_index->pprev;
     218           0 :             }
     219           0 :         } else {
     220             :             // If there is nothing in cache request at least keepOldConnections because this gets cached then later
     221           0 :             nScanCommitments = std::max(nCountRequested, static_cast<size_t>(llmq_params_opt->keepOldConnections));
     222             :         }
     223           0 :     }
     224             : 
     225             :     // Get the block indexes of the mined commitments to build the required quorums from
     226           0 :     std::vector<const CBlockIndex*> pQuorumBaseBlockIndexes{ llmq_params_opt->useRotation ?
     227           0 :             quorumBlockProcessor.GetMinedCommitmentsIndexedUntilBlock(llmqType, pIndexScanCommitments, nScanCommitments) :
     228           0 :             quorumBlockProcessor.GetMinedCommitmentsUntilBlock(llmqType, pIndexScanCommitments, nScanCommitments)
     229             :     };
     230           0 :     vecResultQuorums.reserve(vecResultQuorums.size() + pQuorumBaseBlockIndexes.size());
     231             : 
     232           0 :     for (auto& pQuorumBaseBlockIndex : pQuorumBaseBlockIndexes) {
     233           0 :         assert(pQuorumBaseBlockIndex);
     234             :         // populate cache for keepOldConnections most recent quorums only
     235           0 :         bool populate_cache = vecResultQuorums.size() < static_cast<size_t>(llmq_params_opt->keepOldConnections);
     236             : 
     237             :         // We assume that every quorum asked for is available to us on hand, if this
     238             :         // fails then we can assume that something has gone wrong and we should stop
     239             :         // trying to process any further and return a blank.
     240           0 :         auto quorum = GetQuorum(llmqType, pQuorumBaseBlockIndex, populate_cache);
     241           0 :         if (!quorum) {
     242           0 :             LogPrintf("%s: ERROR! Unexpected missing quorum with llmqType=%d, blockHash=%s, populate_cache=%s\n",
     243             :                       __func__, std23::to_underlying(llmqType), pQuorumBaseBlockIndex->GetBlockHash().ToString(),
     244             :                       util::to_string(populate_cache));
     245           0 :             return {};
     246             :         }
     247           0 :         vecResultQuorums.emplace_back(quorum);
     248           0 :     }
     249             : 
     250           0 :     const size_t nCountResult{vecResultQuorums.size()};
     251           0 :     if (nCountResult > 0) {
     252           0 :         LOCK(m_cs_maps);
     253             :         // Don't cache more than keepOldConnections elements
     254             :         // because signing by old quorums requires the exact quorum hash
     255             :         // to be specified and quorum scanning isn't needed there.
     256           0 :         auto& cache = scanQuorumsCache[llmqType];
     257           0 :         const size_t nCacheEndIndex = std::min(nCountResult, static_cast<size_t>(llmq_params_opt->keepOldConnections));
     258           0 :         cache.emplace(pindexStore->GetBlockHash(), {vecResultQuorums.begin(), vecResultQuorums.begin() + nCacheEndIndex});
     259           0 :     }
     260             :     // Don't return more than nCountRequested elements
     261           0 :     const size_t nResultEndIndex = std::min(nCountResult, nCountRequested);
     262           0 :     return {vecResultQuorums.begin(), vecResultQuorums.begin() + nResultEndIndex};
     263           0 : }
     264             : 
     265           0 : bool CQuorumManager::IsMasternode() const
     266             : {
     267           0 :     if (m_handler) {
     268           0 :         return m_handler->IsMasternode();
     269             :     }
     270           0 :     return false;
     271           0 : }
     272             : 
     273           2 : bool CQuorumManager::IsWatching() const
     274             : {
     275           2 :     if (m_handler) {
     276           0 :         return m_handler->IsWatching();
     277             :     }
     278           2 :     return false;
     279           2 : }
     280             : 
     281           0 : bool CQuorumManager::IsDataRequestPending(const uint256& proRegTx, bool we_requested, const uint256& quorumHash,
     282             :                                           Consensus::LLMQType llmqType) const
     283             : {
     284           0 :     const CQuorumDataRequestKey key{proRegTx, we_requested, quorumHash, llmqType};
     285           0 :     LOCK(cs_data_requests);
     286           0 :     const auto it = mapQuorumDataRequests.find(key);
     287           0 :     return it != mapQuorumDataRequests.end() && !it->second.IsExpired(/*add_bias=*/true);
     288           0 : }
     289             : 
     290           0 : DataRequestStatus CQuorumManager::GetDataRequestStatus(const uint256& proRegTx, bool we_requested,
     291             :                                                        const uint256& quorumHash, Consensus::LLMQType llmqType) const
     292             : {
     293           0 :     const CQuorumDataRequestKey key{proRegTx, we_requested, quorumHash, llmqType};
     294           0 :     LOCK(cs_data_requests);
     295           0 :     const auto it = mapQuorumDataRequests.find(key);
     296           0 :     if (it == mapQuorumDataRequests.end()) {
     297           0 :         return DataRequestStatus::NotFound;
     298             :     }
     299           0 :     if (it->second.IsProcessed()) {
     300           0 :         return DataRequestStatus::Processed;
     301             :     }
     302           0 :     return DataRequestStatus::Pending;
     303           0 : }
     304             : 
     305           0 : void CQuorumManager::CleanupExpiredDataRequests() const
     306             : {
     307           0 :     LOCK(cs_data_requests);
     308           0 :     auto it = mapQuorumDataRequests.begin();
     309           0 :     while (it != mapQuorumDataRequests.end()) {
     310           0 :         if (it->second.IsExpired(/*add_bias=*/true)) {
     311           0 :             it = mapQuorumDataRequests.erase(it);
     312           0 :         } else {
     313           0 :             ++it;
     314             :         }
     315             :     }
     316           0 : }
     317             : 
     318           0 : void CQuorumManager::CleanupOldQuorumData(const Uint256HashSet& dbKeysToSkip) const
     319             : {
     320           0 :     LOCK(cs_db);
     321           0 :     DataCleanupHelper(*db, dbKeysToSkip);
     322           0 : }
     323             : 
     324           0 : CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash) const
     325             : {
     326           0 :     const CBlockIndex* pQuorumBaseBlockIndex = [&]() {
     327             :         // Lock contention may still be high here; consider using a shared lock
     328             :         // We cannot hold cs_quorumBaseBlockIndexCache the whole time as that creates lock-order inversion with cs_main;
     329             :         // We cannot acquire cs_main if we have cs_quorumBaseBlockIndexCache held
     330             :         const CBlockIndex* pindex;
     331           0 :         if (!WITH_LOCK(cs_quorumBaseBlockIndexCache, return quorumBaseBlockIndexCache.get(quorumHash, pindex))) {
     332           0 :             pindex = WITH_LOCK(::cs_main, return m_chainman.m_blockman.LookupBlockIndex(quorumHash));
     333           0 :             if (pindex) {
     334           0 :                 LOCK(cs_quorumBaseBlockIndexCache);
     335           0 :                 quorumBaseBlockIndexCache.insert(quorumHash, pindex);
     336           0 :             }
     337           0 :         }
     338           0 :         return pindex;
     339           0 :     }();
     340           0 :     if (!pQuorumBaseBlockIndex) {
     341           0 :         LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- block %s not found\n", __func__, quorumHash.ToString());
     342           0 :         return nullptr;
     343             :     }
     344           0 :     return GetQuorum(llmqType, pQuorumBaseBlockIndex);
     345           0 : }
     346             : 
     347           0 : CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, gsl::not_null<const CBlockIndex*> pQuorumBaseBlockIndex, bool populate_cache) const
     348             : {
     349           0 :     auto quorumHash = pQuorumBaseBlockIndex->GetBlockHash();
     350             : 
     351             :     // we must check this before we look into the cache. Reorgs might have happened which would mean we might have
     352             :     // cached quorums which are not in the active chain anymore
     353           0 :     if (!HasQuorum(llmqType, quorumBlockProcessor, quorumHash)) {
     354           0 :         return nullptr;
     355             :     }
     356             : 
     357           0 :     CQuorumPtr pQuorum;
     358           0 :     if (LOCK(m_cs_maps); mapQuorumsCache[llmqType].get(quorumHash, pQuorum)) {
     359           0 :         return pQuorum;
     360             :     }
     361             : 
     362           0 :     return BuildQuorumFromCommitment(llmqType, pQuorumBaseBlockIndex, populate_cache);
     363           0 : }
     364             : 
     365           0 : bool CQuorumManager::RegisterDataRequest(const CQuorumDataRequestKey& key, const CQuorumDataRequest& request,
     366             :                                           bool add_expiry_bias) const
     367             : {
     368           0 :     LOCK(cs_data_requests);
     369           0 :     auto [old_pair, inserted] = mapQuorumDataRequests.emplace(key, request);
     370           0 :     if (!inserted) {
     371           0 :         if (old_pair->second.IsExpired(add_expiry_bias)) {
     372           0 :             old_pair->second = request;
     373           0 :             return true;
     374             :         }
     375           0 :         return false;
     376             :     }
     377           0 :     return true;
     378           0 : }
     379             : 
     380           0 : CQuorumManager::DataResponseValidation CQuorumManager::ValidateDataResponse(
     381             :     const CQuorumDataRequestKey& key, const CQuorumDataRequest& response) const
     382             : {
     383           0 :     LOCK(cs_data_requests);
     384           0 :     auto it = mapQuorumDataRequests.find(key);
     385           0 :     if (it == mapQuorumDataRequests.end()) {
     386           0 :         return DataResponseValidation::NotRequested;
     387             :     }
     388           0 :     if (it->second.IsProcessed()) {
     389           0 :         return DataResponseValidation::AlreadyReceived;
     390             :     }
     391           0 :     if (response != it->second) {
     392           0 :         return DataResponseValidation::Mismatch;
     393             :     }
     394           0 :     it->second.SetProcessed();
     395           0 :     return DataResponseValidation::OK;
     396           0 : }
     397             : 
     398           0 : CQuorumPtr CQuorumManager::GetCachedMutableQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash) const
     399             : {
     400           0 :     CQuorumPtr pQuorum;
     401           0 :     LOCK(m_cs_maps);
     402           0 :     mapQuorumsCache[llmqType].get(quorumHash, pQuorum);
     403           0 :     return pQuorum;
     404           0 : }
     405             : 
     406           0 : void CQuorumManager::WriteContributions(const CQuorumPtr& quorum) const
     407             : {
     408           0 :     LOCK(cs_db);
     409           0 :     quorum->WriteContributions(*db);
     410           0 : }
     411             : 
     412         180 : void CQuorumManager::CacheWarmingThreadMain() const
     413             : {
     414        2005 :     while (!m_cache_interrupt) {
     415        1825 :         CQuorumCPtr pQuorum;
     416             :         {
     417        1825 :             LOCK(m_cache_cs);
     418        1825 :             if (!m_cache_queue.empty()) {
     419           0 :                 pQuorum = std::move(m_cache_queue.front());
     420           0 :                 m_cache_queue.pop_front();
     421           0 :             };
     422        1825 :         }
     423             : 
     424        1825 :         if (!pQuorum) {
     425        1825 :             m_cache_interrupt.sleep_for(std::chrono::milliseconds(100));
     426        1825 :             continue;
     427             :         }
     428             : 
     429           0 :         cxxtimer::Timer t(true);
     430           0 :         LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- type=%d height=%d hash=%s start\n", __func__,
     431             :                  std23::to_underlying(pQuorum->params.type), pQuorum->m_quorum_base_block_index->nHeight,
     432             :                  pQuorum->m_quorum_base_block_index->GetBlockHash().ToString());
     433             : 
     434             :         // when then later some other thread tries to get keys, it will be much faster
     435           0 :         for (const auto i : util::irange(pQuorum->members.size())) {
     436           0 :             if (m_cache_interrupt) {
     437           0 :                 break;
     438             :             }
     439           0 :             if (pQuorum->qc->validMembers[i]) {
     440           0 :                 pQuorum->GetPubKeyShare(i);
     441           0 :             }
     442             :         }
     443             : 
     444           0 :         LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- type=%d height=%d hash=%s done. time=%d\n", __func__,
     445             :                  std23::to_underlying(pQuorum->params.type), pQuorum->m_quorum_base_block_index->nHeight,
     446             :                  pQuorum->m_quorum_base_block_index->GetBlockHash().ToString(), t.count());
     447        1825 :     }
     448         180 : }
     449             : 
     450           0 : void CQuorumManager::QueueQuorumForWarming(CQuorumCPtr pQuorum) const
     451             : {
     452           0 :     if (pQuorum->HasVerificationVector()) {
     453           0 :         LOCK(m_cache_cs);
     454           0 :         m_cache_queue.push_back(std::move(pQuorum));
     455           0 :     }
     456           0 : }
     457             : 
     458             : // TODO: remove in v23
     459         180 : void CQuorumManager::MigrateOldQuorumDB(CEvoDB& evoDb) const
     460             : {
     461         180 :     LOCK(cs_db);
     462         180 :     if (!db->IsEmpty()) return;
     463             : 
     464         180 :     const auto prefixes = {DB_QUORUM_QUORUM_VVEC, DB_QUORUM_SK_SHARE};
     465             : 
     466         180 :     LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- start\n", __func__);
     467             : 
     468         180 :     CDBBatch batch(*db);
     469         180 :     std::unique_ptr<CDBIterator> pcursor(evoDb.GetRawDB().NewIterator());
     470             : 
     471         540 :     for (const auto& prefix : prefixes) {
     472         360 :         auto start = std::make_tuple(prefix, uint256());
     473         360 :         pcursor->Seek(start);
     474             : 
     475         360 :         int count{0};
     476         360 :         while (pcursor->Valid()) {
     477           0 :             decltype(start) k;
     478           0 :             CDataStream s(SER_DISK, CLIENT_VERSION);
     479           0 :             CBLSSecretKey sk;
     480             : 
     481           0 :             if (!pcursor->GetKey(k) || std::get<0>(k) != prefix) {
     482           0 :                 break;
     483             :             }
     484             : 
     485           0 :             if (prefix == DB_QUORUM_QUORUM_VVEC) {
     486           0 :                 if (!evoDb.GetRawDB().ReadDataStream(k, s)) {
     487           0 :                     break;
     488             :                 }
     489           0 :                 batch.Write(k, s);
     490           0 :             }
     491           0 :             if (prefix == DB_QUORUM_SK_SHARE) {
     492           0 :                 if (!pcursor->GetValue(sk)) {
     493           0 :                     break;
     494             :                 }
     495           0 :                 batch.Write(k, sk);
     496           0 :             }
     497             : 
     498           0 :             if (batch.SizeEstimate() >= (1 << 24)) {
     499           0 :                 db->WriteBatch(batch);
     500           0 :                 batch.Clear();
     501           0 :             }
     502             : 
     503           0 :             ++count;
     504           0 :             pcursor->Next();
     505           0 :         }
     506             : 
     507         360 :         db->WriteBatch(batch);
     508             : 
     509         360 :         LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- %s moved %d\n", __func__, prefix, count);
     510         360 :     }
     511             : 
     512         180 :     pcursor.reset();
     513         180 :     db->CompactFull();
     514             : 
     515         180 :     DataCleanupHelper(evoDb.GetRawDB(), {});
     516         180 :     evoDb.CommitRootTransaction();
     517             : 
     518         180 :     LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- done\n", __func__);
     519         180 : }
     520             : 
     521           0 : CQuorumCPtr SelectQuorumForSigning(const Consensus::LLMQParams& llmq_params, const CChain& active_chain, const CQuorumManager& qman,
     522             :                                    const uint256& selectionHash, int signHeight, int signOffset)
     523             : {
     524           0 :     size_t poolSize = llmq_params.signingActiveQuorumCount;
     525             : 
     526             :     CBlockIndex* pindexStart;
     527             :     {
     528           0 :         LOCK(::cs_main);
     529           0 :         if (signHeight == -1) {
     530           0 :             signHeight = active_chain.Height();
     531           0 :         }
     532           0 :         int startBlockHeight = signHeight - signOffset;
     533           0 :         if (startBlockHeight > active_chain.Height() || startBlockHeight < 0) {
     534           0 :             return {};
     535             :         }
     536           0 :         pindexStart = active_chain[startBlockHeight];
     537           0 :     }
     538             : 
     539             :     // don't remove connections for the currently in-progress DKG round
     540           0 :     if (IsQuorumRotationEnabled(llmq_params, pindexStart)) {
     541           0 :         auto quorums = qman.ScanQuorums(llmq_params.type, pindexStart, poolSize);
     542           0 :         if (quorums.empty()) {
     543           0 :             return nullptr;
     544             :         }
     545             :         //log2 int
     546           0 :         int n = std::log2(llmq_params.signingActiveQuorumCount);
     547             :         //Extract last 64 bits of selectionHash
     548           0 :         uint64_t b = selectionHash.GetUint64(3);
     549             :         //Take last n bits of b
     550           0 :         uint64_t signer = (((1ull << n) - 1) & (b >> (64 - n - 1)));
     551             : 
     552           0 :         if (signer > quorums.size()) {
     553           0 :             return nullptr;
     554             :         }
     555           0 :         auto itQuorum = std::find_if(quorums.begin(),
     556           0 :                                      quorums.end(),
     557           0 :                                      [signer](const CQuorumCPtr& obj) {
     558           0 :                                          return uint64_t(obj->qc->quorumIndex) == signer;
     559             :                                      });
     560           0 :         if (itQuorum == quorums.end()) {
     561           0 :             return nullptr;
     562             :         }
     563           0 :         return *itQuorum;
     564           0 :     } else {
     565           0 :         auto quorums = qman.ScanQuorums(llmq_params.type, pindexStart, poolSize);
     566           0 :         if (quorums.empty()) {
     567           0 :             return nullptr;
     568             :         }
     569             : 
     570           0 :         std::vector<std::pair<uint256, size_t>> scores;
     571           0 :         scores.reserve(quorums.size());
     572           0 :         for (const auto i : util::irange(quorums.size())) {
     573           0 :             CHashWriter h(SER_NETWORK, 0);
     574           0 :             h << llmq_params.type;
     575           0 :             h << quorums[i]->qc->quorumHash;
     576           0 :             h << selectionHash;
     577           0 :             scores.emplace_back(h.GetHash(), i);
     578             :         }
     579           0 :         std::sort(scores.begin(), scores.end());
     580           0 :         return quorums[scores.front().second];
     581           0 :     }
     582           0 : }
     583             : 
     584           0 : VerifyRecSigStatus VerifyRecoveredSig(Consensus::LLMQType llmqType, const CChain& active_chain, const CQuorumManager& qman,
     585             :                         int signedAtHeight, const uint256& id, const uint256& msgHash, const CBLSSignature& sig,
     586             :                         const int signOffset)
     587             : {
     588           0 :     const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
     589           0 :     assert(llmq_params_opt.has_value());
     590           0 :     auto quorum = SelectQuorumForSigning(llmq_params_opt.value(), active_chain, qman, id, signedAtHeight, signOffset);
     591           0 :     if (!quorum) {
     592           0 :         return VerifyRecSigStatus::NoQuorum;
     593             :     }
     594             : 
     595           0 :     SignHash signHash{llmqType, quorum->qc->quorumHash, id, msgHash};
     596           0 :     const bool ret = sig.VerifyInsecure(quorum->qc->quorumPublicKey, signHash.Get());
     597           0 :     return ret ? VerifyRecSigStatus::Valid : VerifyRecSigStatus::Invalid;
     598           0 : }
     599             : 
     600             : } // namespace llmq

Generated by: LCOV version 1.16