LCOV - code coverage report
Current view: top level - src/llmq - dkgsessionmgr.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 0 178 0.0 %
Date: 2026-06-25 07:23:51 Functions: 0 16 0.0 %

          Line data    Source code
       1             : // Copyright (c) 2018-2025 The Dash Core developers
       2             : // Distributed under the MIT/X11 software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <llmq/dkgsessionmgr.h>
       6             : 
       7             : #include <bls/bls_ies.h>
       8             : #include <evo/deterministicmns.h>
       9             : #include <llmq/dkgsessionhandler.h>
      10             : #include <llmq/options.h>
      11             : #include <llmq/utils.h>
      12             : #include <msg_result.h>
      13             : #include <spork.h>
      14             : #include <unordered_lru_cache.h>
      15             : #include <util/helpers.h>
      16             : #include <util/std23.h>
      17             : 
      18             : #include <chainparams.h>
      19             : #include <dbwrapper.h>
      20             : #include <deploymentstatus.h>
      21             : #include <validation.h>
      22             : 
      23             : namespace llmq
      24             : {
      25             : static const std::string DB_VVEC = "qdkg_V";
      26             : static const std::string DB_SKCONTRIB = "qdkg_S";
      27             : static const std::string DB_ENC_CONTRIB = "qdkg_E";
      28             : 
      29           0 : CDKGSessionManager::CDKGSessionManager(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman,
      30             :                                        const ChainstateManager& chainman, const CSporkManager& sporkman,
      31             :                                        const util::DbWrapperParams& db_params) :
      32           0 :     m_dmnman{dmnman},
      33           0 :     m_qsnapman{qsnapman},
      34           0 :     m_chainman{chainman},
      35           0 :     m_sporkman{sporkman},
      36           0 :     db{util::MakeDbWrapper({db_params.path / "llmq" / "dkgdb", db_params.memory, db_params.wipe, /*cache_size=*/1 << 20})}
      37           0 : {
      38           0 : }
      39             : 
      40           0 : CDKGSessionManager::~CDKGSessionManager() = default;
      41             : 
      42           0 : void CDKGSessionManager::UpdatedBlockTip(const CBlockIndex* pindexNew, bool fInitialDownload)
      43             : {
      44           0 :     CleanupCache();
      45             : 
      46           0 :     if (fInitialDownload)
      47           0 :         return;
      48           0 :     if (!DeploymentDIP0003Enforced(pindexNew->nHeight, Params().GetConsensus()))
      49           0 :         return;
      50           0 :     if (!IsQuorumDKGEnabled(m_sporkman))
      51           0 :         return;
      52             : 
      53           0 :     for (auto& [_, dkgType] : dkgSessionHandlers) {
      54           0 :         Assert(dkgType)->UpdatedBlockTip(pindexNew);
      55             :     }
      56           0 : }
      57             : 
      58           0 : bool CDKGSessionManager::GetContribution(const uint256& hash, CDKGContribution& ret) const
      59             : {
      60           0 :     if (!IsQuorumDKGEnabled(m_sporkman))
      61           0 :         return false;
      62             : 
      63           0 :     for (const auto& [_, dkgType] : dkgSessionHandlers) {
      64           0 :         const auto dkgPhase = Assert(dkgType)->GetPhase();
      65           0 :         if (dkgPhase < QuorumPhase::Initialized || dkgPhase > QuorumPhase::Contribute) {
      66           0 :             continue;
      67             :         }
      68           0 :         if (dkgType->GetContribution(hash, ret)) {
      69           0 :             return true;
      70             :         }
      71             :     }
      72           0 :     return false;
      73           0 : }
      74             : 
      75           0 : bool CDKGSessionManager::GetComplaint(const uint256& hash, CDKGComplaint& ret) const
      76             : {
      77           0 :     if (!IsQuorumDKGEnabled(m_sporkman))
      78           0 :         return false;
      79             : 
      80           0 :     for (const auto& [_, dkgType] : dkgSessionHandlers) {
      81           0 :         const auto dkgPhase = Assert(dkgType)->GetPhase();
      82           0 :         if (dkgPhase < QuorumPhase::Contribute || dkgPhase > QuorumPhase::Complain) {
      83           0 :             continue;
      84             :         }
      85           0 :         if (dkgType->GetComplaint(hash, ret)) {
      86           0 :             return true;
      87             :         }
      88             :     }
      89           0 :     return false;
      90           0 : }
      91             : 
      92           0 : bool CDKGSessionManager::GetJustification(const uint256& hash, CDKGJustification& ret) const
      93             : {
      94           0 :     if (!IsQuorumDKGEnabled(m_sporkman))
      95           0 :         return false;
      96             : 
      97           0 :     for (const auto& [_, dkgType] : dkgSessionHandlers) {
      98           0 :         const auto dkgPhase = Assert(dkgType)->GetPhase();
      99           0 :         if (dkgPhase < QuorumPhase::Complain || dkgPhase > QuorumPhase::Justify) {
     100           0 :             continue;
     101             :         }
     102           0 :         if (dkgType->GetJustification(hash, ret)) {
     103           0 :             return true;
     104             :         }
     105             :     }
     106           0 :     return false;
     107           0 : }
     108             : 
     109           0 : bool CDKGSessionManager::GetPrematureCommitment(const uint256& hash, CDKGPrematureCommitment& ret) const
     110             : {
     111           0 :     if (!IsQuorumDKGEnabled(m_sporkman))
     112           0 :         return false;
     113             : 
     114           0 :     for (const auto& [_, dkgType] : dkgSessionHandlers) {
     115           0 :         const auto dkgPhase = Assert(dkgType)->GetPhase();
     116           0 :         if (dkgPhase < QuorumPhase::Justify || dkgPhase > QuorumPhase::Commit) {
     117           0 :             continue;
     118             :         }
     119           0 :         if (dkgType->GetPrematureCommitment(hash, ret)) {
     120           0 :             return true;
     121             :         }
     122             :     }
     123           0 :     return false;
     124           0 : }
     125             : 
     126           0 : void CDKGSessionManager::WriteVerifiedVvecContribution(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex, const uint256& proTxHash, const BLSVerificationVectorPtr& vvec)
     127             : {
     128           0 :     CDataStream s(SER_DISK, CLIENT_VERSION);
     129           0 :     WriteCompactSize(s, vvec->size());
     130           0 :     for (auto& pubkey : *vvec) {
     131           0 :         s << CBLSPublicKeyVersionWrapper(pubkey, false);
     132             :     }
     133           0 :     db->Write(std::make_tuple(DB_VVEC, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash), s);
     134           0 : }
     135             : 
     136           0 : void CDKGSessionManager::WriteVerifiedSkContribution(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex, const uint256& proTxHash, const CBLSSecretKey& skContribution)
     137             : {
     138           0 :     db->Write(std::make_tuple(DB_SKCONTRIB, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash), skContribution);
     139           0 : }
     140             : 
     141           0 : void CDKGSessionManager::WriteEncryptedContributions(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex, const uint256& proTxHash, const CBLSIESMultiRecipientObjects<CBLSSecretKey>& contributions)
     142             : {
     143           0 :     db->Write(std::make_tuple(DB_ENC_CONTRIB, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash), contributions);
     144           0 : }
     145             : 
     146           0 : bool CDKGSessionManager::GetVerifiedContributions(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex, const std::vector<bool>& validMembers, std::vector<uint16_t>& memberIndexesRet, std::vector<BLSVerificationVectorPtr>& vvecsRet, std::vector<CBLSSecretKey>& skContributionsRet) const
     147             : {
     148           0 :     auto members = utils::GetAllQuorumMembers(llmqType, {m_dmnman, m_qsnapman, m_chainman, pQuorumBaseBlockIndex});
     149             : 
     150           0 :     memberIndexesRet.clear();
     151           0 :     vvecsRet.clear();
     152           0 :     skContributionsRet.clear();
     153           0 :     memberIndexesRet.reserve(members.size());
     154           0 :     vvecsRet.reserve(members.size());
     155           0 :     skContributionsRet.reserve(members.size());
     156             : 
     157             :     // NOTE: the `cs_main` should not be locked under scope of `contributionsCacheCs`
     158           0 :     LOCK(contributionsCacheCs);
     159           0 :     for (const auto i : util::irange(members.size())) {
     160           0 :         if (validMembers[i]) {
     161           0 :             const uint256& proTxHash = members[i]->proTxHash;
     162           0 :             ContributionsCacheKey cacheKey = {llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash};
     163           0 :             auto it = contributionsCache.find(cacheKey);
     164           0 :             if (it == contributionsCache.end()) {
     165           0 :                 CDataStream s(SER_DISK, CLIENT_VERSION);
     166           0 :                 if (!db->ReadDataStream(std::make_tuple(DB_VVEC, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash), s)) {
     167           0 :                     LogPrint(BCLog::LLMQ, "%s -- this node does not have vvec for llmq=%d block=%s protx=%s\n",
     168             :                              __func__, std23::to_underlying(llmqType), pQuorumBaseBlockIndex->GetBlockHash().ToString(),
     169             :                              proTxHash.ToString());
     170           0 :                     return false;
     171             :                 }
     172           0 :                 size_t vvec_size = ReadCompactSize(s);
     173           0 :                 CBLSPublicKey pubkey;
     174           0 :                 std::vector<CBLSPublicKey> qv;
     175           0 :                 for ([[maybe_unused]] size_t _ : util::irange(vvec_size)) {
     176           0 :                     s >> CBLSPublicKeyVersionWrapper(pubkey, false);
     177           0 :                     qv.emplace_back(pubkey);
     178             :                 }
     179           0 :                 auto vvecPtr = std::make_shared<std::vector<CBLSPublicKey>>(std::move(qv));
     180             : 
     181           0 :                 CBLSSecretKey skContribution;
     182           0 :                 db->Read(std::make_tuple(DB_SKCONTRIB, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash), skContribution);
     183             : 
     184           0 :                 it = contributionsCache.emplace(cacheKey, ContributionsCacheEntry{SteadyClock::now(), vvecPtr, skContribution}).first;
     185           0 :             }
     186             : 
     187           0 :             memberIndexesRet.emplace_back(i);
     188           0 :             vvecsRet.emplace_back(it->second.vvec);
     189           0 :             skContributionsRet.emplace_back(it->second.skContribution);
     190           0 :         }
     191             :     }
     192           0 :     return true;
     193           0 : }
     194             : 
     195           0 : bool CDKGSessionManager::GetEncryptedContributions(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex, const std::vector<bool>& validMembers, const uint256& nProTxHash, std::vector<CBLSIESEncryptedObject<CBLSSecretKey>>& vecRet) const
     196             : {
     197           0 :     auto members = utils::GetAllQuorumMembers(llmqType, {m_dmnman, m_qsnapman, m_chainman, pQuorumBaseBlockIndex});
     198             : 
     199           0 :     vecRet.clear();
     200           0 :     vecRet.reserve(members.size());
     201             : 
     202           0 :     size_t nRequestedMemberIdx{std::numeric_limits<size_t>::max()};
     203           0 :     for (const auto i : util::irange(members.size())) {
     204             :         // cppcheck-suppress useStlAlgorithm
     205           0 :         if (members[i]->proTxHash == nProTxHash) {
     206           0 :             nRequestedMemberIdx = i;
     207           0 :             break;
     208             :         }
     209             :     }
     210           0 :     if (nRequestedMemberIdx == std::numeric_limits<size_t>::max()) {
     211           0 :         LogPrint(BCLog::LLMQ, "CDKGSessionManager::%s -- not a member, nProTxHash=%s\n", __func__, nProTxHash.ToString());
     212           0 :         return false;
     213             :     }
     214             : 
     215           0 :     for (const auto i : util::irange(members.size())) {
     216           0 :         if (validMembers[i]) {
     217           0 :             CBLSIESMultiRecipientObjects<CBLSSecretKey> encryptedContributions;
     218           0 :             if (!db->Read(std::make_tuple(DB_ENC_CONTRIB, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), members[i]->proTxHash), encryptedContributions)) {
     219           0 :                 LogPrint(BCLog::LLMQ, "CDKGSessionManager::%s -- can't read from db, nProTxHash=%s\n", __func__, nProTxHash.ToString());
     220           0 :                 return false;
     221             :             }
     222           0 :             vecRet.emplace_back(encryptedContributions.Get(nRequestedMemberIdx));
     223           0 :         }
     224             :     }
     225           0 :     return true;
     226           0 : }
     227             : 
     228           0 : void CDKGSessionManager::CleanupCache() const
     229             : {
     230           0 :     LOCK(contributionsCacheCs);
     231           0 :     const auto curTime = SteadyClock::now();
     232           0 :     for (auto it = contributionsCache.begin(); it != contributionsCache.end(); ) {
     233           0 :         if (curTime - it->second.entryTime > MAX_CONTRIBUTION_CACHE_TIME) {
     234           0 :             it = contributionsCache.erase(it);
     235           0 :         } else {
     236           0 :             ++it;
     237             :         }
     238             :     }
     239           0 : }
     240             : 
     241           0 : void CDKGSessionManager::CleanupOldContributions() const
     242             : {
     243           0 :     if (db->IsEmpty()) {
     244           0 :         return;
     245             :     }
     246             : 
     247           0 :     const auto prefixes = {DB_VVEC, DB_SKCONTRIB, DB_ENC_CONTRIB};
     248             : 
     249           0 :     for (const auto& params : Params().GetConsensus().llmqs) {
     250           0 :         LogPrint(BCLog::LLMQ, "CDKGSessionManager::%s -- looking for old entries for llmq type %d\n", __func__, std23::to_underlying(params.type));
     251             : 
     252           0 :         CDBBatch batch(*db);
     253           0 :         size_t cnt_old{0}, cnt_all{0};
     254           0 :         for (const auto& prefix : prefixes) {
     255           0 :             std::unique_ptr<CDBIterator> pcursor(db->NewIterator());
     256           0 :             auto start = std::make_tuple(prefix, params.type, uint256(), uint256());
     257           0 :             decltype(start) k;
     258             : 
     259           0 :             pcursor->Seek(start);
     260           0 :             LOCK(::cs_main);
     261           0 :             while (pcursor->Valid()) {
     262           0 :                 if (!pcursor->GetKey(k) || std::get<0>(k) != prefix || std::get<1>(k) != params.type) {
     263           0 :                     break;
     264             :                 }
     265           0 :                 cnt_all++;
     266           0 :                 const CBlockIndex* pindexQuorum = m_chainman.m_blockman.LookupBlockIndex(std::get<2>(k));
     267           0 :                 if (pindexQuorum == nullptr ||
     268           0 :                     m_chainman.ActiveHeight() - pindexQuorum->nHeight > params.max_store_depth()) {
     269             :                     // not found or too old
     270           0 :                     batch.Erase(k);
     271           0 :                     cnt_old++;
     272           0 :                 }
     273           0 :                 pcursor->Next();
     274             :             }
     275           0 :             pcursor.reset();
     276           0 :         }
     277           0 :         LogPrint(BCLog::LLMQ, "CDKGSessionManager::%s -- found %lld entries for llmq type %d\n", __func__, cnt_all, uint8_t(params.type));
     278           0 :         if (cnt_old > 0) {
     279           0 :             db->WriteBatch(batch);
     280           0 :             LogPrint(BCLog::LLMQ, "CDKGSessionManager::%s -- removed %lld old entries for llmq type %d\n", __func__, cnt_old, uint8_t(params.type));
     281           0 :         }
     282           0 :     }
     283           0 : }
     284             : } // namespace llmq

Generated by: LCOV version 1.16