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 : #include <llmq/quorums.h>
6 :
7 : #include <evo/deterministicmns.h>
8 : #include <llmq/commitment.h>
9 : #include <util/helpers.h>
10 :
11 : #include <dbwrapper.h>
12 :
13 : #include <memory>
14 : #include <ranges>
15 :
16 : namespace llmq {
17 : const std::string DB_QUORUM_SK_SHARE = "q_Qsk";
18 : const std::string DB_QUORUM_QUORUM_VVEC = "q_Qqvvec";
19 :
20 7370 : uint256 MakeQuorumKey(const CQuorum& q)
21 : {
22 7370 : CHashWriter hw(SER_NETWORK, 0);
23 7370 : hw << q.params.type;
24 7370 : hw << q.qc->quorumHash;
25 34793 : for (const auto& dmn : q.members) {
26 27423 : hw << dmn->proTxHash;
27 : }
28 7370 : return hw.GetHash();
29 : }
30 :
31 6016 : void DataCleanupHelper(CDBWrapper& db, const Uint256HashSet& skip_list, bool compact)
32 : {
33 6016 : const auto prefixes = {DB_QUORUM_QUORUM_VVEC, DB_QUORUM_SK_SHARE};
34 :
35 6016 : CDBBatch batch(db);
36 6016 : std::unique_ptr<CDBIterator> pcursor(db.NewIterator());
37 :
38 11860 : for (const auto& prefix : prefixes) {
39 8938 : auto start = std::make_tuple(prefix, uint256());
40 8938 : pcursor->Seek(start);
41 :
42 5844 : int count{0};
43 7543 : while (pcursor->Valid()) {
44 1699 : decltype(start) k;
45 :
46 1699 : if (!pcursor->GetKey(k) || std::get<0>(k) != prefix) {
47 1547 : break;
48 : }
49 :
50 152 : pcursor->Next();
51 :
52 152 : if (skip_list.find(std::get<1>(k)) != skip_list.end()) continue;
53 :
54 0 : ++count;
55 0 : batch.Erase(k);
56 :
57 0 : if (batch.SizeEstimate() >= (1 << 24)) {
58 0 : db.WriteBatch(batch);
59 0 : batch.Clear();
60 0 : }
61 1699 : }
62 :
63 4297 : db.WriteBatch(batch);
64 :
65 5844 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- %s removed %d\n", __func__, prefix, count);
66 5844 : }
67 :
68 2922 : pcursor.reset();
69 :
70 2922 : if (compact) {
71 : // Avoid using this on regular cleanups, use on db migrations only
72 0 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- compact start\n", __func__);
73 0 : db.CompactFull();
74 0 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- compact end\n", __func__);
75 0 : }
76 21486 : }
77 :
78 19 : std::string CQuorumDataRequest::GetErrorString() const
79 : {
80 19 : switch (nError) {
81 : case (Errors::NONE):
82 0 : return "NONE";
83 : case (Errors::QUORUM_TYPE_INVALID):
84 0 : return "QUORUM_TYPE_INVALID";
85 : case (Errors::QUORUM_BLOCK_NOT_FOUND):
86 0 : return "QUORUM_BLOCK_NOT_FOUND";
87 : case (Errors::QUORUM_NOT_FOUND):
88 0 : return "QUORUM_NOT_FOUND";
89 : case (Errors::MASTERNODE_IS_NO_MEMBER):
90 0 : return "MASTERNODE_IS_NO_MEMBER";
91 : case (Errors::QUORUM_VERIFICATION_VECTOR_MISSING):
92 11 : return "QUORUM_VERIFICATION_VECTOR_MISSING";
93 : case (Errors::ENCRYPTED_CONTRIBUTIONS_MISSING):
94 8 : return "ENCRYPTED_CONTRIBUTIONS_MISSING";
95 : case (Errors::UNDEFINED):
96 0 : return "UNDEFINED";
97 : default:
98 0 : return "UNDEFINED";
99 : }
100 : return "UNDEFINED";
101 19 : }
102 :
103 10710 : CQuorum::CQuorum(const Consensus::LLMQParams& _params, CBLSWorker& _blsWorker,
104 : CFinalCommitmentPtr _qc, const CBlockIndex* _pQuorumBaseBlockIndex,
105 : const uint256& _minedBlockHash, Span<CDeterministicMNCPtr> _members) :
106 5355 : params{_params},
107 5355 : qc{std::move(_qc)},
108 5355 : m_quorum_base_block_index{_pQuorumBaseBlockIndex},
109 5355 : minedBlockHash{_minedBlockHash},
110 5355 : members{_members.begin(), _members.end()},
111 5355 : blsCache{_blsWorker}
112 5355 : {
113 : assert(qc != nullptr);
114 : assert(m_quorum_base_block_index != nullptr);
115 5355 : }
116 :
117 130 : bool CQuorum::SetVerificationVector(const std::vector<CBLSPublicKey>& quorumVecIn)
118 : {
119 130 : const auto quorumVecInSerialized = ::SerializeHash(quorumVecIn);
120 :
121 130 : LOCK(cs_vvec_shShare);
122 130 : if (quorumVecInSerialized != qc->quorumVvecHash) {
123 0 : return false;
124 : }
125 130 : quorumVvec = std::make_shared<std::vector<CBLSPublicKey>>(quorumVecIn);
126 130 : return true;
127 130 : }
128 :
129 1933 : bool CQuorum::SetSecretKeyShare(const CBLSSecretKey& secretKeyShare, const uint256& protx_hash)
130 : {
131 1933 : if (protx_hash.IsNull() || !secretKeyShare.IsValid() || (secretKeyShare.GetPublicKey() != GetPubKeyShare(GetMemberIndex(protx_hash)))) {
132 20 : return false;
133 : }
134 1913 : LOCK(cs_vvec_shShare);
135 1913 : skShare = secretKeyShare;
136 1913 : return true;
137 1933 : }
138 :
139 87454 : bool CQuorum::IsMember(const uint256& proTxHash) const
140 : {
141 357720 : return std::ranges::any_of(members, [&proTxHash](const auto& dmn) { return dmn->proTxHash == proTxHash; });
142 : }
143 :
144 392845 : bool CQuorum::IsValidMember(const uint256& proTxHash) const
145 : {
146 1270264 : for (const auto i : util::irange(members.size())) {
147 : // cppcheck-suppress useStlAlgorithm
148 1113451 : if (members[i]->proTxHash == proTxHash) {
149 236032 : return qc->validMembers[i];
150 : }
151 : }
152 156813 : return false;
153 392845 : }
154 :
155 38425 : CBLSPublicKey CQuorum::GetPubKeyShare(size_t memberIdx) const
156 : {
157 38425 : LOCK(cs_vvec_shShare);
158 38425 : if (!HasVerificationVectorInternal() || memberIdx >= members.size() || !qc->validMembers[memberIdx]) {
159 1978 : return CBLSPublicKey();
160 : }
161 36447 : const auto& m = members[memberIdx];
162 36447 : return blsCache.BuildPubKeyShare(m->proTxHash, quorumVvec, CBLSId(m->proTxHash));
163 38425 : }
164 :
165 169926 : bool CQuorum::HasVerificationVector() const {
166 169926 : LOCK(cs_vvec_shShare);
167 169926 : return HasVerificationVectorInternal();
168 169926 : }
169 :
170 210376 : bool CQuorum::HasVerificationVectorInternal() const {
171 210376 : AssertLockHeld(cs_vvec_shShare);
172 210376 : return quorumVvec != nullptr;
173 : }
174 :
175 147655 : CBLSSecretKey CQuorum::GetSkShare() const
176 : {
177 147655 : LOCK(cs_vvec_shShare);
178 147655 : return skShare;
179 147655 : }
180 :
181 49295 : int CQuorum::GetMemberIndex(const uint256& proTxHash) const
182 : {
183 120297 : for (const auto i : util::irange(members.size())) {
184 : // cppcheck-suppress useStlAlgorithm
185 120291 : if (members[i]->proTxHash == proTxHash) {
186 49289 : return int(i);
187 : }
188 : }
189 6 : return -1;
190 49295 : }
191 :
192 2026 : void CQuorum::WriteContributions(CDBWrapper& db) const
193 : {
194 2026 : uint256 dbKey = MakeQuorumKey(*this);
195 :
196 2026 : LOCK(cs_vvec_shShare);
197 2026 : if (HasVerificationVectorInternal()) {
198 2026 : CDataStream s(SER_DISK, CLIENT_VERSION);
199 2026 : WriteCompactSize(s, quorumVvec->size());
200 7493 : for (auto& pubkey : *quorumVvec) {
201 5467 : s << CBLSPublicKeyVersionWrapper(pubkey, false);
202 : }
203 2026 : db.Write(std::make_pair(DB_QUORUM_QUORUM_VVEC, dbKey), s);
204 2026 : }
205 2026 : if (skShare.IsValid()) {
206 1913 : db.Write(std::make_pair(DB_QUORUM_SK_SHARE, dbKey), skShare);
207 1913 : }
208 2026 : }
209 :
210 5268 : bool CQuorum::ReadContributions(const CDBWrapper& db)
211 : {
212 5268 : uint256 dbKey = MakeQuorumKey(*this);
213 5268 : CDataStream s(SER_DISK, CLIENT_VERSION);
214 :
215 5268 : if (!db.ReadDataStream(std::make_pair(DB_QUORUM_QUORUM_VVEC, dbKey), s)) {
216 4346 : return false;
217 : }
218 :
219 922 : size_t vvec_size = ReadCompactSize(s);
220 922 : CBLSPublicKey pubkey;
221 922 : std::vector<CBLSPublicKey> qv;
222 3391 : for ([[maybe_unused]] size_t _ : util::irange(vvec_size)) {
223 2469 : s >> CBLSPublicKeyVersionWrapper(pubkey, false);
224 2469 : qv.emplace_back(pubkey);
225 : }
226 :
227 922 : LOCK(cs_vvec_shShare);
228 922 : quorumVvec = std::make_shared<std::vector<CBLSPublicKey>>(std::move(qv));
229 : // We ignore the return value here as it is ok if this fails. If it fails, it usually means that we are not a
230 : // member of the quorum but observed the whole DKG process to have the quorum verification vector.
231 922 : db.Read(std::make_pair(DB_QUORUM_SK_SHARE, dbKey), skShare);
232 :
233 922 : return true;
234 5268 : }
235 : } // namespace llmq
|