Line data Source code
1 : // Copyright (c) 2018-2025 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 <active/dkgsession.h>
6 :
7 : #include <active/masternode.h>
8 : #include <evo/deterministicmns.h>
9 : #include <llmq/commitment.h>
10 : #include <llmq/debug.h>
11 : #include <llmq/dkgsessionhandler.h>
12 : #include <llmq/dkgsessionmgr.h>
13 : #include <llmq/options.h>
14 : #include <llmq/utils.h>
15 : #include <masternode/meta.h>
16 : #include <util/helpers.h>
17 :
18 : #include <chain.h>
19 : #include <deploymentstatus.h>
20 : #include <validation.h>
21 :
22 : #include <cxxtimer.hpp>
23 :
24 : namespace llmq {
25 22084 : ActiveDKGSession::ActiveDKGSession(CBLSWorker& bls_worker, CDeterministicMNManager& dmnman, CDKGDebugManager& dkgdbgman,
26 : CDKGSessionManager& qdkgsman, CMasternodeMetaMan& mn_metaman,
27 : CQuorumSnapshotManager& qsnapman, const CActiveMasternodeManager& mn_activeman,
28 : const ChainstateManager& chainman, const CSporkManager& sporkman,
29 : const CBlockIndex* base_block_index, const Consensus::LLMQParams& llmq_params) :
30 11042 : CDKGSession(bls_worker, dmnman, dkgdbgman, qdkgsman, qsnapman, chainman, base_block_index, llmq_params),
31 11042 : m_mn_metaman{mn_metaman},
32 11042 : m_mn_activeman{mn_activeman},
33 11042 : m_sporkman{sporkman},
34 11042 : m_use_legacy_bls{!DeploymentActiveAfter(m_quorum_base_block_index, Params().GetConsensus(), Consensus::DEPLOYMENT_V19)}
35 22084 : {
36 22084 : }
37 :
38 33127 : ActiveDKGSession::~ActiveDKGSession() = default;
39 :
40 4238 : std::optional<CDKGContribution> ActiveDKGSession::Contribute()
41 : {
42 4238 : if (!AreWeMember()) {
43 1634 : return std::nullopt;
44 : }
45 :
46 2604 : assert(params.threshold > 1); // we should not get there with single-node-quorums
47 :
48 2604 : CDKGLogger logger(*this, __func__, __LINE__);
49 :
50 2604 : cxxtimer::Timer t1(true);
51 2604 : logger.Batch("generating contributions");
52 2604 : if (!blsWorker.GenerateContributions(params.threshold, memberIds, vvecContribution, m_sk_contributions)) {
53 : // this should never happen actually
54 0 : logger.Batch("GenerateContributions failed");
55 0 : return std::nullopt;
56 : }
57 2604 : logger.Batch("generated contributions. time=%d", t1.count());
58 2604 : logger.Flush();
59 :
60 2604 : return SendContributions();
61 4238 : }
62 :
63 2606 : std::optional<CDKGContribution> ActiveDKGSession::SendContributions()
64 : {
65 2606 : CDKGLogger logger(*this, __func__, __LINE__);
66 :
67 2606 : assert(AreWeMember());
68 :
69 2604 : logger.Batch("sending contributions");
70 :
71 2604 : if (ShouldSimulateError(DKGError::type::CONTRIBUTION_OMIT)) {
72 2 : logger.Batch("omitting");
73 2 : return std::nullopt;
74 : }
75 :
76 2602 : CDKGContribution qc;
77 2604 : qc.llmqType = params.type;
78 2604 : qc.quorumHash = m_quorum_base_block_index->GetBlockHash();
79 2602 : qc.proTxHash = myProTxHash;
80 2602 : qc.vvec = vvecContribution;
81 :
82 2602 : cxxtimer::Timer t1(true);
83 2602 : qc.contributions = std::make_shared<CBLSIESMultiRecipientObjects<CBLSSecretKey>>();
84 2601 : qc.contributions->InitEncrypt(members.size());
85 :
86 12382 : for (const auto i : util::irange(members.size())) {
87 9780 : const auto& m = members[i];
88 9780 : CBLSSecretKey skContrib = m_sk_contributions[i];
89 :
90 9780 : if (i != myIdx && ShouldSimulateError(DKGError::type::CONTRIBUTION_LIE)) {
91 12 : logger.Batch("lying for %s", m->dmn->proTxHash.ToString());
92 12 : skContrib.MakeNewKey();
93 12 : }
94 :
95 9780 : if (!qc.contributions->Encrypt(i, m->dmn->pdmnState->pubKeyOperator.Get(), skContrib, PROTOCOL_VERSION)) {
96 0 : logger.Batch("failed to encrypt contribution for %s", m->dmn->proTxHash.ToString());
97 0 : return std::nullopt;
98 : }
99 9780 : }
100 :
101 2602 : logger.Batch("encrypted contributions. time=%d", t1.count());
102 :
103 2602 : qc.sig = m_mn_activeman.Sign(qc.GetSignHash(), m_use_legacy_bls);
104 :
105 2602 : logger.Flush();
106 :
107 5204 : dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) {
108 2602 : status.statusBits.sentContributions = true;
109 2602 : return true;
110 : });
111 :
112 2602 : return qc;
113 2612 : }
114 :
115 : // Verifies all pending secret key contributions in one batch
116 : // This is done by aggregating the verification vectors belonging to the secret key contributions
117 : // The resulting aggregated vvec is then used to recover a public key share
118 : // The public key share must match the public key belonging to the aggregated secret key contributions
119 : // See CBLSWorker::VerifyContributionShares for more details.
120 2468 : void ActiveDKGSession::VerifyPendingContributions()
121 : {
122 2468 : AssertLockHeld(cs_pending);
123 :
124 2468 : CDKGLogger logger(*this, __func__, __LINE__);
125 :
126 2468 : cxxtimer::Timer t1(true);
127 :
128 2468 : if (pendingContributionVerifications.empty()) {
129 24 : return;
130 : }
131 :
132 2444 : std::vector<size_t> memberIndexes;
133 2444 : std::vector<BLSVerificationVectorPtr> vvecs;
134 2444 : std::vector<CBLSSecretKey> skContributions;
135 :
136 11093 : for (const auto& idx : pendingContributionVerifications) {
137 8655 : const auto& m = members[idx];
138 8655 : if (m->bad || m->weComplain) {
139 14 : continue;
140 : }
141 8655 : memberIndexes.emplace_back(idx);
142 8649 : vvecs.emplace_back(receivedVvecs[idx]);
143 8649 : skContributions.emplace_back(receivedSkContributions[idx]);
144 : // Write here to definitely store one contribution for each member no matter if
145 : // our share is valid or not, could be that others are still correct
146 8649 : dkgManager.WriteEncryptedContributions(params.type, m_quorum_base_block_index, m->dmn->proTxHash, *vecEncryptedContributions[idx]);
147 : }
148 :
149 2438 : auto result = blsWorker.VerifyContributionShares(myId, vvecs, skContributions);
150 2444 : if (result.size() != memberIndexes.size()) {
151 6 : logger.Batch("VerifyContributionShares returned result of size %d but size %d was expected, something is wrong", result.size(), memberIndexes.size());
152 0 : return;
153 : }
154 :
155 11087 : for (const auto i : util::irange(memberIndexes.size())) {
156 8648 : if (!result[i]) {
157 12 : const auto& m = members[memberIndexes[i]];
158 12 : logger.Batch("invalid contribution from %s. will complain later", m->dmn->proTxHash.ToString());
159 12 : m->weComplain = true;
160 24 : dkgDebugManager.UpdateLocalMemberStatus(params.type, quorumIndex, m->idx, [&](CDKGDebugMemberStatus& status) {
161 12 : status.statusBits.weComplain = true;
162 12 : return true;
163 : });
164 12 : } else {
165 8635 : size_t memberIdx = memberIndexes[i];
166 8635 : dkgManager.WriteVerifiedSkContribution(params.type, m_quorum_base_block_index, members[memberIdx]->dmn->proTxHash, skContributions[i]);
167 : }
168 : }
169 :
170 2438 : logger.Batch("verified %d pending contributions. time=%d", pendingContributionVerifications.size(), t1.count());
171 2438 : pendingContributionVerifications.clear();
172 2486 : }
173 :
174 4039 : std::optional<CDKGComplaint> ActiveDKGSession::VerifyAndComplain(CConnman& connman)
175 : {
176 4039 : if (!AreWeMember()) {
177 1577 : return std::nullopt;
178 : }
179 :
180 : {
181 2462 : LOCK(cs_pending);
182 2462 : VerifyPendingContributions();
183 2462 : }
184 :
185 2462 : CDKGLogger logger(*this, __func__, __LINE__);
186 :
187 : // we check all members if they sent us their contributions
188 : // we consider members as bad if they missed to send anything or if they sent multiple
189 : // in both cases we won't give them a second chance as they might be either down, buggy or an adversary
190 : // we assume that such a participant will be marked as bad by the whole network in most cases,
191 : // as propagation will ensure that all nodes see the same vvecs/contributions. In case nodes come to
192 : // different conclusions, the aggregation phase will handle this (most voted quorum key wins).
193 :
194 2462 : cxxtimer::Timer t1(true);
195 :
196 11765 : for (const auto& m : members) {
197 9303 : if (m->bad) {
198 0 : continue;
199 : }
200 9303 : if (m->contributions.empty()) {
201 650 : logger.Batch("%s did not send any contribution", m->dmn->proTxHash.ToString());
202 650 : MarkBadMember(m->idx);
203 650 : continue;
204 : }
205 : }
206 :
207 2462 : logger.Batch("verified contributions. time=%d", t1.count());
208 2462 : logger.Flush();
209 :
210 2462 : VerifyConnectionAndMinProtoVersions(connman);
211 :
212 2462 : return SendComplaint();
213 4039 : }
214 :
215 2462 : void ActiveDKGSession::VerifyConnectionAndMinProtoVersions(CConnman& connman) const
216 : {
217 2462 : assert(m_mn_metaman.IsValid());
218 :
219 2462 : if (!IsQuorumPoseEnabled(params.type, m_sporkman)) {
220 2063 : return;
221 : }
222 :
223 399 : CDKGLogger logger(*this, __func__, __LINE__);
224 :
225 399 : Uint256HashMap<int> protoMap;
226 2366 : connman.ForEachNode([&](const CNode* pnode) {
227 1967 : auto verifiedProRegTxHash = pnode->GetVerifiedProRegTxHash();
228 1967 : if (verifiedProRegTxHash.IsNull()) {
229 412 : return;
230 : }
231 1555 : protoMap.emplace(verifiedProRegTxHash, pnode->nVersion);
232 1967 : });
233 :
234 399 : bool fShouldAllMembersBeConnected = IsAllMembersConnectedEnabled(params.type, m_sporkman);
235 2055 : for (const auto& m : members) {
236 1656 : if (m->dmn->proTxHash == myProTxHash) {
237 399 : continue;
238 : }
239 1257 : if (auto it = protoMap.find(m->dmn->proTxHash); it == protoMap.end()) {
240 45 : m->badConnection = fShouldAllMembersBeConnected;
241 45 : if (m->badConnection) {
242 1 : logger.Batch("%s is not connected to us, badConnection=1", m->dmn->proTxHash.ToString());
243 1 : }
244 1257 : } else if (it->second < MIN_MASTERNODE_PROTO_VERSION) {
245 17 : m->badConnection = true;
246 17 : logger.Batch("%s does not have min proto version %d (has %d)", m->dmn->proTxHash.ToString(), MIN_MASTERNODE_PROTO_VERSION, it->second);
247 17 : }
248 1257 : if (m_mn_metaman.OutboundFailedTooManyTimes(m->dmn->proTxHash)) {
249 103 : m->badConnection = true;
250 103 : logger.Batch("%s failed to connect to it too many times", m->dmn->proTxHash.ToString());
251 103 : }
252 1257 : if (m_mn_metaman.IsPlatformBanned(m->dmn->proTxHash)) {
253 8 : m->badConnection = true;
254 8 : logger.Batch("%s is Platform PoSe banned", m->dmn->proTxHash.ToString());
255 8 : }
256 : }
257 2462 : }
258 :
259 2462 : std::optional<CDKGComplaint> ActiveDKGSession::SendComplaint()
260 : {
261 2462 : CDKGLogger logger(*this, __func__, __LINE__);
262 :
263 2462 : assert(AreWeMember());
264 :
265 2462 : CDKGComplaint qc(params);
266 2462 : qc.llmqType = params.type;
267 2462 : qc.quorumHash = m_quorum_base_block_index->GetBlockHash();
268 2462 : qc.proTxHash = myProTxHash;
269 :
270 2462 : int badCount = 0;
271 2462 : int complaintCount = 0;
272 11765 : for (const auto i : util::irange(members.size())) {
273 9303 : const auto& m = members[i];
274 9303 : if (m->bad || m->badConnection) {
275 750 : qc.badMembers[i] = true;
276 750 : badCount++;
277 9303 : } else if (m->weComplain) {
278 16 : qc.complainForMembers[i] = true;
279 16 : complaintCount++;
280 16 : }
281 : }
282 :
283 2462 : if (badCount == 0 && complaintCount == 0) {
284 1928 : return std::nullopt;
285 : }
286 :
287 534 : logger.Batch("sending complaint. badCount=%d, complaintCount=%d", badCount, complaintCount);
288 :
289 534 : qc.sig = m_mn_activeman.Sign(qc.GetSignHash(), m_use_legacy_bls);
290 :
291 534 : logger.Flush();
292 :
293 1068 : dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) {
294 534 : status.statusBits.sentComplaint = true;
295 534 : return true;
296 : });
297 :
298 534 : return qc;
299 2462 : }
300 :
301 3938 : std::optional<CDKGJustification> ActiveDKGSession::VerifyAndJustify()
302 : {
303 3938 : if (!AreWeMember()) {
304 1554 : return std::nullopt;
305 : }
306 :
307 2384 : CDKGLogger logger(*this, __func__, __LINE__);
308 :
309 2384 : Uint256HashSet justifyFor;
310 :
311 11430 : for (const auto& m : members) {
312 9046 : if (m->bad) {
313 493 : continue;
314 : }
315 8553 : if (m->badMemberVotes.size() >= size_t(params.dkgBadVotesThreshold)) {
316 134 : logger.Batch("%s marked as bad as %d other members voted for this", m->dmn->proTxHash.ToString(), m->badMemberVotes.size());
317 134 : MarkBadMember(m->idx);
318 134 : continue;
319 : }
320 8419 : if (m->complaints.empty()) {
321 7137 : continue;
322 : }
323 1282 : if (m->complaints.size() != 1) {
324 0 : logger.Batch("%s sent multiple complaints", m->dmn->proTxHash.ToString());
325 0 : MarkBadMember(m->idx);
326 0 : continue;
327 : }
328 :
329 1282 : LOCK(invCs);
330 1282 : if (const auto& qc = complaints.at(*m->complaints.begin());
331 1282 : qc.complainForMembers[*myIdx]) {
332 16 : justifyFor.emplace(qc.proTxHash);
333 16 : }
334 1282 : }
335 :
336 2384 : logger.Flush();
337 2384 : if (justifyFor.empty()) {
338 2374 : return std::nullopt;
339 : }
340 10 : return SendJustification(justifyFor);
341 3938 : }
342 :
343 10 : std::optional<CDKGJustification> ActiveDKGSession::SendJustification(const Uint256HashSet& forMembers)
344 : {
345 10 : CDKGLogger logger(*this, __func__, __LINE__);
346 :
347 10 : assert(AreWeMember());
348 :
349 10 : logger.Batch("sending justification for %d members", forMembers.size());
350 :
351 10 : CDKGJustification qj;
352 10 : qj.llmqType = params.type;
353 10 : qj.quorumHash = m_quorum_base_block_index->GetBlockHash();
354 10 : qj.proTxHash = myProTxHash;
355 10 : qj.contributions.reserve(forMembers.size());
356 :
357 40 : for (const uint32_t i : util::irange(members.size())) {
358 30 : const auto& m = members[i];
359 30 : if (forMembers.count(m->dmn->proTxHash) == 0) {
360 14 : continue;
361 : }
362 16 : logger.Batch("justifying for %s", m->dmn->proTxHash.ToString());
363 :
364 16 : CBLSSecretKey skContribution = m_sk_contributions[i];
365 :
366 16 : if (i != myIdx && ShouldSimulateError(DKGError::type::JUSTIFY_LIE)) {
367 4 : logger.Batch("lying for %s", m->dmn->proTxHash.ToString());
368 4 : skContribution.MakeNewKey();
369 4 : }
370 :
371 16 : qj.contributions.emplace_back(CDKGJustification::Contribution{i, skContribution});
372 16 : }
373 :
374 10 : if (ShouldSimulateError(DKGError::type::JUSTIFY_OMIT)) {
375 2 : logger.Batch("omitting");
376 2 : return std::nullopt;
377 : }
378 :
379 8 : qj.sig = m_mn_activeman.Sign(qj.GetSignHash(), m_use_legacy_bls);
380 :
381 8 : logger.Flush();
382 :
383 16 : dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) {
384 8 : status.statusBits.sentJustification = true;
385 8 : return true;
386 : });
387 :
388 8 : return qj;
389 10 : }
390 :
391 3868 : std::optional<CDKGPrematureCommitment> ActiveDKGSession::VerifyAndCommit()
392 : {
393 3868 : if (!AreWeMember()) {
394 1524 : return std::nullopt;
395 : }
396 :
397 2344 : CDKGLogger logger(*this, __func__, __LINE__);
398 :
399 2344 : std::vector<size_t> badMembers;
400 2344 : badMembers.reserve(members.size());
401 2344 : std::vector<size_t> openComplaintMembers;
402 2344 : openComplaintMembers.reserve(members.size());
403 :
404 11201 : for (const auto& m : members) {
405 8869 : if (m->bad) {
406 606 : badMembers.emplace_back(m->idx);
407 606 : continue;
408 : }
409 8263 : if (!m->complaintsFromOthers.empty()) {
410 6 : MarkBadMember(m->idx);
411 6 : openComplaintMembers.emplace_back(m->idx);
412 6 : }
413 : }
414 :
415 2332 : if (!badMembers.empty() || !openComplaintMembers.empty()) {
416 453 : logger.Batch("verification result:");
417 459 : }
418 2338 : if (!badMembers.empty()) {
419 453 : logger.Batch(" members previously determined as bad:");
420 1059 : for (const auto& idx : badMembers) {
421 606 : logger.Batch(" %s", members[idx]->dmn->proTxHash.ToString());
422 : }
423 453 : }
424 2338 : if (!openComplaintMembers.empty()) {
425 6 : logger.Batch(" members with open complaints and now marked as bad:");
426 12 : for (const auto& idx : openComplaintMembers) {
427 6 : logger.Batch(" %s", members[idx]->dmn->proTxHash.ToString());
428 : }
429 6 : }
430 :
431 2338 : logger.Flush();
432 :
433 2332 : return SendCommitment();
434 3880 : }
435 :
436 2336 : std::optional<CDKGPrematureCommitment> ActiveDKGSession::SendCommitment()
437 : {
438 2336 : CDKGLogger logger(*this, __func__, __LINE__);
439 :
440 2336 : assert(AreWeMember());
441 :
442 2332 : logger.Batch("sending commitment");
443 :
444 2332 : CDKGPrematureCommitment qc(params);
445 2336 : qc.llmqType = params.type;
446 2336 : qc.quorumHash = m_quorum_base_block_index->GetBlockHash();
447 2332 : qc.proTxHash = myProTxHash;
448 :
449 11198 : for (const auto i : util::irange(members.size())) {
450 8865 : const auto& m = members[i];
451 8865 : if (!m->bad) {
452 8253 : qc.validMembers[i] = true;
453 8254 : }
454 : }
455 :
456 2331 : if (qc.CountValidMembers() < params.minSize) {
457 315 : logger.Batch("not enough valid members. not sending commitment");
458 315 : return std::nullopt;
459 : }
460 :
461 2017 : if (ShouldSimulateError(DKGError::type::COMMIT_OMIT)) {
462 2 : logger.Batch("omitting");
463 2 : return std::nullopt;
464 : }
465 :
466 2015 : cxxtimer::Timer timerTotal(true);
467 :
468 2015 : cxxtimer::Timer t1(true);
469 2015 : std::vector<uint16_t> memberIndexes;
470 2015 : std::vector<BLSVerificationVectorPtr> vvecs;
471 2015 : std::vector<CBLSSecretKey> skContributions;
472 2015 : if (!dkgManager.GetVerifiedContributions(params.type, m_quorum_base_block_index, qc.validMembers, memberIndexes, vvecs, skContributions)) {
473 0 : logger.Batch("failed to get valid contributions");
474 0 : return std::nullopt;
475 : }
476 :
477 2015 : BLSVerificationVectorPtr vvec = cache.BuildQuorumVerificationVector(::SerializeHash(memberIndexes), vvecs);
478 2015 : if (vvec == nullptr) {
479 0 : logger.Batch("failed to build quorum verification vector");
480 0 : return std::nullopt;
481 : }
482 2015 : t1.stop();
483 :
484 2015 : cxxtimer::Timer t2(true);
485 2015 : CBLSSecretKey skShare = cache.AggregateSecretKeys(::SerializeHash(memberIndexes), skContributions);
486 2015 : if (!skShare.IsValid()) {
487 0 : logger.Batch("failed to build own secret share");
488 0 : return std::nullopt;
489 : }
490 2015 : t2.stop();
491 :
492 2015 : logger.Batch("pubKeyShare=%s", skShare.GetPublicKey().ToString());
493 :
494 2015 : cxxtimer::Timer t3(true);
495 2015 : qc.quorumPublicKey = (*vvec)[0];
496 2015 : qc.quorumVvecHash = ::SerializeHash(*vvec);
497 :
498 2015 : int lieType = -1;
499 2015 : if (ShouldSimulateError(DKGError::type::COMMIT_LIE)) {
500 2 : lieType = GetRand<int>(/*nMax=*/5);
501 2 : logger.Batch("lying on commitment. lieType=%d", lieType);
502 2 : }
503 :
504 2015 : if (lieType == 0) {
505 0 : CBLSSecretKey k;
506 0 : k.MakeNewKey();
507 0 : qc.quorumPublicKey = k.GetPublicKey();
508 2015 : } else if (lieType == 1) {
509 0 : (*qc.quorumVvecHash.begin())++;
510 0 : }
511 :
512 2015 : uint256 commitmentHash = BuildCommitmentHash(qc.llmqType, qc.quorumHash, qc.validMembers, qc.quorumPublicKey, qc.quorumVvecHash);
513 :
514 2015 : if (lieType == 2) {
515 1 : (*commitmentHash.begin())++;
516 1 : }
517 :
518 2015 : qc.sig = m_mn_activeman.Sign(commitmentHash, m_use_legacy_bls);
519 2015 : qc.quorumSig = skShare.Sign(commitmentHash, m_use_legacy_bls);
520 :
521 2015 : if (lieType == 3) {
522 0 : auto buf = qc.sig.ToBytes(m_use_legacy_bls);
523 0 : buf[5]++;
524 0 : qc.sig.SetBytes(buf, m_use_legacy_bls);
525 2015 : } else if (lieType == 4) {
526 1 : auto buf = qc.quorumSig.ToBytes(m_use_legacy_bls);
527 1 : buf[5]++;
528 1 : qc.quorumSig.SetBytes(buf, m_use_legacy_bls);
529 1 : }
530 :
531 2015 : t3.stop();
532 2015 : timerTotal.stop();
533 :
534 4030 : logger.Batch("built premature commitment. time1=%d, time2=%d, time3=%d, totalTime=%d",
535 2015 : t1.count(), t2.count(), t3.count(), timerTotal.count());
536 :
537 :
538 2015 : logger.Flush();
539 :
540 4030 : dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) {
541 2015 : status.statusBits.sentPrematureCommitment = true;
542 2015 : return true;
543 : });
544 :
545 2015 : return qc;
546 2348 : }
547 :
548 3747 : std::vector<CFinalCommitment> ActiveDKGSession::FinalizeCommitments()
549 : {
550 3747 : if (!AreWeMember()) {
551 1477 : return {};
552 : }
553 :
554 2270 : CDKGLogger logger(*this, __func__, __LINE__);
555 :
556 : using Key = std::vector<bool>;
557 2270 : std::map<Key, std::vector<CDKGPrematureCommitment>> commitmentsMap;
558 :
559 : {
560 2270 : LOCK(invCs);
561 :
562 9610 : for (const auto& p : prematureCommitments) {
563 7342 : const auto& qc = p.second;
564 7342 : if (validCommitments.count(p.first) == 0) {
565 0 : continue;
566 : }
567 :
568 : // should have been verified before
569 7342 : assert(qc.CountValidMembers() >= params.minSize);
570 :
571 7343 : auto it = commitmentsMap.find(qc.validMembers);
572 7343 : if (it == commitmentsMap.end()) {
573 2065 : it = commitmentsMap.emplace(qc.validMembers, std::vector<CDKGPrematureCommitment>()).first;
574 2065 : }
575 :
576 7342 : it->second.emplace_back(qc);
577 : }
578 2264 : }
579 :
580 2264 : std::vector<CFinalCommitment> finalCommitments;
581 4329 : for (const auto& p : commitmentsMap) {
582 2079 : const auto& cvec = p.second;
583 2079 : if (cvec.size() < size_t(params.minSize)) {
584 : // commitment was signed by a minority
585 214 : continue;
586 : }
587 :
588 1865 : std::vector<CBLSId> signerIds;
589 1865 : std::vector<CBLSSignature> thresholdSigs;
590 :
591 1865 : const auto& first = cvec[0];
592 :
593 1865 : CFinalCommitment fqc(params, first.quorumHash);
594 1865 : fqc.validMembers = first.validMembers;
595 1850 : fqc.quorumPublicKey = first.quorumPublicKey;
596 1850 : fqc.quorumVvecHash = first.quorumVvecHash;
597 :
598 1850 : const bool isQuorumRotationEnabled{IsQuorumRotationEnabled(params, m_quorum_base_block_index)};
599 : // TODO: always put `true` here: so far as v19 is activated, we always write BASIC now
600 1849 : fqc.nVersion = CFinalCommitment::GetVersion(isQuorumRotationEnabled, DeploymentActiveAfter(m_quorum_base_block_index, m_chainman.GetConsensus(), Consensus::DEPLOYMENT_V19));
601 1849 : fqc.quorumIndex = isQuorumRotationEnabled ? quorumIndex : 0;
602 :
603 1849 : uint256 commitmentHash = BuildCommitmentHash(fqc.llmqType, fqc.quorumHash, fqc.validMembers, fqc.quorumPublicKey, fqc.quorumVvecHash);
604 :
605 1859 : std::vector<CBLSSignature> aggSigs;
606 1859 : std::vector<CBLSPublicKey> aggPks;
607 1859 : aggSigs.reserve(cvec.size());
608 1850 : aggPks.reserve(cvec.size());
609 :
610 8755 : for (const auto& qc : cvec) {
611 6904 : if (qc.quorumPublicKey != first.quorumPublicKey || qc.quorumVvecHash != first.quorumVvecHash) {
612 1 : logger.Batch("quorumPublicKey or quorumVvecHash does not match, skipping");
613 0 : continue;
614 : }
615 :
616 6904 : size_t signerIndex = membersMap[qc.proTxHash];
617 6905 : const auto& m = members[signerIndex];
618 :
619 6905 : fqc.signers[signerIndex] = true;
620 6905 : aggSigs.emplace_back(qc.sig);
621 6904 : aggPks.emplace_back(m->dmn->pdmnState->pubKeyOperator.Get());
622 :
623 6903 : signerIds.emplace_back(m->id);
624 6904 : thresholdSigs.emplace_back(qc.quorumSig);
625 : }
626 :
627 1851 : cxxtimer::Timer t1(true);
628 1851 : fqc.membersSig = CBLSSignature::AggregateSecure(aggSigs, aggPks, commitmentHash);
629 1851 : t1.stop();
630 :
631 1851 : cxxtimer::Timer t2(true);
632 1851 : if (!fqc.quorumSig.Recover(thresholdSigs, signerIds)) {
633 0 : logger.Batch("failed to recover quorum sig");
634 0 : continue;
635 : }
636 1851 : t2.stop();
637 :
638 1851 : cxxtimer::Timer t3(true);
639 1851 : if (!fqc.Verify({m_dmnman, m_qsnapman, m_chainman, m_quorum_base_block_index}, true)) {
640 0 : logger.Batch("failed to verify final commitment");
641 0 : continue;
642 : }
643 1851 : t3.stop();
644 :
645 1851 : finalCommitments.emplace_back(fqc);
646 :
647 3702 : logger.Batch("final commitment: validMembers=%d, signers=%d, quorumPublicKey=%s, time1=%d, time2=%d, time3=%d",
648 1851 : fqc.CountValidMembers(), fqc.CountSigners(), fqc.quorumPublicKey.ToString(),
649 1851 : t1.count(), t2.count(), t3.count());
650 1851 : }
651 :
652 2250 : logger.Flush();
653 :
654 2250 : return finalCommitments;
655 3855 : }
656 :
657 64 : CFinalCommitment ActiveDKGSession::FinalizeSingleCommitment()
658 : {
659 64 : if (!AreWeMember()) {
660 31 : return {};
661 : }
662 :
663 33 : CDKGLogger logger(*this, __func__, __LINE__);
664 :
665 33 : std::vector<CBLSId> signerIds;
666 33 : std::vector<CBLSSignature> thresholdSigs;
667 :
668 33 : CFinalCommitment fqc(params, m_quorum_base_block_index->GetBlockHash());
669 :
670 :
671 33 : fqc.signers = {true};
672 33 : fqc.validMembers = {true};
673 :
674 33 : CBLSSecretKey sk1;
675 33 : sk1.MakeNewKey();
676 :
677 33 : fqc.quorumPublicKey = sk1.GetPublicKey();
678 33 : fqc.quorumVvecHash = {};
679 :
680 : // use just MN's operator public key as quorum pubkey.
681 : // TODO: use sk1 here instead and use recovery mechanism from shares, but that's not trivial to do
682 33 : const bool workaround_qpublic_key = true;
683 : if (workaround_qpublic_key) {
684 33 : fqc.quorumPublicKey = m_mn_activeman.GetPubKey();
685 : }
686 33 : const bool isQuorumRotationEnabled{false};
687 33 : fqc.nVersion = CFinalCommitment::GetVersion(isQuorumRotationEnabled,
688 33 : DeploymentActiveAfter(m_quorum_base_block_index, m_chainman.GetConsensus(),
689 : Consensus::DEPLOYMENT_V19));
690 33 : fqc.quorumIndex = 0;
691 :
692 66 : uint256 commitmentHash = BuildCommitmentHash(fqc.llmqType, fqc.quorumHash, fqc.validMembers, fqc.quorumPublicKey,
693 33 : fqc.quorumVvecHash);
694 33 : fqc.quorumSig = sk1.Sign(commitmentHash, m_use_legacy_bls);
695 :
696 33 : fqc.membersSig = m_mn_activeman.Sign(commitmentHash, m_use_legacy_bls);
697 :
698 : if (workaround_qpublic_key) {
699 33 : fqc.quorumSig = fqc.membersSig;
700 : }
701 :
702 33 : if (!fqc.Verify({m_dmnman, m_qsnapman, m_chainman, m_quorum_base_block_index}, true)) {
703 0 : logger.Batch("failed to verify final commitment");
704 0 : assert(false);
705 : }
706 :
707 66 : logger.Batch("final commitment: validMembers=%d, signers=%d, quorumPublicKey=%s", fqc.CountValidMembers(),
708 33 : fqc.CountSigners(), fqc.quorumPublicKey.ToString());
709 :
710 33 : logger.Flush();
711 :
712 33 : return fqc;
713 64 : }
714 :
715 8948 : bool ActiveDKGSession::MaybeDecrypt(const CBLSIESMultiRecipientObjects<CBLSSecretKey>& obj, size_t idx,
716 : CBLSSecretKey& ret_obj, int version)
717 : {
718 8948 : return m_mn_activeman.Decrypt(obj, idx, ret_obj, version);
719 : }
720 : } // namespace llmq
|