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 0 : void ConnectManagers(gsl::not_null<llmq::QuorumRole*> handler, gsl::not_null<llmq::CDKGSessionManager*> qdkgsman)
100 : {
101 : // Prohibit double initialization
102 0 : assert(m_handler == nullptr);
103 0 : m_handler = handler;
104 0 : assert(m_qdkgsman == nullptr);
105 0 : m_qdkgsman = qdkgsman;
106 0 : }
107 0 : void DisconnectManagers()
108 : {
109 0 : m_handler = nullptr;
110 0 : m_qdkgsman = nullptr;
111 0 : }
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 0 : explicit QuorumRole(CQuorumManager& qman) : m_qman{qman} {}
200 0 : 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
|