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_QUORUMS_H
6 : #define BITCOIN_LLMQ_QUORUMS_H
7 :
8 : #include <bls/bls_worker.h>
9 : #include <evo/types.h>
10 : #include <llmq/params.h>
11 : #include <llmq/types.h>
12 : #include <saltedhasher.h>
13 :
14 : #include <serialize.h>
15 : #include <span.h>
16 : #include <sync.h>
17 : #include <threadsafety.h>
18 : #include <uint256.h>
19 : #include <util/time.h>
20 :
21 : #include <set>
22 : #include <string>
23 : #include <vector>
24 :
25 : class CBlockIndex;
26 : class CDBWrapper;
27 : namespace llmq {
28 : class CQuorum;
29 : class CQuorumDataRequest;
30 : class CQuorumManager;
31 : class QuorumRole;
32 : } // namespace llmq
33 :
34 : namespace llmq {
35 :
36 : enum class DataRequestStatus : uint8_t {
37 : NotFound,
38 : Requested,
39 : Pending,
40 : Processed,
41 : };
42 :
43 : extern const std::string DB_QUORUM_SK_SHARE;
44 : extern const std::string DB_QUORUM_QUORUM_VVEC;
45 :
46 : uint256 MakeQuorumKey(const CQuorum& q);
47 : void DataCleanupHelper(CDBWrapper& db, const Uint256HashSet& skip_list, bool compact = false);
48 :
49 : /**
50 : * Object used as a key to store CQuorumDataRequest
51 : */
52 : struct CQuorumDataRequestKey
53 : {
54 : uint256 proRegTx;
55 : bool m_we_requested;
56 : uint256 quorumHash;
57 : Consensus::LLMQType llmqType;
58 :
59 1795 : CQuorumDataRequestKey(const uint256& proRegTxIn, const bool _m_we_requested, const uint256& quorumHashIn, const Consensus::LLMQType llmqTypeIn) :
60 898 : proRegTx(proRegTxIn),
61 898 : m_we_requested(_m_we_requested),
62 898 : quorumHash(quorumHashIn),
63 898 : llmqType(llmqTypeIn)
64 1795 : {}
65 :
66 306 : bool operator ==(const CQuorumDataRequestKey& obj) const
67 : {
68 306 : return (proRegTx == obj.proRegTx && m_we_requested == obj.m_we_requested && quorumHash == obj.quorumHash && llmqType == obj.llmqType);
69 : }
70 : };
71 :
72 : /**
73 : * An object of this class represents a QGETDATA request or a QDATA response header
74 : */
75 : class CQuorumDataRequest
76 : {
77 : public:
78 :
79 : enum Flags : uint16_t {
80 : QUORUM_VERIFICATION_VECTOR = 0x0001,
81 : ENCRYPTED_CONTRIBUTIONS = 0x0002,
82 : };
83 : enum Errors : uint8_t {
84 : NONE = 0x00,
85 : QUORUM_TYPE_INVALID = 0x01,
86 : QUORUM_BLOCK_NOT_FOUND = 0x02,
87 : QUORUM_NOT_FOUND = 0x03,
88 : MASTERNODE_IS_NO_MEMBER = 0x04,
89 : QUORUM_VERIFICATION_VECTOR_MISSING = 0x05,
90 : ENCRYPTED_CONTRIBUTIONS_MISSING = 0x06,
91 : UNDEFINED = 0xFF,
92 : };
93 :
94 : private:
95 502 : Consensus::LLMQType llmqType{Consensus::LLMQType::LLMQ_NONE};
96 : uint256 quorumHash;
97 502 : uint16_t nDataMask{0};
98 : uint256 proTxHash;
99 675 : Errors nError{UNDEFINED};
100 :
101 173 : int64_t nTime{GetTime()};
102 675 : bool fProcessed{false};
103 :
104 : static constexpr int64_t EXPIRATION_TIMEOUT{300};
105 : static constexpr int64_t EXPIRATION_BIAS{60};
106 :
107 : public:
108 :
109 3012 : CQuorumDataRequest() : nTime(GetTime()) {}
110 346 : CQuorumDataRequest(const Consensus::LLMQType llmqTypeIn, const uint256& quorumHashIn, const uint16_t nDataMaskIn, const uint256& proTxHashIn = uint256()) :
111 173 : llmqType(llmqTypeIn),
112 173 : quorumHash(quorumHashIn),
113 173 : nDataMask(nDataMaskIn),
114 173 : proTxHash(proTxHashIn)
115 346 : {}
116 :
117 2994 : SERIALIZE_METHODS(CQuorumDataRequest, obj)
118 : {
119 998 : bool fRead{false};
120 1500 : SER_READ(obj, fRead = true);
121 998 : READWRITE(obj.llmqType, obj.quorumHash, obj.nDataMask, obj.proTxHash);
122 998 : if (fRead) {
123 : try {
124 502 : READWRITE(obj.nError);
125 502 : } catch (...) {
126 646 : SER_READ(obj, obj.nError = UNDEFINED);
127 323 : }
128 998 : } else if (obj.nError != UNDEFINED) {
129 323 : READWRITE(obj.nError);
130 323 : }
131 1321 : }
132 :
133 1347 : Consensus::LLMQType GetLLMQType() const { return llmqType; }
134 1260 : const uint256& GetQuorumHash() const { return quorumHash; }
135 853 : uint16_t GetDataMask() const { return nDataMask; }
136 205 : const uint256& GetProTxHash() const { return proTxHash; }
137 :
138 343 : void SetError(Errors nErrorIn) { nError = nErrorIn; }
139 456 : Errors GetError() const { return nError; }
140 : std::string GetErrorString() const;
141 :
142 575 : bool IsExpired(bool add_bias) const { return (GetTime() - nTime) >= (EXPIRATION_TIMEOUT + (add_bias ? EXPIRATION_BIAS : 0)); }
143 173 : bool IsProcessed() const { return fProcessed; }
144 149 : void SetProcessed() { fProcessed = true; }
145 :
146 167 : bool operator==(const CQuorumDataRequest& other) const
147 : {
148 334 : return llmqType == other.llmqType &&
149 167 : quorumHash == other.quorumHash &&
150 167 : nDataMask == other.nDataMask &&
151 149 : proTxHash == other.proTxHash;
152 : }
153 167 : bool operator!=(const CQuorumDataRequest& other) const
154 : {
155 167 : return !(*this == other);
156 : }
157 : };
158 :
159 : /**
160 : * An object of this class represents a quorum which was mined on-chain (through a quorum commitment)
161 : * It at least contains information about the members and the quorum public key which is needed to verify recovered
162 : * signatures from this quorum.
163 : *
164 : * In case the local node is a member of the same quorum and successfully participated in the DKG, the quorum object
165 : * will also contain the secret key share and the quorum verification vector. The quorum vvec is then used to recover
166 : * the public key shares of individual members, which are needed to verify signature shares of these members.
167 : */
168 : class CQuorum
169 : {
170 : friend class CQuorumManager;
171 :
172 : public:
173 : const Consensus::LLMQParams params;
174 : const CFinalCommitmentPtr qc;
175 : const CBlockIndex* m_quorum_base_block_index;
176 : const uint256 minedBlockHash;
177 : const std::vector<CDeterministicMNCPtr> members;
178 :
179 : private:
180 : // Recovery of public key shares is very slow, so we start a background thread that pre-populates a cache so that
181 : // the public key shares are ready when needed later
182 : mutable CBLSWorkerCache blsCache;
183 : mutable std::atomic<bool> fQuorumDataRecoveryThreadRunning{false};
184 :
185 : mutable Mutex cs_vvec_shShare;
186 : // These are only valid when we either participated in the DKG or fully watched it
187 : BLSVerificationVectorPtr quorumVvec GUARDED_BY(cs_vvec_shShare);
188 : CBLSSecretKey skShare GUARDED_BY(cs_vvec_shShare);
189 :
190 : public:
191 : CQuorum(const Consensus::LLMQParams& _params, CBLSWorker& _blsWorker,
192 : CFinalCommitmentPtr _qc, const CBlockIndex* _pQuorumBaseBlockIndex, const uint256& _minedBlockHash, Span<CDeterministicMNCPtr> _members);
193 :
194 10710 : ~CQuorum() = default;
195 :
196 : bool SetVerificationVector(const std::vector<CBLSPublicKey>& quorumVecIn) EXCLUSIVE_LOCKS_REQUIRED(!cs_vvec_shShare);
197 1896 : void SetVerificationVector(BLSVerificationVectorPtr vvec_in) EXCLUSIVE_LOCKS_REQUIRED(!cs_vvec_shShare)
198 : {
199 1896 : LOCK(cs_vvec_shShare);
200 1896 : quorumVvec = std::move(vvec_in);
201 1896 : }
202 : bool SetSecretKeyShare(const CBLSSecretKey& secretKeyShare, const uint256& protx_hash)
203 : EXCLUSIVE_LOCKS_REQUIRED(!cs_vvec_shShare);
204 :
205 : bool HasVerificationVector() const EXCLUSIVE_LOCKS_REQUIRED(!cs_vvec_shShare);
206 307 : std::shared_ptr<const std::vector<CBLSPublicKey>> GetVerificationVector() const EXCLUSIVE_LOCKS_REQUIRED(!cs_vvec_shShare)
207 : {
208 307 : LOCK(cs_vvec_shShare);
209 307 : return quorumVvec;
210 307 : }
211 : bool IsMember(const uint256& proTxHash) const;
212 : bool IsValidMember(const uint256& proTxHash) const;
213 : int GetMemberIndex(const uint256& proTxHash) const;
214 :
215 : CBLSPublicKey GetPubKeyShare(size_t memberIdx) const EXCLUSIVE_LOCKS_REQUIRED(!cs_vvec_shShare);
216 : CBLSSecretKey GetSkShare() const EXCLUSIVE_LOCKS_REQUIRED(!cs_vvec_shShare);
217 :
218 : //! Try to claim exclusive data recovery for this quorum. Returns true if claimed.
219 2665 : bool TryClaimRecovery() const { bool expected = false; return fQuorumDataRecoveryThreadRunning.compare_exchange_strong(expected, true); }
220 83234 : bool IsRecoveryRunning() const { return fQuorumDataRecoveryThreadRunning; }
221 2627 : void ReleaseRecovery() const { fQuorumDataRecoveryThreadRunning = false; }
222 :
223 : private:
224 : bool HasVerificationVectorInternal() const EXCLUSIVE_LOCKS_REQUIRED(cs_vvec_shShare);
225 : void WriteContributions(CDBWrapper& db) const EXCLUSIVE_LOCKS_REQUIRED(!cs_vvec_shShare);
226 : bool ReadContributions(const CDBWrapper& db) EXCLUSIVE_LOCKS_REQUIRED(!cs_vvec_shShare);
227 : };
228 : } // namespace llmq
229 :
230 : template<typename T> struct SaltedHasherImpl;
231 : template<>
232 : struct SaltedHasherImpl<llmq::CQuorumDataRequestKey>
233 : {
234 898 : static std::size_t CalcHash(const llmq::CQuorumDataRequestKey& v, uint64_t k0, uint64_t k1)
235 : {
236 898 : CSipHasher c(k0, k1);
237 898 : c.Write(reinterpret_cast<const unsigned char*>(&v.proRegTx), sizeof(v.proRegTx));
238 898 : c.Write(reinterpret_cast<const unsigned char*>(&v.m_we_requested), sizeof(v.m_we_requested));
239 898 : c.Write(reinterpret_cast<const unsigned char*>(&v.quorumHash), sizeof(v.quorumHash));
240 898 : c.Write(reinterpret_cast<const unsigned char*>(&v.llmqType), sizeof(v.llmqType));
241 898 : return c.Finalize();
242 : }
243 : };
244 :
245 : template<> struct is_serializable_enum<llmq::CQuorumDataRequest::Errors> : std::true_type {};
246 :
247 : #endif // BITCOIN_LLMQ_QUORUMS_H
|