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 0 : uint256 MakeQuorumKey(const CQuorum& q)
21 : {
22 0 : CHashWriter hw(SER_NETWORK, 0);
23 0 : hw << q.params.type;
24 0 : hw << q.qc->quorumHash;
25 0 : for (const auto& dmn : q.members) {
26 0 : hw << dmn->proTxHash;
27 : }
28 0 : return hw.GetHash();
29 : }
30 :
31 180 : void DataCleanupHelper(CDBWrapper& db, const Uint256HashSet& skip_list, bool compact)
32 : {
33 180 : const auto prefixes = {DB_QUORUM_QUORUM_VVEC, DB_QUORUM_SK_SHARE};
34 :
35 180 : CDBBatch batch(db);
36 180 : std::unique_ptr<CDBIterator> pcursor(db.NewIterator());
37 :
38 540 : for (const auto& prefix : prefixes) {
39 360 : auto start = std::make_tuple(prefix, uint256());
40 360 : pcursor->Seek(start);
41 :
42 360 : int count{0};
43 360 : while (pcursor->Valid()) {
44 0 : decltype(start) k;
45 :
46 0 : if (!pcursor->GetKey(k) || std::get<0>(k) != prefix) {
47 0 : break;
48 : }
49 :
50 0 : pcursor->Next();
51 :
52 0 : 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 0 : }
62 :
63 360 : db.WriteBatch(batch);
64 :
65 360 : LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- %s removed %d\n", __func__, prefix, count);
66 360 : }
67 :
68 180 : pcursor.reset();
69 :
70 180 : 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 180 : }
77 :
78 0 : std::string CQuorumDataRequest::GetErrorString() const
79 : {
80 0 : 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 0 : return "QUORUM_VERIFICATION_VECTOR_MISSING";
93 : case (Errors::ENCRYPTED_CONTRIBUTIONS_MISSING):
94 0 : return "ENCRYPTED_CONTRIBUTIONS_MISSING";
95 : case (Errors::UNDEFINED):
96 0 : return "UNDEFINED";
97 : default:
98 0 : return "UNDEFINED";
99 : }
100 : return "UNDEFINED";
101 0 : }
102 :
103 0 : CQuorum::CQuorum(const Consensus::LLMQParams& _params, CBLSWorker& _blsWorker,
104 : CFinalCommitmentPtr _qc, const CBlockIndex* _pQuorumBaseBlockIndex,
105 : const uint256& _minedBlockHash, Span<CDeterministicMNCPtr> _members) :
106 0 : params{_params},
107 0 : qc{std::move(_qc)},
108 0 : m_quorum_base_block_index{_pQuorumBaseBlockIndex},
109 0 : minedBlockHash{_minedBlockHash},
110 0 : members{_members.begin(), _members.end()},
111 0 : blsCache{_blsWorker}
112 0 : {
113 : assert(qc != nullptr);
114 : assert(m_quorum_base_block_index != nullptr);
115 0 : }
116 :
117 0 : bool CQuorum::SetVerificationVector(const std::vector<CBLSPublicKey>& quorumVecIn)
118 : {
119 0 : const auto quorumVecInSerialized = ::SerializeHash(quorumVecIn);
120 :
121 0 : LOCK(cs_vvec_shShare);
122 0 : if (quorumVecInSerialized != qc->quorumVvecHash) {
123 0 : return false;
124 : }
125 0 : quorumVvec = std::make_shared<std::vector<CBLSPublicKey>>(quorumVecIn);
126 0 : return true;
127 0 : }
128 :
129 0 : bool CQuorum::SetSecretKeyShare(const CBLSSecretKey& secretKeyShare, const uint256& protx_hash)
130 : {
131 0 : if (protx_hash.IsNull() || !secretKeyShare.IsValid() || (secretKeyShare.GetPublicKey() != GetPubKeyShare(GetMemberIndex(protx_hash)))) {
132 0 : return false;
133 : }
134 0 : LOCK(cs_vvec_shShare);
135 0 : skShare = secretKeyShare;
136 0 : return true;
137 0 : }
138 :
139 0 : bool CQuorum::IsMember(const uint256& proTxHash) const
140 : {
141 0 : return std::ranges::any_of(members, [&proTxHash](const auto& dmn) { return dmn->proTxHash == proTxHash; });
142 : }
143 :
144 0 : bool CQuorum::IsValidMember(const uint256& proTxHash) const
145 : {
146 0 : for (const auto i : util::irange(members.size())) {
147 : // cppcheck-suppress useStlAlgorithm
148 0 : if (members[i]->proTxHash == proTxHash) {
149 0 : return qc->validMembers[i];
150 : }
151 : }
152 0 : return false;
153 0 : }
154 :
155 0 : CBLSPublicKey CQuorum::GetPubKeyShare(size_t memberIdx) const
156 : {
157 0 : LOCK(cs_vvec_shShare);
158 0 : if (!HasVerificationVectorInternal() || memberIdx >= members.size() || !qc->validMembers[memberIdx]) {
159 0 : return CBLSPublicKey();
160 : }
161 0 : const auto& m = members[memberIdx];
162 0 : return blsCache.BuildPubKeyShare(m->proTxHash, quorumVvec, CBLSId(m->proTxHash));
163 0 : }
164 :
165 0 : bool CQuorum::HasVerificationVector() const {
166 0 : LOCK(cs_vvec_shShare);
167 0 : return HasVerificationVectorInternal();
168 0 : }
169 :
170 0 : bool CQuorum::HasVerificationVectorInternal() const {
171 0 : AssertLockHeld(cs_vvec_shShare);
172 0 : return quorumVvec != nullptr;
173 : }
174 :
175 0 : CBLSSecretKey CQuorum::GetSkShare() const
176 : {
177 0 : LOCK(cs_vvec_shShare);
178 0 : return skShare;
179 0 : }
180 :
181 0 : int CQuorum::GetMemberIndex(const uint256& proTxHash) const
182 : {
183 0 : for (const auto i : util::irange(members.size())) {
184 : // cppcheck-suppress useStlAlgorithm
185 0 : if (members[i]->proTxHash == proTxHash) {
186 0 : return int(i);
187 : }
188 : }
189 0 : return -1;
190 0 : }
191 :
192 0 : void CQuorum::WriteContributions(CDBWrapper& db) const
193 : {
194 0 : uint256 dbKey = MakeQuorumKey(*this);
195 :
196 0 : LOCK(cs_vvec_shShare);
197 0 : if (HasVerificationVectorInternal()) {
198 0 : CDataStream s(SER_DISK, CLIENT_VERSION);
199 0 : WriteCompactSize(s, quorumVvec->size());
200 0 : for (auto& pubkey : *quorumVvec) {
201 0 : s << CBLSPublicKeyVersionWrapper(pubkey, false);
202 : }
203 0 : db.Write(std::make_pair(DB_QUORUM_QUORUM_VVEC, dbKey), s);
204 0 : }
205 0 : if (skShare.IsValid()) {
206 0 : db.Write(std::make_pair(DB_QUORUM_SK_SHARE, dbKey), skShare);
207 0 : }
208 0 : }
209 :
210 0 : bool CQuorum::ReadContributions(const CDBWrapper& db)
211 : {
212 0 : uint256 dbKey = MakeQuorumKey(*this);
213 0 : CDataStream s(SER_DISK, CLIENT_VERSION);
214 :
215 0 : if (!db.ReadDataStream(std::make_pair(DB_QUORUM_QUORUM_VVEC, dbKey), s)) {
216 0 : return false;
217 : }
218 :
219 0 : size_t vvec_size = ReadCompactSize(s);
220 0 : CBLSPublicKey pubkey;
221 0 : std::vector<CBLSPublicKey> qv;
222 0 : for ([[maybe_unused]] size_t _ : util::irange(vvec_size)) {
223 0 : s >> CBLSPublicKeyVersionWrapper(pubkey, false);
224 0 : qv.emplace_back(pubkey);
225 : }
226 :
227 0 : LOCK(cs_vvec_shShare);
228 0 : 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 0 : db.Read(std::make_pair(DB_QUORUM_SK_SHARE, dbKey), skShare);
232 :
233 0 : return true;
234 0 : }
235 : } // namespace llmq
|