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 0 : 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 0 : CDKGSession(bls_worker, dmnman, dkgdbgman, qdkgsman, qsnapman, chainman, base_block_index, llmq_params),
31 0 : m_mn_metaman{mn_metaman},
32 0 : m_mn_activeman{mn_activeman},
33 0 : m_sporkman{sporkman},
34 0 : m_use_legacy_bls{!DeploymentActiveAfter(m_quorum_base_block_index, Params().GetConsensus(), Consensus::DEPLOYMENT_V19)}
35 0 : {
36 0 : }
37 :
38 0 : ActiveDKGSession::~ActiveDKGSession() = default;
39 :
40 0 : std::optional<CDKGContribution> ActiveDKGSession::Contribute()
41 : {
42 0 : if (!AreWeMember()) {
43 0 : return std::nullopt;
44 : }
45 :
46 0 : assert(params.threshold > 1); // we should not get there with single-node-quorums
47 :
48 0 : CDKGLogger logger(*this, __func__, __LINE__);
49 :
50 0 : cxxtimer::Timer t1(true);
51 0 : logger.Batch("generating contributions");
52 0 : 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 0 : logger.Batch("generated contributions. time=%d", t1.count());
58 0 : logger.Flush();
59 :
60 0 : return SendContributions();
61 0 : }
62 :
63 0 : std::optional<CDKGContribution> ActiveDKGSession::SendContributions()
64 : {
65 0 : CDKGLogger logger(*this, __func__, __LINE__);
66 :
67 0 : assert(AreWeMember());
68 :
69 0 : logger.Batch("sending contributions");
70 :
71 0 : if (ShouldSimulateError(DKGError::type::CONTRIBUTION_OMIT)) {
72 0 : logger.Batch("omitting");
73 0 : return std::nullopt;
74 : }
75 :
76 0 : CDKGContribution qc;
77 0 : qc.llmqType = params.type;
78 0 : qc.quorumHash = m_quorum_base_block_index->GetBlockHash();
79 0 : qc.proTxHash = myProTxHash;
80 0 : qc.vvec = vvecContribution;
81 :
82 0 : cxxtimer::Timer t1(true);
83 0 : qc.contributions = std::make_shared<CBLSIESMultiRecipientObjects<CBLSSecretKey>>();
84 0 : qc.contributions->InitEncrypt(members.size());
85 :
86 0 : for (const auto i : util::irange(members.size())) {
87 0 : const auto& m = members[i];
88 0 : CBLSSecretKey skContrib = m_sk_contributions[i];
89 :
90 0 : if (i != myIdx && ShouldSimulateError(DKGError::type::CONTRIBUTION_LIE)) {
91 0 : logger.Batch("lying for %s", m->dmn->proTxHash.ToString());
92 0 : skContrib.MakeNewKey();
93 0 : }
94 :
95 0 : 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 0 : }
100 :
101 0 : logger.Batch("encrypted contributions. time=%d", t1.count());
102 :
103 0 : qc.sig = m_mn_activeman.Sign(qc.GetSignHash(), m_use_legacy_bls);
104 :
105 0 : logger.Flush();
106 :
107 0 : dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) {
108 0 : status.statusBits.sentContributions = true;
109 0 : return true;
110 : });
111 :
112 0 : return qc;
113 0 : }
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 0 : void ActiveDKGSession::VerifyPendingContributions()
121 : {
122 0 : AssertLockHeld(cs_pending);
123 :
124 0 : CDKGLogger logger(*this, __func__, __LINE__);
125 :
126 0 : cxxtimer::Timer t1(true);
127 :
128 0 : if (pendingContributionVerifications.empty()) {
129 0 : return;
130 : }
131 :
132 0 : std::vector<size_t> memberIndexes;
133 0 : std::vector<BLSVerificationVectorPtr> vvecs;
134 0 : std::vector<CBLSSecretKey> skContributions;
135 :
136 0 : for (const auto& idx : pendingContributionVerifications) {
137 0 : const auto& m = members[idx];
138 0 : if (m->bad || m->weComplain) {
139 0 : continue;
140 : }
141 0 : memberIndexes.emplace_back(idx);
142 0 : vvecs.emplace_back(receivedVvecs[idx]);
143 0 : 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 0 : dkgManager.WriteEncryptedContributions(params.type, m_quorum_base_block_index, m->dmn->proTxHash, *vecEncryptedContributions[idx]);
147 : }
148 :
149 0 : auto result = blsWorker.VerifyContributionShares(myId, vvecs, skContributions);
150 0 : if (result.size() != memberIndexes.size()) {
151 0 : 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 0 : for (const auto i : util::irange(memberIndexes.size())) {
156 0 : if (!result[i]) {
157 0 : const auto& m = members[memberIndexes[i]];
158 0 : logger.Batch("invalid contribution from %s. will complain later", m->dmn->proTxHash.ToString());
159 0 : m->weComplain = true;
160 0 : dkgDebugManager.UpdateLocalMemberStatus(params.type, quorumIndex, m->idx, [&](CDKGDebugMemberStatus& status) {
161 0 : status.statusBits.weComplain = true;
162 0 : return true;
163 : });
164 0 : } else {
165 0 : size_t memberIdx = memberIndexes[i];
166 0 : dkgManager.WriteVerifiedSkContribution(params.type, m_quorum_base_block_index, members[memberIdx]->dmn->proTxHash, skContributions[i]);
167 : }
168 : }
169 :
170 0 : logger.Batch("verified %d pending contributions. time=%d", pendingContributionVerifications.size(), t1.count());
171 0 : pendingContributionVerifications.clear();
172 0 : }
173 :
174 0 : std::optional<CDKGComplaint> ActiveDKGSession::VerifyAndComplain(CConnman& connman)
175 : {
176 0 : if (!AreWeMember()) {
177 0 : return std::nullopt;
178 : }
179 :
180 : {
181 0 : LOCK(cs_pending);
182 0 : VerifyPendingContributions();
183 0 : }
184 :
185 0 : 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 0 : cxxtimer::Timer t1(true);
195 :
196 0 : for (const auto& m : members) {
197 0 : if (m->bad) {
198 0 : continue;
199 : }
200 0 : if (m->contributions.empty()) {
201 0 : logger.Batch("%s did not send any contribution", m->dmn->proTxHash.ToString());
202 0 : MarkBadMember(m->idx);
203 0 : continue;
204 : }
205 : }
206 :
207 0 : logger.Batch("verified contributions. time=%d", t1.count());
208 0 : logger.Flush();
209 :
210 0 : VerifyConnectionAndMinProtoVersions(connman);
211 :
212 0 : return SendComplaint();
213 0 : }
214 :
215 0 : void ActiveDKGSession::VerifyConnectionAndMinProtoVersions(CConnman& connman) const
216 : {
217 0 : assert(m_mn_metaman.IsValid());
218 :
219 0 : if (!IsQuorumPoseEnabled(params.type, m_sporkman)) {
220 0 : return;
221 : }
222 :
223 0 : CDKGLogger logger(*this, __func__, __LINE__);
224 :
225 0 : Uint256HashMap<int> protoMap;
226 0 : connman.ForEachNode([&](const CNode* pnode) {
227 0 : auto verifiedProRegTxHash = pnode->GetVerifiedProRegTxHash();
228 0 : if (verifiedProRegTxHash.IsNull()) {
229 0 : return;
230 : }
231 0 : protoMap.emplace(verifiedProRegTxHash, pnode->nVersion);
232 0 : });
233 :
234 0 : bool fShouldAllMembersBeConnected = IsAllMembersConnectedEnabled(params.type, m_sporkman);
235 0 : for (const auto& m : members) {
236 0 : if (m->dmn->proTxHash == myProTxHash) {
237 0 : continue;
238 : }
239 0 : if (auto it = protoMap.find(m->dmn->proTxHash); it == protoMap.end()) {
240 0 : m->badConnection = fShouldAllMembersBeConnected;
241 0 : if (m->badConnection) {
242 0 : logger.Batch("%s is not connected to us, badConnection=1", m->dmn->proTxHash.ToString());
243 0 : }
244 0 : } else if (it->second < MIN_MASTERNODE_PROTO_VERSION) {
245 0 : m->badConnection = true;
246 0 : logger.Batch("%s does not have min proto version %d (has %d)", m->dmn->proTxHash.ToString(), MIN_MASTERNODE_PROTO_VERSION, it->second);
247 0 : }
248 0 : if (m_mn_metaman.OutboundFailedTooManyTimes(m->dmn->proTxHash)) {
249 0 : m->badConnection = true;
250 0 : logger.Batch("%s failed to connect to it too many times", m->dmn->proTxHash.ToString());
251 0 : }
252 0 : if (m_mn_metaman.IsPlatformBanned(m->dmn->proTxHash)) {
253 0 : m->badConnection = true;
254 0 : logger.Batch("%s is Platform PoSe banned", m->dmn->proTxHash.ToString());
255 0 : }
256 : }
257 0 : }
258 :
259 0 : std::optional<CDKGComplaint> ActiveDKGSession::SendComplaint()
260 : {
261 0 : CDKGLogger logger(*this, __func__, __LINE__);
262 :
263 0 : assert(AreWeMember());
264 :
265 0 : CDKGComplaint qc(params);
266 0 : qc.llmqType = params.type;
267 0 : qc.quorumHash = m_quorum_base_block_index->GetBlockHash();
268 0 : qc.proTxHash = myProTxHash;
269 :
270 0 : int badCount = 0;
271 0 : int complaintCount = 0;
272 0 : for (const auto i : util::irange(members.size())) {
273 0 : const auto& m = members[i];
274 0 : if (m->bad || m->badConnection) {
275 0 : qc.badMembers[i] = true;
276 0 : badCount++;
277 0 : } else if (m->weComplain) {
278 0 : qc.complainForMembers[i] = true;
279 0 : complaintCount++;
280 0 : }
281 : }
282 :
283 0 : if (badCount == 0 && complaintCount == 0) {
284 0 : return std::nullopt;
285 : }
286 :
287 0 : logger.Batch("sending complaint. badCount=%d, complaintCount=%d", badCount, complaintCount);
288 :
289 0 : qc.sig = m_mn_activeman.Sign(qc.GetSignHash(), m_use_legacy_bls);
290 :
291 0 : logger.Flush();
292 :
293 0 : dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) {
294 0 : status.statusBits.sentComplaint = true;
295 0 : return true;
296 : });
297 :
298 0 : return qc;
299 0 : }
300 :
301 0 : std::optional<CDKGJustification> ActiveDKGSession::VerifyAndJustify()
302 : {
303 0 : if (!AreWeMember()) {
304 0 : return std::nullopt;
305 : }
306 :
307 0 : CDKGLogger logger(*this, __func__, __LINE__);
308 :
309 0 : Uint256HashSet justifyFor;
310 :
311 0 : for (const auto& m : members) {
312 0 : if (m->bad) {
313 0 : continue;
314 : }
315 0 : if (m->badMemberVotes.size() >= size_t(params.dkgBadVotesThreshold)) {
316 0 : logger.Batch("%s marked as bad as %d other members voted for this", m->dmn->proTxHash.ToString(), m->badMemberVotes.size());
317 0 : MarkBadMember(m->idx);
318 0 : continue;
319 : }
320 0 : if (m->complaints.empty()) {
321 0 : continue;
322 : }
323 0 : 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 0 : LOCK(invCs);
330 0 : if (const auto& qc = complaints.at(*m->complaints.begin());
331 0 : qc.complainForMembers[*myIdx]) {
332 0 : justifyFor.emplace(qc.proTxHash);
333 0 : }
334 0 : }
335 :
336 0 : logger.Flush();
337 0 : if (justifyFor.empty()) {
338 0 : return std::nullopt;
339 : }
340 0 : return SendJustification(justifyFor);
341 0 : }
342 :
343 0 : std::optional<CDKGJustification> ActiveDKGSession::SendJustification(const Uint256HashSet& forMembers)
344 : {
345 0 : CDKGLogger logger(*this, __func__, __LINE__);
346 :
347 0 : assert(AreWeMember());
348 :
349 0 : logger.Batch("sending justification for %d members", forMembers.size());
350 :
351 0 : CDKGJustification qj;
352 0 : qj.llmqType = params.type;
353 0 : qj.quorumHash = m_quorum_base_block_index->GetBlockHash();
354 0 : qj.proTxHash = myProTxHash;
355 0 : qj.contributions.reserve(forMembers.size());
356 :
357 0 : for (const uint32_t i : util::irange(members.size())) {
358 0 : const auto& m = members[i];
359 0 : if (forMembers.count(m->dmn->proTxHash) == 0) {
360 0 : continue;
361 : }
362 0 : logger.Batch("justifying for %s", m->dmn->proTxHash.ToString());
363 :
364 0 : CBLSSecretKey skContribution = m_sk_contributions[i];
365 :
366 0 : if (i != myIdx && ShouldSimulateError(DKGError::type::JUSTIFY_LIE)) {
367 0 : logger.Batch("lying for %s", m->dmn->proTxHash.ToString());
368 0 : skContribution.MakeNewKey();
369 0 : }
370 :
371 0 : qj.contributions.emplace_back(CDKGJustification::Contribution{i, skContribution});
372 0 : }
373 :
374 0 : if (ShouldSimulateError(DKGError::type::JUSTIFY_OMIT)) {
375 0 : logger.Batch("omitting");
376 0 : return std::nullopt;
377 : }
378 :
379 0 : qj.sig = m_mn_activeman.Sign(qj.GetSignHash(), m_use_legacy_bls);
380 :
381 0 : logger.Flush();
382 :
383 0 : dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) {
384 0 : status.statusBits.sentJustification = true;
385 0 : return true;
386 : });
387 :
388 0 : return qj;
389 0 : }
390 :
391 0 : std::optional<CDKGPrematureCommitment> ActiveDKGSession::VerifyAndCommit()
392 : {
393 0 : if (!AreWeMember()) {
394 0 : return std::nullopt;
395 : }
396 :
397 0 : CDKGLogger logger(*this, __func__, __LINE__);
398 :
399 0 : std::vector<size_t> badMembers;
400 0 : badMembers.reserve(members.size());
401 0 : std::vector<size_t> openComplaintMembers;
402 0 : openComplaintMembers.reserve(members.size());
403 :
404 0 : for (const auto& m : members) {
405 0 : if (m->bad) {
406 0 : badMembers.emplace_back(m->idx);
407 0 : continue;
408 : }
409 0 : if (!m->complaintsFromOthers.empty()) {
410 0 : MarkBadMember(m->idx);
411 0 : openComplaintMembers.emplace_back(m->idx);
412 0 : }
413 : }
414 :
415 0 : if (!badMembers.empty() || !openComplaintMembers.empty()) {
416 0 : logger.Batch("verification result:");
417 0 : }
418 0 : if (!badMembers.empty()) {
419 0 : logger.Batch(" members previously determined as bad:");
420 0 : for (const auto& idx : badMembers) {
421 0 : logger.Batch(" %s", members[idx]->dmn->proTxHash.ToString());
422 : }
423 0 : }
424 0 : if (!openComplaintMembers.empty()) {
425 0 : logger.Batch(" members with open complaints and now marked as bad:");
426 0 : for (const auto& idx : openComplaintMembers) {
427 0 : logger.Batch(" %s", members[idx]->dmn->proTxHash.ToString());
428 : }
429 0 : }
430 :
431 0 : logger.Flush();
432 :
433 0 : return SendCommitment();
434 0 : }
435 :
436 0 : std::optional<CDKGPrematureCommitment> ActiveDKGSession::SendCommitment()
437 : {
438 0 : CDKGLogger logger(*this, __func__, __LINE__);
439 :
440 0 : assert(AreWeMember());
441 :
442 0 : logger.Batch("sending commitment");
443 :
444 0 : CDKGPrematureCommitment qc(params);
445 0 : qc.llmqType = params.type;
446 0 : qc.quorumHash = m_quorum_base_block_index->GetBlockHash();
447 0 : qc.proTxHash = myProTxHash;
448 :
449 0 : for (const auto i : util::irange(members.size())) {
450 0 : const auto& m = members[i];
451 0 : if (!m->bad) {
452 0 : qc.validMembers[i] = true;
453 0 : }
454 : }
455 :
456 0 : if (qc.CountValidMembers() < params.minSize) {
457 0 : logger.Batch("not enough valid members. not sending commitment");
458 0 : return std::nullopt;
459 : }
460 :
461 0 : if (ShouldSimulateError(DKGError::type::COMMIT_OMIT)) {
462 0 : logger.Batch("omitting");
463 0 : return std::nullopt;
464 : }
465 :
466 0 : cxxtimer::Timer timerTotal(true);
467 :
468 0 : cxxtimer::Timer t1(true);
469 0 : std::vector<uint16_t> memberIndexes;
470 0 : std::vector<BLSVerificationVectorPtr> vvecs;
471 0 : std::vector<CBLSSecretKey> skContributions;
472 0 : 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 0 : BLSVerificationVectorPtr vvec = cache.BuildQuorumVerificationVector(::SerializeHash(memberIndexes), vvecs);
478 0 : if (vvec == nullptr) {
479 0 : logger.Batch("failed to build quorum verification vector");
480 0 : return std::nullopt;
481 : }
482 0 : t1.stop();
483 :
484 0 : cxxtimer::Timer t2(true);
485 0 : CBLSSecretKey skShare = cache.AggregateSecretKeys(::SerializeHash(memberIndexes), skContributions);
486 0 : if (!skShare.IsValid()) {
487 0 : logger.Batch("failed to build own secret share");
488 0 : return std::nullopt;
489 : }
490 0 : t2.stop();
491 :
492 0 : logger.Batch("pubKeyShare=%s", skShare.GetPublicKey().ToString());
493 :
494 0 : cxxtimer::Timer t3(true);
495 0 : qc.quorumPublicKey = (*vvec)[0];
496 0 : qc.quorumVvecHash = ::SerializeHash(*vvec);
497 :
498 0 : int lieType = -1;
499 0 : if (ShouldSimulateError(DKGError::type::COMMIT_LIE)) {
500 0 : lieType = GetRand<int>(/*nMax=*/5);
501 0 : logger.Batch("lying on commitment. lieType=%d", lieType);
502 0 : }
503 :
504 0 : if (lieType == 0) {
505 0 : CBLSSecretKey k;
506 0 : k.MakeNewKey();
507 0 : qc.quorumPublicKey = k.GetPublicKey();
508 0 : } else if (lieType == 1) {
509 0 : (*qc.quorumVvecHash.begin())++;
510 0 : }
511 :
512 0 : uint256 commitmentHash = BuildCommitmentHash(qc.llmqType, qc.quorumHash, qc.validMembers, qc.quorumPublicKey, qc.quorumVvecHash);
513 :
514 0 : if (lieType == 2) {
515 0 : (*commitmentHash.begin())++;
516 0 : }
517 :
518 0 : qc.sig = m_mn_activeman.Sign(commitmentHash, m_use_legacy_bls);
519 0 : qc.quorumSig = skShare.Sign(commitmentHash, m_use_legacy_bls);
520 :
521 0 : 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 0 : } else if (lieType == 4) {
526 0 : auto buf = qc.quorumSig.ToBytes(m_use_legacy_bls);
527 0 : buf[5]++;
528 0 : qc.quorumSig.SetBytes(buf, m_use_legacy_bls);
529 0 : }
530 :
531 0 : t3.stop();
532 0 : timerTotal.stop();
533 :
534 0 : logger.Batch("built premature commitment. time1=%d, time2=%d, time3=%d, totalTime=%d",
535 0 : t1.count(), t2.count(), t3.count(), timerTotal.count());
536 :
537 :
538 0 : logger.Flush();
539 :
540 0 : dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) {
541 0 : status.statusBits.sentPrematureCommitment = true;
542 0 : return true;
543 : });
544 :
545 0 : return qc;
546 0 : }
547 :
548 0 : std::vector<CFinalCommitment> ActiveDKGSession::FinalizeCommitments()
549 : {
550 0 : if (!AreWeMember()) {
551 0 : return {};
552 : }
553 :
554 0 : CDKGLogger logger(*this, __func__, __LINE__);
555 :
556 : using Key = std::vector<bool>;
557 0 : std::map<Key, std::vector<CDKGPrematureCommitment>> commitmentsMap;
558 :
559 : {
560 0 : LOCK(invCs);
561 :
562 0 : for (const auto& p : prematureCommitments) {
563 0 : const auto& qc = p.second;
564 0 : if (validCommitments.count(p.first) == 0) {
565 0 : continue;
566 : }
567 :
568 : // should have been verified before
569 0 : assert(qc.CountValidMembers() >= params.minSize);
570 :
571 0 : auto it = commitmentsMap.find(qc.validMembers);
572 0 : if (it == commitmentsMap.end()) {
573 0 : it = commitmentsMap.emplace(qc.validMembers, std::vector<CDKGPrematureCommitment>()).first;
574 0 : }
575 :
576 0 : it->second.emplace_back(qc);
577 : }
578 0 : }
579 :
580 0 : std::vector<CFinalCommitment> finalCommitments;
581 0 : for (const auto& p : commitmentsMap) {
582 0 : const auto& cvec = p.second;
583 0 : if (cvec.size() < size_t(params.minSize)) {
584 : // commitment was signed by a minority
585 0 : continue;
586 : }
587 :
588 0 : std::vector<CBLSId> signerIds;
589 0 : std::vector<CBLSSignature> thresholdSigs;
590 :
591 0 : const auto& first = cvec[0];
592 :
593 0 : CFinalCommitment fqc(params, first.quorumHash);
594 0 : fqc.validMembers = first.validMembers;
595 0 : fqc.quorumPublicKey = first.quorumPublicKey;
596 0 : fqc.quorumVvecHash = first.quorumVvecHash;
597 :
598 0 : 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 0 : fqc.nVersion = CFinalCommitment::GetVersion(isQuorumRotationEnabled, DeploymentActiveAfter(m_quorum_base_block_index, m_chainman.GetConsensus(), Consensus::DEPLOYMENT_V19));
601 0 : fqc.quorumIndex = isQuorumRotationEnabled ? quorumIndex : 0;
602 :
603 0 : uint256 commitmentHash = BuildCommitmentHash(fqc.llmqType, fqc.quorumHash, fqc.validMembers, fqc.quorumPublicKey, fqc.quorumVvecHash);
604 :
605 0 : std::vector<CBLSSignature> aggSigs;
606 0 : std::vector<CBLSPublicKey> aggPks;
607 0 : aggSigs.reserve(cvec.size());
608 0 : aggPks.reserve(cvec.size());
609 :
610 0 : for (const auto& qc : cvec) {
611 0 : if (qc.quorumPublicKey != first.quorumPublicKey || qc.quorumVvecHash != first.quorumVvecHash) {
612 0 : logger.Batch("quorumPublicKey or quorumVvecHash does not match, skipping");
613 0 : continue;
614 : }
615 :
616 0 : size_t signerIndex = membersMap[qc.proTxHash];
617 0 : const auto& m = members[signerIndex];
618 :
619 0 : fqc.signers[signerIndex] = true;
620 0 : aggSigs.emplace_back(qc.sig);
621 0 : aggPks.emplace_back(m->dmn->pdmnState->pubKeyOperator.Get());
622 :
623 0 : signerIds.emplace_back(m->id);
624 0 : thresholdSigs.emplace_back(qc.quorumSig);
625 : }
626 :
627 0 : cxxtimer::Timer t1(true);
628 0 : fqc.membersSig = CBLSSignature::AggregateSecure(aggSigs, aggPks, commitmentHash);
629 0 : t1.stop();
630 :
631 0 : cxxtimer::Timer t2(true);
632 0 : if (!fqc.quorumSig.Recover(thresholdSigs, signerIds)) {
633 0 : logger.Batch("failed to recover quorum sig");
634 0 : continue;
635 : }
636 0 : t2.stop();
637 :
638 0 : cxxtimer::Timer t3(true);
639 0 : 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 0 : t3.stop();
644 :
645 0 : finalCommitments.emplace_back(fqc);
646 :
647 0 : logger.Batch("final commitment: validMembers=%d, signers=%d, quorumPublicKey=%s, time1=%d, time2=%d, time3=%d",
648 0 : fqc.CountValidMembers(), fqc.CountSigners(), fqc.quorumPublicKey.ToString(),
649 0 : t1.count(), t2.count(), t3.count());
650 0 : }
651 :
652 0 : logger.Flush();
653 :
654 0 : return finalCommitments;
655 0 : }
656 :
657 0 : CFinalCommitment ActiveDKGSession::FinalizeSingleCommitment()
658 : {
659 0 : if (!AreWeMember()) {
660 0 : return {};
661 : }
662 :
663 0 : CDKGLogger logger(*this, __func__, __LINE__);
664 :
665 0 : std::vector<CBLSId> signerIds;
666 0 : std::vector<CBLSSignature> thresholdSigs;
667 :
668 0 : CFinalCommitment fqc(params, m_quorum_base_block_index->GetBlockHash());
669 :
670 :
671 0 : fqc.signers = {true};
672 0 : fqc.validMembers = {true};
673 :
674 0 : CBLSSecretKey sk1;
675 0 : sk1.MakeNewKey();
676 :
677 0 : fqc.quorumPublicKey = sk1.GetPublicKey();
678 0 : 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 0 : const bool workaround_qpublic_key = true;
683 : if (workaround_qpublic_key) {
684 0 : fqc.quorumPublicKey = m_mn_activeman.GetPubKey();
685 : }
686 0 : const bool isQuorumRotationEnabled{false};
687 0 : fqc.nVersion = CFinalCommitment::GetVersion(isQuorumRotationEnabled,
688 0 : DeploymentActiveAfter(m_quorum_base_block_index, m_chainman.GetConsensus(),
689 : Consensus::DEPLOYMENT_V19));
690 0 : fqc.quorumIndex = 0;
691 :
692 0 : uint256 commitmentHash = BuildCommitmentHash(fqc.llmqType, fqc.quorumHash, fqc.validMembers, fqc.quorumPublicKey,
693 0 : fqc.quorumVvecHash);
694 0 : fqc.quorumSig = sk1.Sign(commitmentHash, m_use_legacy_bls);
695 :
696 0 : fqc.membersSig = m_mn_activeman.Sign(commitmentHash, m_use_legacy_bls);
697 :
698 : if (workaround_qpublic_key) {
699 0 : fqc.quorumSig = fqc.membersSig;
700 : }
701 :
702 0 : 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 0 : logger.Batch("final commitment: validMembers=%d, signers=%d, quorumPublicKey=%s", fqc.CountValidMembers(),
708 0 : fqc.CountSigners(), fqc.quorumPublicKey.ToString());
709 :
710 0 : logger.Flush();
711 :
712 0 : return fqc;
713 0 : }
714 :
715 0 : bool ActiveDKGSession::MaybeDecrypt(const CBLSIESMultiRecipientObjects<CBLSSecretKey>& obj, size_t idx,
716 : CBLSSecretKey& ret_obj, int version)
717 : {
718 0 : return m_mn_activeman.Decrypt(obj, idx, ret_obj, version);
719 : }
720 : } // namespace llmq
|