LCOV - code coverage report
Current view: top level - src/llmq - dkgsessionmgr.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 170 178 95.5 %
Date: 2026-06-25 07:23:43 Functions: 16 16 100.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        1998 : CDKGSessionManager::CDKGSessionManager(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman,
      30             :                                        const ChainstateManager& chainman, const CSporkManager& sporkman,
      31             :                                        const util::DbWrapperParams& db_params) :
      32         666 :     m_dmnman{dmnman},
      33         666 :     m_qsnapman{qsnapman},
      34         666 :     m_chainman{chainman},
      35         666 :     m_sporkman{sporkman},
      36         666 :     db{util::MakeDbWrapper({db_params.path / "llmq" / "dkgdb", db_params.memory, db_params.wipe, /*cache_size=*/1 << 20})}
      37         666 : {
      38        1332 : }
      39             : 
      40        1332 : CDKGSessionManager::~CDKGSessionManager() = default;
      41             : 
      42       81279 : void CDKGSessionManager::UpdatedBlockTip(const CBlockIndex* pindexNew, bool fInitialDownload)
      43             : {
      44       81279 :     CleanupCache();
      45             : 
      46       81279 :     if (fInitialDownload)
      47           0 :         return;
      48       81279 :     if (!DeploymentDIP0003Enforced(pindexNew->nHeight, Params().GetConsensus()))
      49        7164 :         return;
      50       74115 :     if (!IsQuorumDKGEnabled(m_sporkman))
      51       17123 :         return;
      52             : 
      53      398944 :     for (auto& [_, dkgType] : dkgSessionHandlers) {
      54      341952 :         Assert(dkgType)->UpdatedBlockTip(pindexNew);
      55             :     }
      56       81279 : }
      57             : 
      58        6792 : bool CDKGSessionManager::GetContribution(const uint256& hash, CDKGContribution& ret) const
      59             : {
      60        6792 :     if (!IsQuorumDKGEnabled(m_sporkman))
      61           0 :         return false;
      62             : 
      63       24507 :     for (const auto& [_, dkgType] : dkgSessionHandlers) {
      64       17715 :         const auto dkgPhase = Assert(dkgType)->GetPhase();
      65       17715 :         if (dkgPhase < QuorumPhase::Initialized || dkgPhase > QuorumPhase::Contribute) {
      66        4678 :             continue;
      67             :         }
      68       13037 :         if (dkgType->GetContribution(hash, ret)) {
      69        6518 :             return true;
      70             :         }
      71             :     }
      72         274 :     return false;
      73        6792 : }
      74             : 
      75        1214 : bool CDKGSessionManager::GetComplaint(const uint256& hash, CDKGComplaint& ret) const
      76             : {
      77        1214 :     if (!IsQuorumDKGEnabled(m_sporkman))
      78           7 :         return false;
      79             : 
      80        5047 :     for (const auto& [_, dkgType] : dkgSessionHandlers) {
      81        3840 :         const auto dkgPhase = Assert(dkgType)->GetPhase();
      82        3840 :         if (dkgPhase < QuorumPhase::Contribute || dkgPhase > QuorumPhase::Complain) {
      83        2211 :             continue;
      84             :         }
      85        1629 :         if (dkgType->GetComplaint(hash, ret)) {
      86        1057 :             return true;
      87             :         }
      88             :     }
      89         150 :     return false;
      90        1214 : }
      91             : 
      92          16 : bool CDKGSessionManager::GetJustification(const uint256& hash, CDKGJustification& ret) const
      93             : {
      94          16 :     if (!IsQuorumDKGEnabled(m_sporkman))
      95           0 :         return false;
      96             : 
      97          32 :     for (const auto& [_, dkgType] : dkgSessionHandlers) {
      98          16 :         const auto dkgPhase = Assert(dkgType)->GetPhase();
      99          16 :         if (dkgPhase < QuorumPhase::Complain || dkgPhase > QuorumPhase::Justify) {
     100           0 :             continue;
     101             :         }
     102          16 :         if (dkgType->GetJustification(hash, ret)) {
     103          16 :             return true;
     104             :         }
     105             :     }
     106           0 :     return false;
     107          16 : }
     108             : 
     109        5579 : bool CDKGSessionManager::GetPrematureCommitment(const uint256& hash, CDKGPrematureCommitment& ret) const
     110             : {
     111        5579 :     if (!IsQuorumDKGEnabled(m_sporkman))
     112           0 :         return false;
     113             : 
     114       18715 :     for (const auto& [_, dkgType] : dkgSessionHandlers) {
     115       13136 :         const auto dkgPhase = Assert(dkgType)->GetPhase();
     116       13136 :         if (dkgPhase < QuorumPhase::Justify || dkgPhase > QuorumPhase::Commit) {
     117        1901 :             continue;
     118             :         }
     119       11235 :         if (dkgType->GetPrematureCommitment(hash, ret)) {
     120        5548 :             return true;
     121             :         }
     122             :     }
     123          31 :     return false;
     124        5579 : }
     125             : 
     126        8948 : void CDKGSessionManager::WriteVerifiedVvecContribution(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex, const uint256& proTxHash, const BLSVerificationVectorPtr& vvec)
     127             : {
     128        8948 :     CDataStream s(SER_DISK, CLIENT_VERSION);
     129        8948 :     WriteCompactSize(s, vvec->size());
     130       34204 :     for (auto& pubkey : *vvec) {
     131       25256 :         s << CBLSPublicKeyVersionWrapper(pubkey, false);
     132             :     }
     133        8948 :     db->Write(std::make_tuple(DB_VVEC, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash), s);
     134        8948 : }
     135             : 
     136        8645 : void CDKGSessionManager::WriteVerifiedSkContribution(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex, const uint256& proTxHash, const CBLSSecretKey& skContribution)
     137             : {
     138        8645 :     db->Write(std::make_tuple(DB_SKCONTRIB, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash), skContribution);
     139        8645 : }
     140             : 
     141        8649 : void CDKGSessionManager::WriteEncryptedContributions(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex, const uint256& proTxHash, const CBLSIESMultiRecipientObjects<CBLSSecretKey>& contributions)
     142             : {
     143        8649 :     db->Write(std::make_tuple(DB_ENC_CONTRIB, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash), contributions);
     144        8649 : }
     145             : 
     146       13213 : 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       13213 :     auto members = utils::GetAllQuorumMembers(llmqType, {m_dmnman, m_qsnapman, m_chainman, pQuorumBaseBlockIndex});
     149             : 
     150       13213 :     memberIndexesRet.clear();
     151       13213 :     vvecsRet.clear();
     152       13213 :     skContributionsRet.clear();
     153       13213 :     memberIndexesRet.reserve(members.size());
     154       13213 :     vvecsRet.reserve(members.size());
     155       13213 :     skContributionsRet.reserve(members.size());
     156             : 
     157             :     // NOTE: the `cs_main` should not be locked under scope of `contributionsCacheCs`
     158       13213 :     LOCK(contributionsCacheCs);
     159       57495 :     for (const auto i : util::irange(members.size())) {
     160       46237 :         if (validMembers[i]) {
     161       45416 :             const uint256& proTxHash = members[i]->proTxHash;
     162       45416 :             ContributionsCacheKey cacheKey = {llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash};
     163       45416 :             auto it = contributionsCache.find(cacheKey);
     164       45416 :             if (it == contributionsCache.end()) {
     165        9606 :                 CDataStream s(SER_DISK, CLIENT_VERSION);
     166        9606 :                 if (!db->ReadDataStream(std::make_tuple(DB_VVEC, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash), s)) {
     167        1955 :                     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        1955 :                     return false;
     171             :                 }
     172        7651 :                 size_t vvec_size = ReadCompactSize(s);
     173        7651 :                 CBLSPublicKey pubkey;
     174        7651 :                 std::vector<CBLSPublicKey> qv;
     175       29165 :                 for ([[maybe_unused]] size_t _ : util::irange(vvec_size)) {
     176       21514 :                     s >> CBLSPublicKeyVersionWrapper(pubkey, false);
     177       21514 :                     qv.emplace_back(pubkey);
     178             :                 }
     179        7651 :                 auto vvecPtr = std::make_shared<std::vector<CBLSPublicKey>>(std::move(qv));
     180             : 
     181        7651 :                 CBLSSecretKey skContribution;
     182        7651 :                 db->Read(std::make_tuple(DB_SKCONTRIB, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash), skContribution);
     183             : 
     184        7651 :                 it = contributionsCache.emplace(cacheKey, ContributionsCacheEntry{SteadyClock::now(), vvecPtr, skContribution}).first;
     185        9606 :             }
     186             : 
     187       43461 :             memberIndexesRet.emplace_back(i);
     188       43461 :             vvecsRet.emplace_back(it->second.vvec);
     189       43461 :             skContributionsRet.emplace_back(it->second.skContribution);
     190       43461 :         }
     191             :     }
     192       11258 :     return true;
     193       13213 : }
     194             : 
     195          81 : 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          81 :     auto members = utils::GetAllQuorumMembers(llmqType, {m_dmnman, m_qsnapman, m_chainman, pQuorumBaseBlockIndex});
     198             : 
     199          81 :     vecRet.clear();
     200          81 :     vecRet.reserve(members.size());
     201             : 
     202          81 :     size_t nRequestedMemberIdx{std::numeric_limits<size_t>::max()};
     203         187 :     for (const auto i : util::irange(members.size())) {
     204             :         // cppcheck-suppress useStlAlgorithm
     205         187 :         if (members[i]->proTxHash == nProTxHash) {
     206          81 :             nRequestedMemberIdx = i;
     207          81 :             break;
     208             :         }
     209             :     }
     210          81 :     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         299 :     for (const auto i : util::irange(members.size())) {
     216         232 :         if (validMembers[i]) {
     217         231 :             CBLSIESMultiRecipientObjects<CBLSSecretKey> encryptedContributions;
     218         231 :             if (!db->Read(std::make_tuple(DB_ENC_CONTRIB, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), members[i]->proTxHash), encryptedContributions)) {
     219          14 :                 LogPrint(BCLog::LLMQ, "CDKGSessionManager::%s -- can't read from db, nProTxHash=%s\n", __func__, nProTxHash.ToString());
     220          14 :                 return false;
     221             :             }
     222         217 :             vecRet.emplace_back(encryptedContributions.Get(nRequestedMemberIdx));
     223         231 :         }
     224             :     }
     225          67 :     return true;
     226          81 : }
     227             : 
     228       81279 : void CDKGSessionManager::CleanupCache() const
     229             : {
     230       81279 :     LOCK(contributionsCacheCs);
     231       81279 :     const auto curTime = SteadyClock::now();
     232      592754 :     for (auto it = contributionsCache.begin(); it != contributionsCache.end(); ) {
     233      511475 :         if (curTime - it->second.entryTime > MAX_CONTRIBUTION_CACHE_TIME) {
     234        1510 :             it = contributionsCache.erase(it);
     235        1510 :         } else {
     236      509965 :             ++it;
     237             :         }
     238             :     }
     239       81279 : }
     240             : 
     241       10551 : void CDKGSessionManager::CleanupOldContributions() const
     242             : {
     243       10551 :     if (db->IsEmpty()) {
     244          57 :         return;
     245             :     }
     246             : 
     247       10494 :     const auto prefixes = {DB_VVEC, DB_SKCONTRIB, DB_ENC_CONTRIB};
     248             : 
     249       11334 :     for (const auto& params : Params().GetConsensus().llmqs) {
     250         840 :         LogPrint(BCLog::LLMQ, "CDKGSessionManager::%s -- looking for old entries for llmq type %d\n", __func__, std23::to_underlying(params.type));
     251             : 
     252         840 :         CDBBatch batch(*db);
     253       11166 :         size_t cnt_old{0}, cnt_all{0};
     254       13686 :         for (const auto& prefix : prefixes) {
     255       12846 :             std::unique_ptr<CDBIterator> pcursor(db->NewIterator());
     256       12846 :             auto start = std::make_tuple(prefix, params.type, uint256(), uint256());
     257       12846 :             decltype(start) k;
     258             : 
     259       12846 :             pcursor->Seek(start);
     260        2520 :             LOCK(::cs_main);
     261       19944 :             while (pcursor->Valid()) {
     262        9144 :                 if (!pcursor->GetKey(k) || std::get<0>(k) != prefix || std::get<1>(k) != params.type) {
     263        2046 :                     break;
     264             :                 }
     265        7098 :                 cnt_all++;
     266        7098 :                 const CBlockIndex* pindexQuorum = m_chainman.m_blockman.LookupBlockIndex(std::get<2>(k));
     267       14196 :                 if (pindexQuorum == nullptr ||
     268        7098 :                     m_chainman.ActiveHeight() - pindexQuorum->nHeight > params.max_store_depth()) {
     269             :                     // not found or too old
     270        7098 :                     batch.Erase(k);
     271        1935 :                     cnt_old++;
     272        1935 :                 }
     273        1935 :                 pcursor->Next();
     274             :             }
     275        2520 :             pcursor.reset();
     276       23172 :         }
     277         840 :         LogPrint(BCLog::LLMQ, "CDKGSessionManager::%s -- found %lld entries for llmq type %d\n", __func__, cnt_all, uint8_t(params.type));
     278         840 :         if (cnt_old > 0) {
     279          74 :             db->WriteBatch(batch);
     280          74 :             LogPrint(BCLog::LLMQ, "CDKGSessionManager::%s -- removed %lld old entries for llmq type %d\n", __func__, cnt_old, uint8_t(params.type));
     281          74 :         }
     282         840 :     }
     283      103485 : }
     284             : } // namespace llmq

Generated by: LCOV version 1.16