LCOV - code coverage report
Current view: top level - src/llmq - quorumsman.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 317 358 88.5 %
Date: 2026-06-25 07:23:43 Functions: 35 37 94.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        6126 : CQuorumManager::CQuorumManager(CBLSWorker& _blsWorker, CDeterministicMNManager& dmnman, CEvoDB& _evoDb,
      32             :                                CQuorumBlockProcessor& _quorumBlockProcessor, CQuorumSnapshotManager& qsnapman,
      33             :                                const ChainstateManager& chainman, const util::DbWrapperParams& db_params) :
      34        3063 :     blsWorker{_blsWorker},
      35        3063 :     m_dmnman{dmnman},
      36        3063 :     quorumBlockProcessor{_quorumBlockProcessor},
      37        3063 :     m_qsnapman{qsnapman},
      38        3063 :     m_chainman{chainman},
      39             :     db{util::MakeDbWrapper({db_params.path / "llmq" / "quorumdb", db_params.memory, db_params.wipe, /*cache_size=*/1 << 20})}
      40        3063 : {
      41             :     utils::InitQuorumsCache(mapQuorumsCache, m_chainman.GetConsensus(), /*limit_by_connections=*/false);
      42             :     m_cache_interrupt.reset();
      43        3063 :     m_cache_thread = std::thread(&util::TraceThread, "q-cache", [this] { CacheWarmingThreadMain(); });
      44             :     MigrateOldQuorumDB(_evoDb);
      45        3063 : }
      46             : 
      47        6126 : CQuorumManager::~CQuorumManager()
      48        3063 : {
      49        3063 :     if (m_cache_thread.joinable()) {
      50        3063 :         m_cache_interrupt();
      51        3063 :         m_cache_thread.join();
      52        3063 :     }
      53        6126 : }
      54             : 
      55          81 : 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          81 :     if (m_qdkgsman) {
      60          81 :         return m_qdkgsman->GetEncryptedContributions(llmq_type, block_index, valid_members, protx_hash, vec_enc);
      61             :     }
      62           0 :     return false;
      63          81 : }
      64             : 
      65        5355 : CQuorumPtr CQuorumManager::BuildQuorumFromCommitment(const Consensus::LLMQType llmqType, gsl::not_null<const CBlockIndex*> pQuorumBaseBlockIndex, bool populate_cache) const
      66             : {
      67        5355 :     const uint256& quorumHash{pQuorumBaseBlockIndex->GetBlockHash()};
      68             : 
      69       21420 :     auto [qc, minedBlockHash] = quorumBlockProcessor.GetMinedCommitment(llmqType, quorumHash);
      70        5355 :     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        5355 :     assert(qc.quorumHash == pQuorumBaseBlockIndex->GetBlockHash());
      75             : 
      76        5355 :     const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
      77        5355 :     assert(llmq_params_opt.has_value());
      78        5355 :     auto members = utils::GetAllQuorumMembers(qc.llmqType, {m_dmnman, m_qsnapman, m_chainman, pQuorumBaseBlockIndex});
      79             : 
      80        5355 :     auto quorum = std::make_shared<CQuorum>(llmq_params_opt.value(), blsWorker,
      81        5355 :         std::make_unique<CFinalCommitment>(std::move(qc)), pQuorumBaseBlockIndex, minedBlockHash, members);
      82             : 
      83        5355 :     if (populate_cache && llmq_params_opt->size == 1) {
      84         174 :         WITH_LOCK(m_cs_maps, mapQuorumsCache[llmqType].insert(quorumHash, quorum));
      85             : 
      86          87 :         return quorum;
      87             :     }
      88             : 
      89        5268 :     bool hasValidVvec = false;
      90       10536 :     if (WITH_LOCK(cs_db, return quorum->ReadContributions(*db))) {
      91         922 :         hasValidVvec = true;
      92         922 :     } else {
      93        4346 :         if (BuildQuorumContributions(quorum->qc, quorum)) {
      94        3792 :             WITH_LOCK(cs_db, quorum->WriteContributions(*db));
      95        1896 :             hasValidVvec = true;
      96        1896 :         } else {
      97        2450 :             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        5268 :     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        2818 :         QueueQuorumForWarming(quorum);
     106        2818 :     }
     107             : 
     108       10536 :     WITH_LOCK(m_cs_maps, mapQuorumsCache[llmqType].insert(quorumHash, quorum));
     109             : 
     110        5268 :     return quorum;
     111        5355 : }
     112             : 
     113        4346 : bool CQuorumManager::BuildQuorumContributions(const CFinalCommitmentPtr& fqc, const std::shared_ptr<CQuorum>& quorum) const
     114             : {
     115        4346 :     std::vector<uint16_t> memberIndexes;
     116        4346 :     std::vector<BLSVerificationVectorPtr> vvecs;
     117        4346 :     std::vector<CBLSSecretKey> skContributions;
     118        8041 :     if (!m_qdkgsman ||
     119        7390 :         !m_qdkgsman->GetVerifiedContributions((Consensus::LLMQType)fqc->llmqType, quorum->m_quorum_base_block_index,
     120        3695 :                                             fqc->validMembers, memberIndexes, vvecs, skContributions)) {
     121        2450 :         return false;
     122             :     }
     123             : 
     124        1896 :     cxxtimer::Timer t2(true);
     125        1896 :     quorum->SetVerificationVector(blsWorker.BuildQuorumVerificationVector(vvecs));
     126        1896 :     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        1896 :     if (!m_handler || !m_handler->SetQuorumSecretKeyShare(*quorum, skContributions)) {
     133          20 :         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          20 :     }
     137        1896 :     t2.stop();
     138             : 
     139        1896 :     LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- built quorum vvec and skShare. time=%d\n", __func__, t2.count());
     140             : 
     141        1896 :     return true;
     142        4346 : }
     143             : 
     144      200163 : bool CQuorumManager::HasQuorum(Consensus::LLMQType llmqType, const CQuorumBlockProcessor& quorum_block_processor, const uint256& quorumHash)
     145             : {
     146      200163 :     return quorum_block_processor.HasMinedCommitment(llmqType, quorumHash);
     147             : }
     148             : 
     149       78984 : std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType, size_t nCountRequested) const
     150             : {
     151      157968 :     const CBlockIndex* pindex = WITH_LOCK(::cs_main, return m_chainman.ActiveTip());
     152       78984 :     return ScanQuorums(llmqType, pindex, nCountRequested);
     153             : }
     154             : 
     155      950204 : std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType,
     156             :                                                      gsl::not_null<const CBlockIndex*> pindexStart,
     157             :                                                      size_t nCountRequested) const
     158             : {
     159      950204 :     if (nCountRequested == 0 || !m_chainman.IsQuorumTypeEnabled(llmqType, pindexStart)) {
     160      292690 :         return {};
     161             :     }
     162             : 
     163      657514 :     gsl::not_null<const CBlockIndex*> pindexStore{pindexStart};
     164      657514 :     const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
     165      657514 :     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      657514 :     const int quorumCycleStartHeight = pindexStart->nHeight - (pindexStart->nHeight % llmq_params_opt->dkgInterval);
     170      657514 :     const int quorumCycleMiningStartHeight = quorumCycleStartHeight + llmq_params_opt->dkgMiningWindowStart;
     171      657514 :     const int quorumCycleMiningEndHeight = quorumCycleStartHeight + llmq_params_opt->dkgMiningWindowEnd;
     172             : 
     173      657514 :     if (pindexStart->nHeight < quorumCycleMiningStartHeight) {
     174             :         // too early for this cycle, use the previous one
     175             :         // bail out if it's below genesis block
     176      291605 :         if (quorumCycleMiningEndHeight < llmq_params_opt->dkgInterval) return {};
     177      288595 :         pindexStore = pindexStart->GetAncestor(quorumCycleMiningEndHeight - llmq_params_opt->dkgInterval);
     178      654504 :     } else if (pindexStart->nHeight > quorumCycleMiningEndHeight) {
     179             :         // we are past the mining phase of this cycle, use it
     180      128373 :         pindexStore = pindexStart->GetAncestor(quorumCycleMiningEndHeight);
     181      128373 :     }
     182             :     // everything else is inside the mining phase of this cycle, no pindexStore adjustment needed
     183             : 
     184      654504 :     gsl::not_null<const CBlockIndex*> pIndexScanCommitments{pindexStore};
     185      654504 :     size_t nScanCommitments{nCountRequested};
     186      654504 :     std::vector<CQuorumCPtr> vecResultQuorums;
     187             : 
     188             :     {
     189      654504 :         LOCK(m_cs_maps);
     190      654504 :         if (scanQuorumsCache.empty()) {
     191        4590 :             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        3825 :                 scanQuorumsCache.try_emplace(llmq.type, llmq.max_cycles(llmq.keepOldConnections) * (llmq.dkgMiningWindowEnd - llmq.dkgMiningWindowStart));
     198             :             }
     199         765 :         }
     200      654504 :         auto& cache = scanQuorumsCache[llmqType];
     201      654504 :         bool fCacheExists = cache.get(pindexStore->GetBlockHash(), vecResultQuorums);
     202      654504 :         if (fCacheExists) {
     203             :             // We have exactly what requested so just return it
     204      282436 :             if (vecResultQuorums.size() == nCountRequested) {
     205      119178 :                 return vecResultQuorums;
     206             :             }
     207             :             // If we have more cached than requested return only a subvector
     208      163258 :             if (vecResultQuorums.size() > nCountRequested) {
     209       27846 :                 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      135412 :             if (!vecResultQuorums.empty()) {
     214      135412 :                 nScanCommitments -= vecResultQuorums.size();
     215             :                 // bail out if it's below genesis block
     216      135412 :                 if (vecResultQuorums.back()->m_quorum_base_block_index->pprev == nullptr) return {};
     217      135412 :                 pIndexScanCommitments = vecResultQuorums.back()->m_quorum_base_block_index->pprev;
     218      135412 :             }
     219      135412 :         } else {
     220             :             // If there is nothing in cache request at least keepOldConnections because this gets cached then later
     221      372068 :             nScanCommitments = std::max(nCountRequested, static_cast<size_t>(llmq_params_opt->keepOldConnections));
     222             :         }
     223      654504 :     }
     224             : 
     225             :     // Get the block indexes of the mined commitments to build the required quorums from
     226     1014960 :     std::vector<const CBlockIndex*> pQuorumBaseBlockIndexes{ llmq_params_opt->useRotation ?
     227      144190 :             quorumBlockProcessor.GetMinedCommitmentsIndexedUntilBlock(llmqType, pIndexScanCommitments, nScanCommitments) :
     228      363290 :             quorumBlockProcessor.GetMinedCommitmentsUntilBlock(llmqType, pIndexScanCommitments, nScanCommitments)
     229             :     };
     230      507480 :     vecResultQuorums.reserve(vecResultQuorums.size() + pQuorumBaseBlockIndexes.size());
     231             : 
     232      607372 :     for (auto& pQuorumBaseBlockIndex : pQuorumBaseBlockIndexes) {
     233       99892 :         assert(pQuorumBaseBlockIndex);
     234             :         // populate cache for keepOldConnections most recent quorums only
     235       99892 :         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       99892 :         auto quorum = GetQuorum(llmqType, pQuorumBaseBlockIndex, populate_cache);
     241       99892 :         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       99892 :         vecResultQuorums.emplace_back(quorum);
     248       99892 :     }
     249             : 
     250      507480 :     const size_t nCountResult{vecResultQuorums.size()};
     251      507480 :     if (nCountResult > 0) {
     252      177476 :         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      177476 :         auto& cache = scanQuorumsCache[llmqType];
     257      177476 :         const size_t nCacheEndIndex = std::min(nCountResult, static_cast<size_t>(llmq_params_opt->keepOldConnections));
     258      177476 :         cache.emplace(pindexStore->GetBlockHash(), {vecResultQuorums.begin(), vecResultQuorums.begin() + nCacheEndIndex});
     259      177476 :     }
     260             :     // Don't return more than nCountRequested elements
     261      507480 :     const size_t nResultEndIndex = std::min(nCountResult, nCountRequested);
     262      507480 :     return {vecResultQuorums.begin(), vecResultQuorums.begin() + nResultEndIndex};
     263      950208 : }
     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        8932 : bool CQuorumManager::IsWatching() const
     274             : {
     275        8932 :     if (m_handler) {
     276        4913 :         return m_handler->IsWatching();
     277             :     }
     278        4019 :     return false;
     279        8932 : }
     280             : 
     281         223 : bool CQuorumManager::IsDataRequestPending(const uint256& proRegTx, bool we_requested, const uint256& quorumHash,
     282             :                                           Consensus::LLMQType llmqType) const
     283             : {
     284         223 :     const CQuorumDataRequestKey key{proRegTx, we_requested, quorumHash, llmqType};
     285         223 :     LOCK(cs_data_requests);
     286         223 :     const auto it = mapQuorumDataRequests.find(key);
     287         223 :     return it != mapQuorumDataRequests.end() && !it->second.IsExpired(/*add_bias=*/true);
     288         223 : }
     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       79784 : void CQuorumManager::CleanupExpiredDataRequests() const
     306             : {
     307       79784 :     LOCK(cs_data_requests);
     308       79784 :     auto it = mapQuorumDataRequests.begin();
     309       80226 :     while (it != mapQuorumDataRequests.end()) {
     310         442 :         if (it->second.IsExpired(/*add_bias=*/true)) {
     311         262 :             it = mapQuorumDataRequests.erase(it);
     312         262 :         } else {
     313         180 :             ++it;
     314             :         }
     315             :     }
     316       79784 : }
     317             : 
     318          54 : void CQuorumManager::CleanupOldQuorumData(const Uint256HashSet& dbKeysToSkip) const
     319             : {
     320          54 :     LOCK(cs_db);
     321          54 :     DataCleanupHelper(*db, dbKeysToSkip);
     322          54 : }
     323             : 
     324      100279 : CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash) const
     325             : {
     326      200558 :     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      200558 :         if (!WITH_LOCK(cs_quorumBaseBlockIndexCache, return quorumBaseBlockIndexCache.get(quorumHash, pindex))) {
     332        4256 :             pindex = WITH_LOCK(::cs_main, return m_chainman.m_blockman.LookupBlockIndex(quorumHash));
     333        2128 :             if (pindex) {
     334        2121 :                 LOCK(cs_quorumBaseBlockIndexCache);
     335        2121 :                 quorumBaseBlockIndexCache.insert(quorumHash, pindex);
     336        2121 :             }
     337        2128 :         }
     338      100279 :         return pindex;
     339           0 :     }();
     340      100279 :     if (!pQuorumBaseBlockIndex) {
     341           7 :         LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- block %s not found\n", __func__, quorumHash.ToString());
     342           7 :         return nullptr;
     343             :     }
     344      100272 :     return GetQuorum(llmqType, pQuorumBaseBlockIndex);
     345      100279 : }
     346             : 
     347      200164 : CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, gsl::not_null<const CBlockIndex*> pQuorumBaseBlockIndex, bool populate_cache) const
     348             : {
     349      200164 :     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      200164 :     if (!HasQuorum(llmqType, quorumBlockProcessor, quorumHash)) {
     354          11 :         return nullptr;
     355             :     }
     356             : 
     357      200153 :     CQuorumPtr pQuorum;
     358      394951 :     if (LOCK(m_cs_maps); mapQuorumsCache[llmqType].get(quorumHash, pQuorum)) {
     359      194798 :         return pQuorum;
     360             :     }
     361             : 
     362        5355 :     return BuildQuorumFromCommitment(llmqType, pQuorumBaseBlockIndex, populate_cache);
     363      200164 : }
     364             : 
     365         496 : bool CQuorumManager::RegisterDataRequest(const CQuorumDataRequestKey& key, const CQuorumDataRequest& request,
     366             :                                           bool add_expiry_bias) const
     367             : {
     368         496 :     LOCK(cs_data_requests);
     369         539 :     auto [old_pair, inserted] = mapQuorumDataRequests.emplace(key, request);
     370         496 :     if (!inserted) {
     371         133 :         if (old_pair->second.IsExpired(add_expiry_bias)) {
     372          86 :             old_pair->second = request;
     373          43 :             return true;
     374             :         }
     375          90 :         return false;
     376             :     }
     377         363 :     return true;
     378         496 : }
     379             : 
     380         179 : CQuorumManager::DataResponseValidation CQuorumManager::ValidateDataResponse(
     381             :     const CQuorumDataRequestKey& key, const CQuorumDataRequest& response) const
     382             : {
     383         179 :     LOCK(cs_data_requests);
     384         179 :     auto it = mapQuorumDataRequests.find(key);
     385         179 :     if (it == mapQuorumDataRequests.end()) {
     386           6 :         return DataResponseValidation::NotRequested;
     387             :     }
     388         173 :     if (it->second.IsProcessed()) {
     389           6 :         return DataResponseValidation::AlreadyReceived;
     390             :     }
     391         167 :     if (response != it->second) {
     392          18 :         return DataResponseValidation::Mismatch;
     393             :     }
     394         149 :     it->second.SetProcessed();
     395         149 :     return DataResponseValidation::OK;
     396         179 : }
     397             : 
     398         130 : CQuorumPtr CQuorumManager::GetCachedMutableQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash) const
     399             : {
     400         130 :     CQuorumPtr pQuorum;
     401         130 :     LOCK(m_cs_maps);
     402         130 :     mapQuorumsCache[llmqType].get(quorumHash, pQuorum);
     403         130 :     return pQuorum;
     404         130 : }
     405             : 
     406         130 : void CQuorumManager::WriteContributions(const CQuorumPtr& quorum) const
     407             : {
     408         130 :     LOCK(cs_db);
     409         130 :     quorum->WriteContributions(*db);
     410         130 : }
     411             : 
     412        3063 : void CQuorumManager::CacheWarmingThreadMain() const
     413             : {
     414      400319 :     while (!m_cache_interrupt) {
     415      397256 :         CQuorumCPtr pQuorum;
     416             :         {
     417      397256 :             LOCK(m_cache_cs);
     418      397256 :             if (!m_cache_queue.empty()) {
     419        2948 :                 pQuorum = std::move(m_cache_queue.front());
     420        2948 :                 m_cache_queue.pop_front();
     421        2948 :             };
     422      397256 :         }
     423             : 
     424      397256 :         if (!pQuorum) {
     425      394308 :             m_cache_interrupt.sleep_for(std::chrono::milliseconds(100));
     426      394308 :             continue;
     427             :         }
     428             : 
     429        2948 :         cxxtimer::Timer t(true);
     430        2948 :         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       14009 :         for (const auto i : util::irange(pQuorum->members.size())) {
     436       11061 :             if (m_cache_interrupt) {
     437           0 :                 break;
     438             :             }
     439       11061 :             if (pQuorum->qc->validMembers[i]) {
     440       10906 :                 pQuorum->GetPubKeyShare(i);
     441       10906 :             }
     442             :         }
     443             : 
     444        2948 :         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      397256 :     }
     448        3063 : }
     449             : 
     450        2948 : void CQuorumManager::QueueQuorumForWarming(CQuorumCPtr pQuorum) const
     451             : {
     452        2948 :     if (pQuorum->HasVerificationVector()) {
     453        2948 :         LOCK(m_cache_cs);
     454        2948 :         m_cache_queue.push_back(std::move(pQuorum));
     455        2948 :     }
     456        2948 : }
     457             : 
     458             : // TODO: remove in v23
     459        3063 : void CQuorumManager::MigrateOldQuorumDB(CEvoDB& evoDb) const
     460             : {
     461        3063 :     LOCK(cs_db);
     462        3063 :     if (!db->IsEmpty()) return;
     463             : 
     464        2868 :     const auto prefixes = {DB_QUORUM_QUORUM_VVEC, DB_QUORUM_SK_SHARE};
     465             : 
     466        2868 :     LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- start\n", __func__);
     467             : 
     468        2868 :     CDBBatch batch(*db);
     469        2868 :     std::unique_ptr<CDBIterator> pcursor(evoDb.GetRawDB().NewIterator());
     470             : 
     471        8604 :     for (const auto& prefix : prefixes) {
     472        5736 :         auto start = std::make_tuple(prefix, uint256());
     473        5736 :         pcursor->Seek(start);
     474             : 
     475        5736 :         int count{0};
     476        5736 :         while (pcursor->Valid()) {
     477        1541 :             decltype(start) k;
     478        1541 :             CDataStream s(SER_DISK, CLIENT_VERSION);
     479        1541 :             CBLSSecretKey sk;
     480             : 
     481        1541 :             if (!pcursor->GetKey(k) || std::get<0>(k) != prefix) {
     482        1541 :                 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        1541 :         }
     506             : 
     507        5736 :         db->WriteBatch(batch);
     508             : 
     509        5736 :         LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- %s moved %d\n", __func__, prefix, count);
     510        5736 :     }
     511             : 
     512        2868 :     pcursor.reset();
     513        2868 :     db->CompactFull();
     514             : 
     515        2868 :     DataCleanupHelper(evoDb.GetRawDB(), {});
     516        2868 :     evoDb.CommitRootTransaction();
     517             : 
     518        2868 :     LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- done\n", __func__);
     519        3063 : }
     520             : 
     521       82937 : CQuorumCPtr SelectQuorumForSigning(const Consensus::LLMQParams& llmq_params, const CChain& active_chain, const CQuorumManager& qman,
     522             :                                    const uint256& selectionHash, int signHeight, int signOffset)
     523             : {
     524       82937 :     size_t poolSize = llmq_params.signingActiveQuorumCount;
     525             : 
     526             :     CBlockIndex* pindexStart;
     527             :     {
     528       82937 :         LOCK(::cs_main);
     529       82937 :         if (signHeight == -1) {
     530       64632 :             signHeight = active_chain.Height();
     531       64632 :         }
     532       82937 :         int startBlockHeight = signHeight - signOffset;
     533       82937 :         if (startBlockHeight > active_chain.Height() || startBlockHeight < 0) {
     534           9 :             return {};
     535             :         }
     536       82928 :         pindexStart = active_chain[startBlockHeight];
     537       82937 :     }
     538             : 
     539             :     // don't remove connections for the currently in-progress DKG round
     540       82928 :     if (IsQuorumRotationEnabled(llmq_params, pindexStart)) {
     541        5698 :         auto quorums = qman.ScanQuorums(llmq_params.type, pindexStart, poolSize);
     542        5698 :         if (quorums.empty()) {
     543        2239 :             return nullptr;
     544             :         }
     545             :         //log2 int
     546        3459 :         int n = std::log2(llmq_params.signingActiveQuorumCount);
     547             :         //Extract last 64 bits of selectionHash
     548        3459 :         uint64_t b = selectionHash.GetUint64(3);
     549             :         //Take last n bits of b
     550        3459 :         uint64_t signer = (((1ull << n) - 1) & (b >> (64 - n - 1)));
     551             : 
     552        3459 :         if (signer > quorums.size()) {
     553           0 :             return nullptr;
     554             :         }
     555        6918 :         auto itQuorum = std::find_if(quorums.begin(),
     556        3459 :                                      quorums.end(),
     557        8132 :                                      [signer](const CQuorumCPtr& obj) {
     558        4673 :                                          return uint64_t(obj->qc->quorumIndex) == signer;
     559             :                                      });
     560        3459 :         if (itQuorum == quorums.end()) {
     561         564 :             return nullptr;
     562             :         }
     563        2895 :         return *itQuorum;
     564        5698 :     } else {
     565       77230 :         auto quorums = qman.ScanQuorums(llmq_params.type, pindexStart, poolSize);
     566       77230 :         if (quorums.empty()) {
     567       27980 :             return nullptr;
     568             :         }
     569             : 
     570       49250 :         std::vector<std::pair<uint256, size_t>> scores;
     571       49250 :         scores.reserve(quorums.size());
     572      136310 :         for (const auto i : util::irange(quorums.size())) {
     573       87060 :             CHashWriter h(SER_NETWORK, 0);
     574       87060 :             h << llmq_params.type;
     575       87060 :             h << quorums[i]->qc->quorumHash;
     576       87060 :             h << selectionHash;
     577       87060 :             scores.emplace_back(h.GetHash(), i);
     578             :         }
     579       49250 :         std::sort(scores.begin(), scores.end());
     580       49250 :         return quorums[scores.front().second];
     581       77230 :     }
     582       82937 : }
     583             : 
     584       18185 : 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       18185 :     const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
     589       18185 :     assert(llmq_params_opt.has_value());
     590       18185 :     auto quorum = SelectQuorumForSigning(llmq_params_opt.value(), active_chain, qman, id, signedAtHeight, signOffset);
     591       18185 :     if (!quorum) {
     592          28 :         return VerifyRecSigStatus::NoQuorum;
     593             :     }
     594             : 
     595       18157 :     SignHash signHash{llmqType, quorum->qc->quorumHash, id, msgHash};
     596       18157 :     const bool ret = sig.VerifyInsecure(quorum->qc->quorumPublicKey, signHash.Get());
     597       18157 :     return ret ? VerifyRecSigStatus::Valid : VerifyRecSigStatus::Invalid;
     598       18185 : }
     599             : 
     600             : } // namespace llmq

Generated by: LCOV version 1.16