LCOV - code coverage report
Current view: top level - src/llmq - quorumsman.h (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 12 13 92.3 %
Date: 2026-06-25 07:23:43 Functions: 4 7 57.1 %

          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             : #ifndef BITCOIN_LLMQ_QUORUMSMAN_H
       6             : #define BITCOIN_LLMQ_QUORUMSMAN_H
       7             : 
       8             : #include <bls/bls_ies.h>
       9             : #include <evo/types.h>
      10             : #include <llmq/params.h>
      11             : #include <llmq/quorums.h>
      12             : #include <llmq/types.h>
      13             : #include <saltedhasher.h>
      14             : #include <unordered_lru_cache.h>
      15             : 
      16             : #include <sync.h>
      17             : #include <util/threadinterrupt.h>
      18             : 
      19             : #include <gsl/pointers.h>
      20             : 
      21             : #include <deque>
      22             : #include <map>
      23             : #include <memory>
      24             : #include <thread>
      25             : 
      26             : class CBLSSignature;
      27             : class CBLSWorker;
      28             : class CBlockIndex;
      29             : class CChain;
      30             : class CDeterministicMNManager;
      31             : class CDBWrapper;
      32             : class CEvoDB;
      33             : class ChainstateManager;
      34             : namespace util {
      35             : struct DbWrapperParams;
      36             : } // namespace util
      37             : 
      38             : namespace llmq {
      39             : enum class VerifyRecSigStatus : uint8_t {
      40             :     NoQuorum,
      41             :     Invalid,
      42             :     Valid,
      43             : };
      44             : 
      45             : class QuorumRole;
      46             : class CDKGSessionManager;
      47             : class CQuorumBlockProcessor;
      48             : class CQuorumSnapshotManager;
      49             : 
      50             : /**
      51             :  * The quorum manager maintains quorums which were mined on chain. When a quorum is requested from the manager,
      52             :  * it will lookup the commitment (through CQuorumBlockProcessor) and build a CQuorum object from it.
      53             :  */
      54             : class CQuorumManager final
      55             : {
      56             : private:
      57             :     CBLSWorker& blsWorker;
      58             :     CDeterministicMNManager& m_dmnman;
      59             :     CQuorumBlockProcessor& quorumBlockProcessor;
      60             :     CQuorumSnapshotManager& m_qsnapman;
      61             :     const ChainstateManager& m_chainman;
      62             :     llmq::CDKGSessionManager* m_qdkgsman{nullptr};
      63             :     llmq::QuorumRole* m_handler{nullptr};
      64             : 
      65             : private:
      66             :     mutable Mutex cs_db;
      67             :     std::unique_ptr<CDBWrapper> db GUARDED_BY(cs_db){nullptr};
      68             : 
      69             :     mutable Mutex cs_data_requests;
      70             :     mutable std::unordered_map<CQuorumDataRequestKey, CQuorumDataRequest, StaticSaltedHasher> mapQuorumDataRequests
      71             :         GUARDED_BY(cs_data_requests);
      72             : 
      73             :     mutable Mutex m_cs_maps;
      74             :     mutable std::map<Consensus::LLMQType, Uint256LruHashMap<CQuorumPtr>> mapQuorumsCache
      75             :         GUARDED_BY(m_cs_maps);
      76             :     mutable std::map<Consensus::LLMQType, Uint256LruHashMap<std::vector<CQuorumCPtr>>> scanQuorumsCache
      77             :         GUARDED_BY(m_cs_maps);
      78             : 
      79             :     // On mainnet, we have around 62 quorums active at any point; let's cache a little more than double that to be safe.
      80             :     // it maps `quorum_hash` to `pindex`
      81             :     mutable Mutex cs_quorumBaseBlockIndexCache;
      82             :     mutable Uint256LruHashMap<const CBlockIndex*, /*max_size=*/128> quorumBaseBlockIndexCache
      83             :         GUARDED_BY(cs_quorumBaseBlockIndexCache);
      84             : 
      85             :     mutable Mutex m_cache_cs;
      86             :     mutable std::deque<CQuorumCPtr> m_cache_queue GUARDED_BY(m_cache_cs);
      87             :     mutable CThreadInterrupt m_cache_interrupt;
      88             :     mutable std::thread m_cache_thread;
      89             : 
      90             : public:
      91             :     CQuorumManager() = delete;
      92             :     CQuorumManager(const CQuorumManager&) = delete;
      93             :     CQuorumManager& operator=(const CQuorumManager&) = delete;
      94             :     explicit CQuorumManager(CBLSWorker& _blsWorker, CDeterministicMNManager& dmnman, CEvoDB& _evoDb,
      95             :                             CQuorumBlockProcessor& _quorumBlockProcessor, CQuorumSnapshotManager& qsnapman,
      96             :                             const ChainstateManager& chainman, const util::DbWrapperParams& db_params);
      97             :     ~CQuorumManager();
      98             : 
      99         666 :     void ConnectManagers(gsl::not_null<llmq::QuorumRole*> handler, gsl::not_null<llmq::CDKGSessionManager*> qdkgsman)
     100             :     {
     101             :         // Prohibit double initialization
     102         666 :         assert(m_handler == nullptr);
     103         666 :         m_handler = handler;
     104         666 :         assert(m_qdkgsman == nullptr);
     105         666 :         m_qdkgsman = qdkgsman;
     106         666 :     }
     107         666 :     void DisconnectManagers()
     108             :     {
     109         666 :         m_handler = nullptr;
     110         666 :         m_qdkgsman = nullptr;
     111         666 :     }
     112             : 
     113             :     bool GetEncryptedContributions(Consensus::LLMQType llmq_type, const CBlockIndex* block_index,
     114             :                                    const std::vector<bool>& valid_members, const uint256& protx_hash,
     115             :                                    std::vector<CBLSIESEncryptedObject<CBLSSecretKey>>& vec_enc) const;
     116             : 
     117             :     static bool HasQuorum(Consensus::LLMQType llmqType, const CQuorumBlockProcessor& quorum_block_processor, const uint256& quorumHash);
     118             : 
     119             :     // all these methods will lock cs_main for a short period of time
     120             :     CQuorumCPtr GetQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash) const
     121             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_db, !m_cs_maps, !m_cache_cs);
     122             :     std::vector<CQuorumCPtr> ScanQuorums(Consensus::LLMQType llmqType, size_t nCountRequested) const
     123             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_db, !m_cs_maps, !m_cache_cs);
     124             : 
     125             :     // this one is cs_main-free
     126             :     std::vector<CQuorumCPtr> ScanQuorums(Consensus::LLMQType llmqType, gsl::not_null<const CBlockIndex*> pindexStart,
     127             :                                          size_t nCountRequested) const
     128             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_db, !m_cs_maps, !m_cache_cs);
     129             : 
     130             :     bool IsMasternode() const;
     131             :     bool IsWatching() const;
     132             : 
     133             :     //! Request tracking for QGETDATA/QDATA — used by NetQuorum and RPC
     134             :     bool RegisterDataRequest(const CQuorumDataRequestKey& key, const CQuorumDataRequest& request,
     135             :                              bool add_expiry_bias = true) const
     136             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_data_requests);
     137             :     enum class DataResponseValidation : uint8_t { OK, NotRequested, AlreadyReceived, Mismatch };
     138             :     DataResponseValidation ValidateDataResponse(const CQuorumDataRequestKey& key,
     139             :                                                 const CQuorumDataRequest& response) const
     140             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_data_requests);
     141             :     bool IsDataRequestPending(const uint256& proRegTx, bool we_requested, const uint256& quorumHash,
     142             :                               Consensus::LLMQType llmqType) const EXCLUSIVE_LOCKS_REQUIRED(!cs_data_requests);
     143             :     DataRequestStatus GetDataRequestStatus(const uint256& proRegTx, bool we_requested, const uint256& quorumHash,
     144             :                                            Consensus::LLMQType llmqType) const
     145             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_data_requests);
     146             :     void CleanupExpiredDataRequests() const EXCLUSIVE_LOCKS_REQUIRED(!cs_data_requests);
     147             : 
     148             :     void CleanupOldQuorumData(const Uint256HashSet& dbKeysToSkip) const EXCLUSIVE_LOCKS_REQUIRED(!cs_db);
     149             : 
     150             :     //! Used by NetQuorum for QDATA processing
     151             :     CQuorumPtr GetCachedMutableQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash) const
     152             :         EXCLUSIVE_LOCKS_REQUIRED(!m_cs_maps);
     153             :     void WriteContributions(const CQuorumPtr& quorum) const EXCLUSIVE_LOCKS_REQUIRED(!cs_db);
     154             :     void QueueQuorumForWarming(CQuorumCPtr pQuorum) const EXCLUSIVE_LOCKS_REQUIRED(!m_cache_cs);
     155             : 
     156             : private:
     157             :     // all private methods here are cs_main-free
     158             :     bool BuildQuorumContributions(const CFinalCommitmentPtr& fqc, const std::shared_ptr<CQuorum>& quorum) const;
     159             : 
     160             :     CQuorumPtr BuildQuorumFromCommitment(Consensus::LLMQType llmqType,
     161             :                                          gsl::not_null<const CBlockIndex*> pQuorumBaseBlockIndex,
     162             :                                          bool populate_cache) const
     163             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_db, !m_cs_maps, !m_cache_cs);
     164             : 
     165             :     CQuorumCPtr GetQuorum(Consensus::LLMQType llmqType, gsl::not_null<const CBlockIndex*> pindex,
     166             :                           bool populate_cache = true) const
     167             :         EXCLUSIVE_LOCKS_REQUIRED(!cs_db, !m_cs_maps, !m_cache_cs);
     168             : 
     169             :     void CacheWarmingThreadMain() const EXCLUSIVE_LOCKS_REQUIRED(!m_cache_cs);
     170             :     void MigrateOldQuorumDB(CEvoDB& evoDb) const EXCLUSIVE_LOCKS_REQUIRED(!cs_db);
     171             : };
     172             : 
     173             : // when selecting a quorum for signing and verification, we use CQuorumManager::SelectQuorum with this offset as
     174             : // starting height for scanning. This is because otherwise the resulting signatures would not be verifiable by nodes
     175             : // which are not 100% at the chain tip.
     176             : static constexpr int SIGN_HEIGHT_OFFSET{8};
     177             : 
     178             : CQuorumCPtr SelectQuorumForSigning(const Consensus::LLMQParams& llmq_params, const CChain& active_chain, const CQuorumManager& qman,
     179             :                                    const uint256& selectionHash, int signHeight = -1 /*chain tip*/, int signOffset = SIGN_HEIGHT_OFFSET);
     180             : 
     181             : // Verifies a recovered sig that was signed while the chain tip was at signedAtTip
     182             : VerifyRecSigStatus VerifyRecoveredSig(Consensus::LLMQType llmqType, const CChain& active_chain, const CQuorumManager& qman,
     183             :                                       int signedAtHeight, const uint256& id, const uint256& msgHash, const CBLSSignature& sig,
     184             :                                       int signOffset = SIGN_HEIGHT_OFFSET);
     185             : 
     186             : /**
     187             :  * Base class providing the interface used by CQuorumManager and NetQuorum.
     188             :  * Both ObserverContext and ActiveContext inherit from this class.
     189             :  */
     190             : class QuorumRole
     191             : {
     192             : protected:
     193             :     CQuorumManager& m_qman;
     194             : 
     195             : public:
     196             :     QuorumRole() = delete;
     197             :     QuorumRole(const QuorumRole&) = delete;
     198             :     QuorumRole& operator=(const QuorumRole&) = delete;
     199         666 :     explicit QuorumRole(CQuorumManager& qman) : m_qman{qman} {}
     200         666 :     virtual ~QuorumRole() = default;
     201             : 
     202             :     virtual bool IsMasternode() const = 0;
     203             :     virtual bool IsWatching() const = 0;
     204           0 :     virtual uint256 GetProTxHash() const { return {}; }
     205             :     virtual bool SetQuorumSecretKeyShare(CQuorum& quorum, Span<CBLSSecretKey> skContributions) const = 0;
     206             : };
     207             : 
     208             : } // namespace llmq
     209             : 
     210             : #endif // BITCOIN_LLMQ_QUORUMSMAN_H

Generated by: LCOV version 1.16