Line data Source code
1 : // Copyright (c) 2018-2025 The Dash Core developers
2 : // Distributed under the MIT/X11 software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #include <llmq/signing_shares.h>
6 :
7 : #include <active/masternode.h>
8 : #include <chainparams.h>
9 : #include <evo/deterministicmns.h>
10 : #include <llmq/commitment.h>
11 : #include <llmq/options.h>
12 : #include <llmq/quorums.h>
13 : #include <llmq/quorumsman.h>
14 : #include <llmq/signhash.h>
15 : #include <llmq/signing.h>
16 : #include <netmessagemaker.h>
17 : #include <util/helpers.h>
18 : #include <util/std23.h>
19 : #include <util/thread.h>
20 : #include <util/time.h>
21 : #include <validation.h>
22 :
23 : #include <cxxtimer.hpp>
24 :
25 : #include <ranges>
26 :
27 : namespace llmq
28 : {
29 43386 : void CSigShare::UpdateKey()
30 : {
31 43386 : key.first = this->buildSignHash().Get();
32 43386 : key.second = quorumMember;
33 43386 : }
34 :
35 21816 : std::string CSigSesAnn::ToString() const
36 : {
37 21816 : return strprintf("sessionId=%d, llmqType=%d, quorumHash=%s, id=%s, msgHash=%s",
38 21816 : sessionId, std23::to_underlying(getLlmqType()), getQuorumHash().ToString(), getId().ToString(), getMsgHash().ToString());
39 0 : }
40 :
41 26966 : void CSigSharesInv::Merge(const CSigSharesInv& inv2)
42 : {
43 138580 : for (const auto i : util::irange(inv.size())) {
44 111614 : if (inv2.inv[i]) {
45 31638 : inv[i] = inv2.inv[i];
46 31638 : }
47 : }
48 26966 : }
49 :
50 15346 : size_t CSigSharesInv::CountSet() const
51 : {
52 15346 : return (size_t)std::count(inv.begin(), inv.end(), true);
53 : }
54 :
55 67115 : std::string CSigSharesInv::ToString() const
56 : {
57 67115 : std::string str = "(";
58 67115 : bool first = true;
59 15197227 : for (const auto i : util::irange(inv.size())) {
60 15129820 : if (!inv[i]) {
61 15058886 : continue;
62 : }
63 :
64 71226 : if (!first) {
65 4845 : str += ",";
66 4845 : }
67 71226 : first = false;
68 71226 : str += strprintf("%d", i);
69 : }
70 66380 : str += ")";
71 66381 : return str;
72 67849 : }
73 :
74 150693 : void CSigSharesInv::Init(size_t size)
75 : {
76 150693 : inv.resize(size, false);
77 150693 : }
78 :
79 41368 : void CSigSharesInv::Set(uint16_t quorumMember, bool v)
80 : {
81 41368 : assert(quorumMember < inv.size());
82 41368 : inv[quorumMember] = v;
83 41368 : }
84 :
85 97 : void CSigSharesInv::SetAll(bool v)
86 : {
87 97 : std::fill(inv.begin(), inv.end(), v);
88 97 : }
89 :
90 37547 : std::string CBatchedSigShares::ToInvString() const
91 : {
92 37547 : CSigSharesInv inv;
93 : // we use 400 here no matter what the real size is. We don't really care about that size as we just want to call ToString()
94 37547 : inv.Init(400);
95 75140 : for (const auto& sigShare : sigShares) {
96 37593 : inv.inv[sigShare.first] = true;
97 : }
98 37547 : return inv.ToString();
99 37547 : }
100 :
101 32599 : static void InitSession(CSigSharesNodeState::Session& s, const llmq::SignHash& signHash, CSigBase from)
102 : {
103 32599 : const auto& llmq_params_opt = Params().GetLLMQ(from.getLlmqType());
104 32599 : assert(llmq_params_opt.has_value());
105 32599 : const auto& llmq_params = llmq_params_opt.value();
106 :
107 32599 : s.llmqType = from.getLlmqType();
108 32599 : s.quorumHash = from.getQuorumHash();
109 32599 : s.id = from.getId();
110 32599 : s.msgHash = from.getMsgHash();
111 32599 : s.signHash = signHash;
112 32599 : s.announced.Init((size_t)llmq_params.size);
113 32599 : s.requested.Init((size_t)llmq_params.size);
114 32599 : s.knows.Init((size_t)llmq_params.size);
115 32599 : }
116 :
117 53057 : CSigSharesNodeState::Session& CSigSharesNodeState::GetOrCreateSessionFromShare(const llmq::CSigShare& sigShare)
118 : {
119 53057 : auto& s = sessions[sigShare.GetSignHash()];
120 53057 : if (s.announced.inv.empty()) {
121 21689 : InitSession(s, sigShare.buildSignHash(), sigShare);
122 21689 : }
123 53057 : return s;
124 : }
125 :
126 21816 : CSigSharesNodeState::Session& CSigSharesNodeState::GetOrCreateSessionFromAnn(const llmq::CSigSesAnn& ann)
127 : {
128 21816 : auto signHash = ann.buildSignHash();
129 21816 : auto& s = sessions[signHash.Get()];
130 21816 : if (s.announced.inv.empty()) {
131 10910 : InitSession(s, signHash, ann);
132 10910 : }
133 21816 : return s;
134 : }
135 :
136 36271 : CSigSharesNodeState::Session* CSigSharesNodeState::GetSessionBySignHash(const uint256& signHash)
137 : {
138 36271 : auto it = sessions.find(signHash);
139 36271 : if (it == sessions.end()) {
140 1904 : return nullptr;
141 : }
142 34367 : return &it->second;
143 36271 : }
144 :
145 47714 : CSigSharesNodeState::Session* CSigSharesNodeState::GetSessionByRecvId(uint32_t sessionId)
146 : {
147 47714 : auto it = sessionByRecvId.find(sessionId);
148 47714 : if (it == sessionByRecvId.end()) {
149 1284 : return nullptr;
150 : }
151 46430 : return it->second;
152 47714 : }
153 :
154 34226 : bool CSigSharesNodeState::GetSessionInfoByRecvId(uint32_t sessionId, SessionInfo& retInfo)
155 : {
156 34226 : const auto* s = GetSessionByRecvId(sessionId);
157 34226 : if (s == nullptr) {
158 1279 : return false;
159 : }
160 32947 : retInfo.llmqType = s->llmqType;
161 32947 : retInfo.quorumHash = s->quorumHash;
162 32947 : retInfo.id = s->id;
163 32947 : retInfo.msgHash = s->msgHash;
164 32947 : retInfo.signHash = s->signHash;
165 32947 : retInfo.quorum = s->quorum;
166 :
167 32947 : return true;
168 34226 : }
169 :
170 88724 : void CSigSharesNodeState::RemoveSession(const uint256& signHash)
171 : {
172 88724 : if (const auto it = sessions.find(signHash); it != sessions.end()) {
173 30923 : sessionByRecvId.erase(it->second.recvSessionId);
174 30923 : sessions.erase(it);
175 30923 : }
176 88724 : requestedSigShares.EraseAllForSignHash(signHash);
177 88724 : pendingIncomingSigShares.EraseAllForSignHash(signHash);
178 88724 : }
179 :
180 : //////////////////////
181 :
182 2640 : CSigSharesManager::CSigSharesManager(CConnman& connman, const ChainstateManager& chainman, CSigningManager& _sigman,
183 : const CActiveMasternodeManager& mn_activeman, const CQuorumManager& _qman,
184 : const CSporkManager& sporkman) :
185 660 : m_connman{connman},
186 660 : m_chainman{chainman},
187 660 : sigman{_sigman},
188 660 : m_mn_activeman{mn_activeman},
189 660 : qman{_qman},
190 660 : m_sporkman{sporkman}
191 1320 : {
192 660 : }
193 :
194 1980 : CSigSharesManager::~CSigSharesManager() = default;
195 :
196 660 : void CSigSharesManager::RegisterRecoveryInterface()
197 : {
198 660 : sigman.RegisterRecoveredSigsListener(this);
199 660 : }
200 :
201 660 : void CSigSharesManager::UnregisterRecoveryInterface()
202 : {
203 660 : sigman.UnregisterRecoveredSigsListener(this);
204 660 : }
205 :
206 21816 : bool CSigSharesManager::ProcessMessageSigSesAnn(const CNode& pfrom, const CSigSesAnn& ann)
207 : {
208 21816 : auto llmqType = ann.getLlmqType();
209 21816 : if (!Params().GetLLMQ(llmqType).has_value()) {
210 0 : return false;
211 : }
212 21816 : if (ann.getSessionId() == UNINITIALIZED_SESSION_ID || ann.getQuorumHash().IsNull() || ann.getId().IsNull() || ann.getMsgHash().IsNull()) {
213 0 : return false;
214 : }
215 :
216 21816 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- ann={%s}, node=%d\n", __func__, ann.ToString(), pfrom.GetId());
217 :
218 21816 : auto quorum = qman.GetQuorum(llmqType, ann.getQuorumHash());
219 21816 : if (!quorum) {
220 : // TODO should we ban here?
221 0 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- quorum %s not found, node=%d\n", __func__,
222 : ann.getQuorumHash().ToString(), pfrom.GetId());
223 0 : return true; // let's still try other announcements from the same message
224 : }
225 :
226 21816 : LOCK(cs);
227 21816 : auto& nodeState = nodeStates[pfrom.GetId()];
228 21816 : auto& session = nodeState.GetOrCreateSessionFromAnn(ann);
229 21816 : nodeState.sessionByRecvId.erase(session.recvSessionId);
230 21816 : nodeState.sessionByRecvId.erase(ann.getSessionId());
231 21816 : session.recvSessionId = ann.getSessionId();
232 21816 : session.quorum = quorum;
233 21816 : nodeState.sessionByRecvId.try_emplace(ann.getSessionId(), &session);
234 :
235 21816 : return true;
236 21816 : }
237 :
238 14226 : static bool VerifySigSharesInv(Consensus::LLMQType llmqType, const CSigSharesInv& inv)
239 : {
240 14226 : const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
241 14226 : return llmq_params_opt.has_value() && (inv.inv.size() == size_t(llmq_params_opt->size));
242 : }
243 :
244 15333 : bool CSigSharesManager::ProcessMessageSigShares(const CNode& pfrom, const CSigSharesInv& inv, const std::string& msg_type)
245 : {
246 15333 : CSigSharesNodeState::SessionInfo sessionInfo;
247 15333 : if (!GetSessionInfoByRecvId(pfrom.GetId(), inv.sessionId, sessionInfo)) {
248 1107 : return true;
249 : }
250 :
251 14226 : if (!VerifySigSharesInv(sessionInfo.llmqType, inv)) {
252 0 : return false;
253 : }
254 :
255 : // TODO for PoSe, we should consider propagating shares even if we already have a recovered sig
256 14226 : if (sigman.HasRecoveredSigForSession(sessionInfo.signHash.Get())) {
257 738 : return true;
258 : }
259 :
260 13488 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- signHash=%s, inv={%s}, node=%d\n", __func__,
261 : sessionInfo.signHash.ToString(), inv.ToString(), pfrom.GetId());
262 :
263 13488 : if (msg_type == NetMsgType::QSIGSHARESINV && !sessionInfo.quorum->HasVerificationVector()) {
264 : // TODO we should allow to ask other nodes for the quorum vvec if we missed it in the DKG
265 0 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- we don't have the quorum vvec for %s, not requesting sig shares. node=%d\n", __func__,
266 : sessionInfo.quorumHash.ToString(), pfrom.GetId());
267 0 : return true;
268 : }
269 :
270 13488 : LOCK(cs);
271 13488 : auto& nodeState = nodeStates[pfrom.GetId()];
272 13488 : auto* session = nodeState.GetSessionByRecvId(inv.sessionId);
273 13488 : if (session == nullptr) {
274 5 : return true;
275 : }
276 13483 : if (msg_type == NetMsgType::QSIGSHARESINV) {
277 10688 : session->announced.Merge(inv);
278 10688 : } else { // msg_type == NetMsgType::QGETSIGSHARES
279 2795 : session->requested.Merge(inv);
280 : }
281 :
282 13483 : session->knows.Merge(inv);
283 13483 : return true;
284 15333 : }
285 :
286 : // Failure is not issue, we should not ban node
287 25100 : static bool PreVerifySigShareQuorum(const CActiveMasternodeManager& mn_activeman, const CQuorumManager& quorum_manager,
288 : const CQuorumCPtr& quorum, Consensus::LLMQType llmqType)
289 : {
290 25100 : if (!IsQuorumActive(llmqType, quorum_manager, quorum->qc->quorumHash)) {
291 : // quorum is too old
292 0 : return false;
293 : }
294 25100 : if (!quorum->IsMember(mn_activeman.GetProTxHash())) {
295 : // we're not a member so we can't verify it (we actually shouldn't have received it)
296 179 : return false;
297 : }
298 24921 : if (!quorum->HasVerificationVector()) {
299 : // TODO we should allow to ask other nodes for the quorum vvec if we missed it in the DKG
300 131 : LogPrint(BCLog::LLMQ_SIGS, "%s -- we don't have the quorum vvec for %s, no verification possible.\n", __func__,
301 : quorum->qc->quorumHash.ToString());
302 131 : return false;
303 : }
304 24790 : return true;
305 25100 : }
306 :
307 : // Ban node if PreVerifyBatchedSigShares failed
308 18631 : bool PreVerifyBatchedSigShares(const CSigSharesNodeState::SessionInfo& session, const CBatchedSigShares& batchedSigShares)
309 : {
310 18631 : std::unordered_set<uint16_t> dupMembers;
311 :
312 55935 : for (const auto& [quorumMember, _] : batchedSigShares.sigShares) {
313 18652 : if (!dupMembers.emplace(quorumMember).second) {
314 0 : return false;
315 : }
316 :
317 18652 : if (quorumMember >= session.quorum->members.size()) {
318 0 : LogPrint(BCLog::LLMQ_SIGS, "%s -- quorumMember out of bounds\n", __func__);
319 0 : return false;
320 : }
321 37304 : if (!session.quorum->qc->validMembers[quorumMember]) {
322 0 : LogPrint(BCLog::LLMQ_SIGS, "%s -- quorumMember not valid\n", __func__);
323 0 : return false;
324 : }
325 : }
326 18631 : return true;
327 18631 : }
328 :
329 18893 : bool CSigSharesManager::ProcessMessageBatchedSigShares(const CNode& pfrom, const CBatchedSigShares& batchedSigShares)
330 : {
331 18893 : CSigSharesNodeState::SessionInfo sessionInfo;
332 18893 : if (!GetSessionInfoByRecvId(pfrom.GetId(), batchedSigShares.sessionId, sessionInfo)) {
333 172 : return true;
334 : }
335 :
336 18721 : if (!PreVerifySigShareQuorum(m_mn_activeman, qman, sessionInfo.quorum, sessionInfo.llmqType)) {
337 90 : return true;
338 : }
339 :
340 18631 : if (!PreVerifyBatchedSigShares(sessionInfo, batchedSigShares)) {
341 0 : return false; // ban node
342 : }
343 :
344 18631 : std::vector<CSigShare> sigSharesToProcess;
345 18631 : sigSharesToProcess.reserve(batchedSigShares.sigShares.size());
346 :
347 : {
348 18631 : LOCK(cs);
349 18631 : auto& nodeState = nodeStates[pfrom.GetId()];
350 :
351 37283 : for (const auto& sigSharetmp : batchedSigShares.sigShares) {
352 18652 : CSigShare sigShare = RebuildSigShare(sessionInfo, sigSharetmp);
353 18652 : nodeState.requestedSigShares.Erase(sigShare.GetKey());
354 :
355 : // TODO track invalid sig shares received for PoSe?
356 : // It's important to only skip seen *valid* sig shares here. If a node sends us a
357 : // batch of mostly valid sig shares with a single invalid one and thus batched
358 : // verification fails, we'd skip the valid ones in the future if received from other nodes
359 18652 : if (sigShares.Has(sigShare.GetKey())) {
360 14 : continue;
361 : }
362 :
363 : // TODO for PoSe, we should consider propagating shares even if we already have a recovered sig
364 18638 : if (sigman.HasRecoveredSigForId(sigShare.getLlmqType(), sigShare.getId())) {
365 396 : continue;
366 : }
367 :
368 18242 : sigSharesToProcess.emplace_back(sigShare);
369 18652 : }
370 18631 : }
371 :
372 18631 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- signHash=%s, shares=%d, new=%d, inv={%s}, node=%d\n", __func__,
373 : sessionInfo.signHash.ToString(), batchedSigShares.sigShares.size(), sigSharesToProcess.size(), batchedSigShares.ToInvString(), pfrom.GetId());
374 :
375 18631 : if (sigSharesToProcess.empty()) {
376 405 : return true;
377 : }
378 :
379 18226 : LOCK(cs);
380 18226 : auto& nodeState = nodeStates[pfrom.GetId()];
381 36468 : for (const auto& s : sigSharesToProcess) {
382 18242 : nodeState.pendingIncomingSigShares.Add(s.GetKey(), s);
383 : }
384 18226 : return true;
385 18893 : }
386 :
387 6379 : bool CSigSharesManager::ProcessMessageSigShare(NodeId fromId, const CSigShare& sigShare)
388 : {
389 6379 : auto quorum = qman.GetQuorum(sigShare.getLlmqType(), sigShare.getQuorumHash());
390 6379 : if (!quorum) {
391 0 : return true;
392 : }
393 6379 : if (!PreVerifySigShareQuorum(m_mn_activeman, qman, quorum, sigShare.getLlmqType())) {
394 220 : return true;
395 : }
396 :
397 6159 : if (sigShare.getQuorumMember() >= quorum->members.size()) {
398 0 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- quorumMember out of bounds\n", __func__);
399 0 : return false;
400 : }
401 6159 : if (!quorum->qc->validMembers[sigShare.getQuorumMember()]) {
402 0 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- quorumMember not valid\n", __func__);
403 0 : return false;
404 : }
405 :
406 6159 : const auto signHash = sigShare.GetSignHash();
407 11559 : const bool alreadyRecovered = sigman.HasRecoveredSigForId(sigShare.getLlmqType(), sigShare.getId()) ||
408 5400 : sigman.HasRecoveredSigForSession(signHash);
409 :
410 : {
411 6159 : LOCK(cs);
412 :
413 6159 : if (alreadyRecovered) {
414 759 : LogPrint(BCLog::LLMQ_SIGS, /* Continued */
415 : "CSigSharesManager::%s -- dropping sigShare for recovered session. signHash=%s, id=%s, "
416 : "msgHash=%s, member=%d, node=%d\n",
417 : __func__, signHash.ToString(), sigShare.getId().ToString(), sigShare.getMsgHash().ToString(),
418 : sigShare.getQuorumMember(), fromId);
419 759 : return true;
420 : }
421 :
422 5400 : if (sigShares.Has(sigShare.GetKey())) {
423 0 : return true;
424 : }
425 :
426 5400 : auto& nodeState = nodeStates[fromId];
427 5400 : nodeState.pendingIncomingSigShares.Add(sigShare.GetKey(), sigShare);
428 6159 : }
429 :
430 5400 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- signHash=%s, id=%s, msgHash=%s, member=%d, node=%d\n", __func__,
431 : signHash.ToString(), sigShare.getId().ToString(), sigShare.getMsgHash().ToString(), sigShare.getQuorumMember(), fromId);
432 5400 : return true;
433 6379 : }
434 :
435 708919 : bool CSigSharesManager::CollectPendingSigSharesToVerify(
436 : size_t maxUniqueSessions, std::unordered_map<NodeId, std::vector<CSigShare>>& retSigShares,
437 : std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& retQuorums)
438 : {
439 708919 : bool more_work{false};
440 :
441 : {
442 708919 : LOCK(cs);
443 708919 : if (nodeStates.empty()) {
444 416847 : return false;
445 : }
446 :
447 : // This will iterate node states in random order and pick one sig share at a time. This avoids processing
448 : // of large batches at once from the same node while other nodes also provided shares. If we wouldn't do this,
449 : // other nodes would be able to poison us with a large batch with N-1 valid shares and the last one being
450 : // invalid, making batch verification fail and revert to per-share verification, which in turn would slow down
451 : // the whole verification process
452 292072 : std::unordered_set<std::pair<NodeId, uint256>, StaticSaltedHasher> uniqueSignHashes;
453 292072 : IterateNodesRandom(
454 292072 : nodeStates,
455 1214592 : [&]() {
456 922520 : return uniqueSignHashes.size() < maxUniqueSessions;
457 : // TODO: remove NO_THREAD_SAFETY_ANALYSIS
458 : // using here template IterateNodesRandom makes impossible to use lock annotation
459 : },
460 1214584 : [&](NodeId nodeId, CSigSharesNodeState& ns) NO_THREAD_SAFETY_ANALYSIS {
461 922512 : if (ns.pendingIncomingSigShares.Empty()) {
462 899672 : return false;
463 : }
464 22840 : const auto& sigShare = *ns.pendingIncomingSigShares.GetFirst();
465 :
466 22840 : AssertLockHeld(cs);
467 22840 : if (const bool alreadyHave = this->sigShares.Has(sigShare.GetKey()); !alreadyHave) {
468 22840 : uniqueSignHashes.emplace(nodeId, sigShare.GetSignHash());
469 22840 : retSigShares[nodeId].emplace_back(sigShare);
470 22840 : }
471 22840 : ns.pendingIncomingSigShares.Erase(sigShare.GetKey());
472 22840 : return !ns.pendingIncomingSigShares.Empty();
473 922512 : },
474 292072 : rnd);
475 :
476 292072 : if (retSigShares.empty()) {
477 279890 : return false;
478 : }
479 :
480 : // Determine if there is still work left in any node state after pulling this batch
481 12182 : more_work = std::any_of(nodeStates.begin(), nodeStates.end(),
482 41825 : [](const auto& entry) {
483 41825 : const auto& ns = entry.second;
484 41825 : return !ns.pendingIncomingSigShares.Empty();
485 : });
486 708919 : }
487 :
488 : // For the convenience of the caller, also build a map of quorumHash -> quorum
489 :
490 27572 : for (const auto& [_, vecSigShares] : retSigShares) {
491 38230 : for (const auto& sigShare : vecSigShares) {
492 22840 : auto llmqType = sigShare.getLlmqType();
493 :
494 22840 : auto k = std::make_pair(llmqType, sigShare.getQuorumHash());
495 22840 : if (retQuorums.count(k) != 0) {
496 9571 : continue;
497 : }
498 :
499 13269 : auto quorum = qman.GetQuorum(llmqType, sigShare.getQuorumHash());
500 : // Despite constructing a convenience map, we assume that the quorum *must* be present.
501 : // The absence of it might indicate an inconsistent internal state, so we should report
502 : // nothing instead of reporting flawed data.
503 13269 : if (!quorum) {
504 0 : LogPrintf("%s: ERROR! Unexpected missing quorum with llmqType=%d, quorumHash=%s\n", __func__,
505 : std23::to_underlying(llmqType), sigShare.getQuorumHash().ToString());
506 0 : return false;
507 : }
508 13269 : retQuorums.try_emplace(k, quorum);
509 13269 : }
510 : }
511 :
512 12182 : return more_work;
513 708919 : }
514 :
515 : // It's ensured that no duplicates are passed to this method
516 15386 : std::vector<std::shared_ptr<CRecoveredSig>> CSigSharesManager::ProcessPendingSigShares(
517 : const std::vector<CSigShare>& sigSharesToProcess,
518 : const std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& quorums)
519 : {
520 15386 : cxxtimer::Timer t(true);
521 15386 : std::vector<std::shared_ptr<CRecoveredSig>> recovered_sigs;
522 38222 : for (const auto& sigShare : sigSharesToProcess) {
523 22836 : auto quorumKey = std::make_pair(sigShare.getLlmqType(), sigShare.getQuorumHash());
524 22836 : auto rs = ProcessSigShare(sigShare, quorums.at(quorumKey));
525 22836 : if (rs != nullptr) {
526 8464 : recovered_sigs.emplace_back(std::move(rs));
527 8464 : }
528 22836 : }
529 15386 : t.stop();
530 :
531 15386 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- processed sigShare batch. shares=%d, time=%ds\n", __func__,
532 : sigSharesToProcess.size(), t.count());
533 15386 : return recovered_sigs;
534 15386 : }
535 :
536 : // sig shares are already verified when entering this method
537 41194 : std::shared_ptr<CRecoveredSig> CSigSharesManager::ProcessSigShare(const CSigShare& sigShare, const CQuorumCPtr& quorum)
538 : {
539 41194 : auto llmqType = quorum->params.type;
540 :
541 41194 : const bool isAllMembersConnectedEnabled = IsAllMembersConnectedEnabled(llmqType, m_sporkman);
542 :
543 : // prepare node set for direct-push in case this is our sig share
544 41194 : std::vector<NodeId> quorumNodes;
545 70084 : if (!isAllMembersConnectedEnabled &&
546 28890 : sigShare.getQuorumMember() == quorum->GetMemberIndex(m_mn_activeman.GetProTxHash())) {
547 11279 : quorumNodes = m_connman.GetMasternodeQuorumNodes(sigShare.getLlmqType(), sigShare.getQuorumHash());
548 11279 : }
549 :
550 41194 : if (sigman.HasRecoveredSigForId(llmqType, sigShare.getId())) {
551 1483 : return nullptr;
552 : }
553 :
554 39711 : bool canTryRecovery = false;
555 : {
556 39711 : LOCK(cs);
557 :
558 39711 : if (!sigShares.Add(sigShare.GetKey(), sigShare)) {
559 44 : return nullptr;
560 : }
561 39667 : if (!isAllMembersConnectedEnabled) {
562 27974 : sigSharesQueuedToAnnounce.Add(sigShare.GetKey(), true);
563 27974 : }
564 :
565 : // Update the time we've seen the last sigShare
566 39667 : timeSeenForSessions[sigShare.GetSignHash()] = GetTime<std::chrono::seconds>().count();
567 :
568 : // don't announce and wait for other nodes to request this share and directly send it to them
569 : // there is no way the other nodes know about this share as this is the one created on this node
570 60351 : for (auto otherNodeId : quorumNodes) {
571 20684 : auto& nodeState = nodeStates[otherNodeId];
572 20684 : auto& session = nodeState.GetOrCreateSessionFromShare(sigShare);
573 20684 : session.quorum = quorum;
574 20684 : session.requested.Set(sigShare.getQuorumMember(), true);
575 20684 : session.knows.Set(sigShare.getQuorumMember(), true);
576 : }
577 :
578 39667 : size_t sigShareCount = sigShares.CountForSignHash(sigShare.GetSignHash());
579 39667 : if (sigShareCount >= size_t(quorum->params.threshold)) {
580 8782 : canTryRecovery = true;
581 8782 : }
582 39711 : }
583 39667 : if (!canTryRecovery) return nullptr;
584 :
585 8782 : return TryRecoverSig(*quorum, sigShare.getId(), sigShare.getMsgHash());
586 41196 : }
587 :
588 8782 : std::shared_ptr<CRecoveredSig> CSigSharesManager::TryRecoverSig(const CQuorum& quorum, const uint256& id,
589 : const uint256& msgHash)
590 : {
591 8782 : if (sigman.HasRecoveredSigForId(quorum.params.type, id)) {
592 0 : return nullptr;
593 : }
594 :
595 8782 : std::vector<CBLSSignature> sigSharesForRecovery;
596 8782 : std::vector<CBLSId> idsForRecovery;
597 : {
598 8782 : LOCK(cs);
599 :
600 8782 : auto signHash = SignHash(quorum.params.type, quorum.qc->quorumHash, id, msgHash).Get();
601 8782 : const auto* sigSharesForSignHash = sigShares.GetAllForSignHash(signHash);
602 8782 : if (sigSharesForSignHash == nullptr) {
603 0 : return nullptr;
604 : }
605 :
606 8782 : std::shared_ptr<CRecoveredSig> singleMemberRecoveredSig;
607 8782 : if (quorum.params.is_single_member()) {
608 241 : if (sigSharesForSignHash->empty()) {
609 0 : LogPrint(BCLog::LLMQ_SIGS, /* Continued */
610 : "CSigSharesManager::%s -- impossible to recover single-node signature - no shares yet. id=%s, "
611 : "msgHash=%s\n",
612 : __func__, id.ToString(), msgHash.ToString());
613 0 : return nullptr;
614 : }
615 241 : const auto& sigShare = sigSharesForSignHash->begin()->second;
616 241 : CBLSSignature recoveredSig = sigShare.sigShare.Get();
617 241 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- recover single-node signature. id=%s, msgHash=%s\n",
618 : __func__, id.ToString(), msgHash.ToString());
619 :
620 241 : singleMemberRecoveredSig = std::make_shared<CRecoveredSig>(quorum.params.type, quorum.qc->quorumHash, id, msgHash,
621 : recoveredSig);
622 241 : }
623 :
624 8782 : sigSharesForRecovery.reserve((size_t) quorum.params.threshold);
625 8782 : idsForRecovery.reserve((size_t) quorum.params.threshold);
626 31438 : for (auto it = sigSharesForSignHash->begin(); it != sigSharesForSignHash->end() && sigSharesForRecovery.size() < size_t(quorum.params.threshold); ++it) {
627 22656 : const auto& sigShare = it->second;
628 22656 : sigSharesForRecovery.emplace_back(sigShare.sigShare.Get());
629 22656 : idsForRecovery.emplace_back(quorum.members[sigShare.getQuorumMember()]->proTxHash);
630 22656 : }
631 :
632 : // check if we can recover the final signature
633 8782 : if (sigSharesForRecovery.size() < size_t(quorum.params.threshold)) {
634 0 : return nullptr;
635 : }
636 8782 : if (quorum.params.is_single_member()) {
637 241 : return singleMemberRecoveredSig; // end of single-quorum processing
638 : }
639 8782 : }
640 :
641 : // now recover it
642 8541 : cxxtimer::Timer t(true);
643 8541 : CBLSSignature recoveredSig;
644 8541 : if (!recoveredSig.Recover(sigSharesForRecovery, idsForRecovery)) {
645 5 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- failed to recover signature. id=%s, msgHash=%s, time=%d\n", __func__,
646 : id.ToString(), msgHash.ToString(), t.count());
647 5 : return nullptr;
648 : }
649 :
650 8536 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- recovered signature. id=%s, msgHash=%s, time=%d\n", __func__,
651 : id.ToString(), msgHash.ToString(), t.count());
652 :
653 8536 : auto rs = std::make_shared<CRecoveredSig>(quorum.params.type, quorum.qc->quorumHash, id, msgHash, recoveredSig);
654 :
655 : // There should actually be no need to verify the self-recovered signatures as it should always succeed. Let's
656 : // however still verify it from time to time, so that we have a chance to catch bugs. We do only this sporadic
657 : // verification because this is unbatched and thus slow verification that happens here.
658 8536 : if (((recoveredSigsCounter++) % 100) == 0) {
659 394 : auto signHash = rs->buildSignHash();
660 394 : bool valid = recoveredSig.VerifyInsecure(quorum.qc->quorumPublicKey, signHash.Get());
661 394 : if (!valid) {
662 : // this should really not happen as we have verified all signature shares before
663 0 : LogPrintf("CSigSharesManager::%s -- own recovered signature is invalid. id=%s, msgHash=%s\n", __func__,
664 : id.ToString(), msgHash.ToString());
665 0 : return nullptr;
666 : }
667 394 : }
668 8536 : return rs;
669 8782 : }
670 :
671 8726 : CDeterministicMNCPtr CSigSharesManager::SelectMemberForRecovery(const CQuorum& quorum, const uint256 &id, int attempt)
672 : {
673 8726 : assert(attempt < quorum.params.recoveryMembers);
674 :
675 8726 : std::vector<std::pair<uint256, CDeterministicMNCPtr>> v;
676 8726 : v.reserve(quorum.members.size());
677 47831 : for (const auto& dmn : quorum.members) {
678 39105 : auto h = ::SerializeHash(std::make_pair(dmn->proTxHash, id));
679 39105 : v.emplace_back(h, dmn);
680 : }
681 8726 : std::sort(v.begin(), v.end());
682 :
683 8726 : return v[attempt % v.size()].second;
684 8726 : }
685 :
686 49103 : bool CSigSharesManager::AsyncSignIfMember(Consensus::LLMQType llmqType, CSigningManager& sigman, const uint256& id,
687 : const uint256& msgHash, const uint256& quorumHash, bool allowReSign,
688 : bool allowDiffMsgHashSigning)
689 : {
690 49103 : AssertLockNotHeld(cs_pendingSigns);
691 :
692 49103 : if (m_mn_activeman.GetProTxHash().IsNull()) return false;
693 :
694 95508 : auto quorum = [&]() {
695 47754 : if (quorumHash.IsNull()) {
696 : // This might end up giving different results on different members
697 : // This might happen when we are on the brink of confirming a new quorum
698 : // This gives a slight risk of not getting enough shares to recover a signature
699 : // But at least it shouldn't be possible to get conflicting recovered signatures
700 : // TODO fix this by re-signing when the next block arrives, but only when that block results in a change of
701 : // the quorum list and no recovered signature has been created in the mean time
702 43409 : const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
703 43409 : assert(llmq_params_opt.has_value());
704 43409 : return SelectQuorumForSigning(llmq_params_opt.value(), m_chainman.ActiveChain(), qman, id);
705 : } else {
706 4345 : return qman.GetQuorum(llmqType, quorumHash);
707 : }
708 47754 : }();
709 :
710 47754 : if (!quorum) {
711 14020 : LogPrint(BCLog::LLMQ, "CSigningManager::%s -- failed to select quorum. id=%s, msgHash=%s\n", __func__,
712 : id.ToString(), msgHash.ToString());
713 14020 : return false;
714 : }
715 :
716 33734 : if (!quorum->IsValidMember(m_mn_activeman.GetProTxHash())) {
717 14250 : return false;
718 : }
719 :
720 : {
721 19484 : auto& db = sigman.GetDb();
722 19484 : bool hasVoted = db.HasVotedOnId(llmqType, id);
723 19484 : if (hasVoted) {
724 1129 : uint256 prevMsgHash;
725 1129 : db.GetVoteForId(llmqType, id, prevMsgHash);
726 1129 : if (msgHash != prevMsgHash) {
727 16 : if (allowDiffMsgHashSigning) {
728 8 : LogPrintf("%s -- already voted for id=%s and msgHash=%s. Signing for different " /* Continued */
729 : "msgHash=%s\n",
730 : __func__, id.ToString(), prevMsgHash.ToString(), msgHash.ToString());
731 8 : hasVoted = false;
732 8 : } else {
733 8 : LogPrintf("%s -- already voted for id=%s and msgHash=%s. Not voting on " /* Continued */
734 : "conflicting msgHash=%s\n",
735 : __func__, id.ToString(), prevMsgHash.ToString(), msgHash.ToString());
736 8 : return false;
737 : }
738 1121 : } else if (allowReSign) {
739 97 : LogPrint(BCLog::LLMQ, "%s -- already voted for id=%s and msgHash=%s. Resigning!\n", __func__,
740 : id.ToString(), prevMsgHash.ToString());
741 97 : } else {
742 1016 : LogPrint(BCLog::LLMQ, "%s -- already voted for id=%s and msgHash=%s. Not voting again.\n", __func__,
743 : id.ToString(), prevMsgHash.ToString());
744 1016 : return false;
745 : }
746 105 : }
747 :
748 18460 : if (db.HasRecoveredSigForId(llmqType, id)) {
749 : // no need to sign it if we already have a recovered sig
750 41 : return true;
751 : }
752 18419 : if (!hasVoted) {
753 18322 : db.WriteVoteForId(llmqType, id, msgHash);
754 18322 : }
755 : }
756 :
757 18419 : if (allowReSign) {
758 : // make us re-announce all known shares (other nodes might have run into a timeout)
759 887 : ForceReAnnouncement(*quorum, llmqType, id, msgHash);
760 887 : }
761 18419 : AsyncSign(std::move(quorum), id, msgHash);
762 :
763 18419 : return true;
764 49103 : }
765 :
766 216234 : void CSigSharesManager::CollectSigSharesToRequest(std::unordered_map<NodeId, Uint256HashMap<CSigSharesInv>>& sigSharesToRequest)
767 : {
768 216234 : AssertLockHeld(cs);
769 :
770 216234 : int64_t now = GetTime<std::chrono::seconds>().count();
771 216234 : const size_t maxRequestsForNode = 32;
772 :
773 : // avoid requesting from same nodes all the time
774 216234 : std::vector<NodeId> shuffledNodeIds;
775 216234 : shuffledNodeIds.reserve(nodeStates.size());
776 493393 : for (const auto& [nodeId, nodeState] : nodeStates) {
777 277159 : if (nodeState.sessions.empty()) {
778 171415 : continue;
779 : }
780 105744 : shuffledNodeIds.emplace_back(nodeId);
781 : }
782 216234 : Shuffle(shuffledNodeIds.begin(), shuffledNodeIds.end(), rnd);
783 :
784 321978 : for (const auto& nodeId : shuffledNodeIds) {
785 105744 : auto& nodeState = nodeStates[nodeId];
786 :
787 105744 : if (nodeState.banned) {
788 47 : continue;
789 : }
790 :
791 107488 : nodeState.requestedSigShares.EraseIf([&now, &nodeId](const SigShareKey& k, int64_t t) {
792 1791 : if (now - t >= SIG_SHARE_REQUEST_TIMEOUT) {
793 : // timeout while waiting for this one, so retry it with another node
794 25 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::CollectSigSharesToRequest -- timeout while waiting for %s-%d, node=%d\n",
795 : k.first.ToString(), k.second, nodeId);
796 25 : return true;
797 : }
798 1766 : return false;
799 1791 : });
800 :
801 105697 : decltype(sigSharesToRequest.begin()->second)* invMap = nullptr;
802 :
803 5035985 : for (auto& [signHash, session] : nodeState.sessions) {
804 800939 : if (IsAllMembersConnectedEnabled(session.llmqType, m_sporkman)) {
805 2690 : continue;
806 : }
807 :
808 1596498 : if (sigman.HasRecoveredSigForSession(signHash)) {
809 127499 : continue;
810 : }
811 :
812 3303545 : for (const auto i : util::irange(session.announced.inv.size())) {
813 2632795 : if (!session.announced.inv[i]) {
814 2620935 : continue;
815 : }
816 11860 : auto k = std::make_pair(signHash, (uint16_t) i);
817 11860 : if (sigShares.Has(k)) {
818 : // we already have it
819 6179 : session.announced.inv[i] = false;
820 6179 : continue;
821 : }
822 5681 : if (nodeState.requestedSigShares.Size() >= maxRequestsForNode) {
823 : // too many pending requests for this node
824 0 : break;
825 : }
826 5681 : if (auto *const p = sigSharesRequested.Get(k)) {
827 2498 : if (now - p->second >= SIG_SHARE_REQUEST_TIMEOUT && nodeId != p->first) {
828 : // other node timed out, re-request from this node
829 2 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- other node timeout while waiting for %s-%d, re-request from=%d, node=%d\n", __func__,
830 : k.first.ToString(), k.second, nodeId, p->first);
831 2 : } else {
832 2496 : continue;
833 : }
834 2 : }
835 : // if we got this far we should do a request
836 :
837 : // track when we initiated the request so that we can detect timeouts
838 3185 : nodeState.requestedSigShares.Add(k, now);
839 :
840 : // don't request it from other nodes until a timeout happens
841 3185 : auto& r = sigSharesRequested.GetOrAdd(k);
842 3185 : r.first = nodeId;
843 3185 : r.second = now;
844 :
845 3185 : if (invMap == nullptr) {
846 1987 : invMap = &sigSharesToRequest[nodeId];
847 1987 : }
848 6370 : auto& inv = (*invMap)[signHash];
849 3185 : if (inv.inv.empty()) {
850 3146 : const auto& llmq_params_opt = Params().GetLLMQ(session.llmqType);
851 3146 : assert(llmq_params_opt.has_value());
852 3146 : inv.Init(llmq_params_opt->size);
853 3146 : }
854 3185 : inv.inv[k.second] = true;
855 :
856 : // don't request it again from this node
857 3185 : session.announced.inv[i] = false;
858 : }
859 : }
860 : }
861 216234 : }
862 :
863 216234 : void CSigSharesManager::CollectSigSharesToSend(std::unordered_map<NodeId, Uint256HashMap<CBatchedSigShares>>& sigSharesToSend)
864 : {
865 216234 : AssertLockHeld(cs);
866 :
867 504859 : for (auto& [nodeId, nodeState] : nodeStates) {
868 277159 : if (nodeState.banned) {
869 47 : continue;
870 : }
871 :
872 277112 : decltype(sigSharesToSend.begin()->second)* sigSharesToSend2 = nullptr;
873 :
874 3748715 : for (auto& [signHash, session] : nodeState.sessions) {
875 800939 : if (IsAllMembersConnectedEnabled(session.llmqType, m_sporkman)) {
876 2690 : continue;
877 : }
878 :
879 1596498 : if (sigman.HasRecoveredSigForSession(signHash)) {
880 127504 : continue;
881 : }
882 :
883 670745 : CBatchedSigShares batchedSigShares;
884 :
885 3303522 : for (const auto i : util::irange(session.requested.inv.size())) {
886 2632777 : if (!session.requested.inv[i]) {
887 2613821 : continue;
888 : }
889 18956 : session.requested.inv[i] = false;
890 :
891 18956 : auto k = std::make_pair(signHash, (uint16_t)i);
892 18956 : const CSigShare* sigShare = sigShares.Get(k);
893 18956 : if (sigShare == nullptr) {
894 : // he requested something we don't have
895 10 : session.requested.inv[i] = false;
896 10 : continue;
897 : }
898 :
899 18946 : batchedSigShares.sigShares.emplace_back((uint16_t)i, sigShare->sigShare);
900 : }
901 :
902 670745 : if (!batchedSigShares.sigShares.empty()) {
903 18921 : if (sigSharesToSend2 == nullptr) {
904 : // only create the map if we actually add a batched sig
905 22932 : sigSharesToSend2 = &sigSharesToSend[nodeId];
906 11466 : }
907 37842 : sigSharesToSend2->try_emplace(signHash, std::move(batchedSigShares));
908 18921 : }
909 670745 : }
910 : }
911 216234 : }
912 :
913 216234 : void CSigSharesManager::CollectSigSharesToSendConcentrated(std::unordered_map<NodeId, std::vector<CSigShare>>& sigSharesToSend, const std::vector<CNode*>& vNodes)
914 : {
915 216234 : AssertLockHeld(cs);
916 :
917 216234 : Uint256HashMap<CNode*> proTxToNode;
918 960566 : for (const auto& pnode : vNodes) {
919 744332 : auto verifiedProRegTxHash = pnode->GetVerifiedProRegTxHash();
920 744332 : if (verifiedProRegTxHash.IsNull()) {
921 200801 : continue;
922 : }
923 543531 : proTxToNode.try_emplace(verifiedProRegTxHash, pnode);
924 : }
925 :
926 216234 : auto curTime = GetTime<std::chrono::milliseconds>().count();
927 :
928 457204 : for (auto& [_, signedSession] : signedSessions) {
929 83407 : if (!IsAllMembersConnectedEnabled(signedSession.quorum->params.type, m_sporkman)) {
930 0 : continue;
931 : }
932 :
933 166814 : if (signedSession.attempt >= signedSession.quorum->params.recoveryMembers) {
934 43352 : continue;
935 : }
936 :
937 80110 : if (curTime >= signedSession.nextAttemptTime) {
938 8132 : int64_t waitTime = exp2(signedSession.attempt) * EXP_SEND_FOR_RECOVERY_TIMEOUT;
939 8132 : waitTime = std::min(MAX_SEND_FOR_RECOVERY_TIMEOUT, waitTime);
940 16264 : signedSession.nextAttemptTime = curTime + waitTime;
941 16264 : auto dmn = SelectMemberForRecovery(*signedSession.quorum, signedSession.sigShare.getId(), signedSession.attempt);
942 8132 : signedSession.attempt++;
943 :
944 8132 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- signHash=%s, sending to %s, attempt=%d\n", __func__,
945 : signedSession.sigShare.GetSignHash().ToString(), dmn->proTxHash.ToString(), signedSession.attempt);
946 :
947 8132 : auto it = proTxToNode.find(dmn->proTxHash);
948 8132 : if (it == proTxToNode.end()) {
949 1724 : continue;
950 : }
951 :
952 6408 : auto& m = sigSharesToSend[it->second->GetId()];
953 12816 : m.emplace_back(signedSession.sigShare);
954 8132 : }
955 : }
956 216234 : }
957 :
958 216234 : void CSigSharesManager::CollectSigSharesToAnnounce(std::unordered_map<NodeId, Uint256HashMap<CSigSharesInv>>& sigSharesToAnnounce)
959 : {
960 216234 : AssertLockHeld(cs);
961 :
962 216234 : std::unordered_map<std::pair<Consensus::LLMQType, uint256>, std::unordered_set<NodeId>, StaticSaltedHasher> quorumNodesMap;
963 :
964 : // TODO: remove NO_THREAD_SAFETY_ANALYSIS
965 : // using here template ForEach makes impossible to use lock annotation
966 233478 : sigSharesQueuedToAnnounce.ForEach([this, &quorumNodesMap, &sigSharesToAnnounce](const SigShareKey& sigShareKey,
967 : bool) NO_THREAD_SAFETY_ANALYSIS {
968 17244 : AssertLockHeld(cs);
969 17244 : const auto& signHash = sigShareKey.first;
970 17244 : auto quorumMember = sigShareKey.second;
971 17244 : const CSigShare* sigShare = sigShares.Get(sigShareKey);
972 17244 : if (sigShare == nullptr) {
973 0 : return;
974 : }
975 :
976 : // announce to the nodes which we know through the intra-quorum-communication system
977 17244 : auto quorumKey = std::make_pair(sigShare->getLlmqType(), sigShare->getQuorumHash());
978 17244 : auto it = quorumNodesMap.find(quorumKey);
979 17244 : if (it == quorumNodesMap.end()) {
980 8513 : auto nodeIds = m_connman.GetMasternodeQuorumNodes(quorumKey.first, quorumKey.second);
981 8513 : it = quorumNodesMap.emplace(std::piecewise_construct, std::forward_as_tuple(quorumKey), std::forward_as_tuple(nodeIds.begin(), nodeIds.end())).first;
982 8513 : }
983 :
984 17244 : const auto& quorumNodes = it->second;
985 :
986 49617 : for (const auto& nodeId : quorumNodes) {
987 32373 : auto& nodeState = nodeStates[nodeId];
988 :
989 32373 : if (nodeState.banned) {
990 0 : continue;
991 : }
992 :
993 32373 : auto& session = nodeState.GetOrCreateSessionFromShare(*sigShare);
994 :
995 32373 : if (session.knows.inv[quorumMember]) {
996 : // he already knows that one
997 17747 : continue;
998 : }
999 :
1000 14626 : auto& inv = sigSharesToAnnounce[nodeId][signHash];
1001 14626 : if (inv.inv.empty()) {
1002 12203 : const auto& llmq_params_opt = Params().GetLLMQ(sigShare->getLlmqType());
1003 12203 : assert(llmq_params_opt.has_value());
1004 12203 : inv.Init(llmq_params_opt->size);
1005 12203 : }
1006 14626 : inv.inv[quorumMember] = true;
1007 14626 : session.knows.inv[quorumMember] = true;
1008 : }
1009 17244 : });
1010 :
1011 : // don't announce these anymore
1012 216234 : sigSharesQueuedToAnnounce.Clear();
1013 216234 : }
1014 :
1015 216234 : bool CSigSharesManager::SendMessages()
1016 : {
1017 216234 : std::unordered_map<NodeId, Uint256HashMap<CSigSharesInv>> sigSharesToRequest;
1018 216234 : std::unordered_map<NodeId, Uint256HashMap<CBatchedSigShares>> sigShareBatchesToSend;
1019 216234 : std::unordered_map<NodeId, std::vector<CSigShare>> sigSharesToSend;
1020 216234 : std::unordered_map<NodeId, Uint256HashMap<CSigSharesInv>> sigSharesToAnnounce;
1021 216234 : std::unordered_map<NodeId, std::vector<CSigSesAnn>> sigSessionAnnouncements;
1022 :
1023 250504 : auto addSigSesAnnIfNeeded = [&](NodeId nodeId, const uint256& signHash) EXCLUSIVE_LOCKS_REQUIRED(cs) {
1024 34270 : AssertLockHeld(cs);
1025 34270 : auto& nodeState = nodeStates[nodeId];
1026 34270 : auto* session = nodeState.GetSessionBySignHash(signHash);
1027 34270 : assert(session);
1028 56106 : while (session->sendSessionId == UNINITIALIZED_SESSION_ID) {
1029 21836 : const uint32_t session_id{GetRand<uint32_t>()};
1030 43672 : if (std::ranges::all_of(nodeState.sessions,
1031 257326 : [&session_id](const auto& s) { return s.second.sendSessionId != session_id; })) {
1032 : // No session is using this id yet
1033 21836 : session->sendSessionId = session_id;
1034 43672 : sigSessionAnnouncements[nodeId].emplace_back(
1035 43672 : CSigSesAnn(/*sessionId=*/session->sendSessionId, /*llmqType=*/session->llmqType,
1036 21836 : /*quorumHash=*/session->quorumHash, /*id=*/session->id, /*msgHash=*/session->msgHash));
1037 21836 : }
1038 : // It's very unlikely that there is a session with the same id,
1039 : // but if there is one we just start over and pick another id
1040 : }
1041 34270 : return session->sendSessionId;
1042 : };
1043 :
1044 216234 : const CConnman::NodesSnapshot snap{m_connman, /* cond = */ CConnman::FullyConnectedOnly};
1045 : {
1046 216234 : LOCK(cs);
1047 216234 : CollectSigSharesToRequest(sigSharesToRequest);
1048 216234 : CollectSigSharesToSend(sigShareBatchesToSend);
1049 216234 : CollectSigSharesToAnnounce(sigSharesToAnnounce);
1050 216234 : CollectSigSharesToSendConcentrated(sigSharesToSend, snap.Nodes());
1051 :
1052 221367 : for (auto& [nodeId, sigShareMap] : sigSharesToRequest) {
1053 5133 : for (auto& [hash, sigShareInv] : sigShareMap) {
1054 6292 : sigShareInv.sessionId = addSigSesAnnIfNeeded(nodeId, hash);
1055 : }
1056 : }
1057 246621 : for (auto& [nodeId, sigShareBatchesMap] : sigShareBatchesToSend) {
1058 30387 : for (auto& [hash, sigShareBatch] : sigShareBatchesMap) {
1059 37842 : sigShareBatch.sessionId = addSigSesAnnIfNeeded(nodeId, hash);
1060 : }
1061 : }
1062 235718 : for (auto& [nodeId, sigShareMap] : sigSharesToAnnounce) {
1063 19484 : for (auto& [hash, sigShareInv] : sigShareMap) {
1064 24406 : sigShareInv.sessionId = addSigSesAnnIfNeeded(nodeId, hash);
1065 : }
1066 : }
1067 216234 : }
1068 :
1069 216234 : bool didSend = false;
1070 :
1071 960566 : for (auto& pnode : snap.Nodes()) {
1072 744332 : CNetMsgMaker msgMaker(pnode->GetCommonVersion());
1073 :
1074 744332 : if (const auto it1 = sigSessionAnnouncements.find(pnode->GetId()); it1 != sigSessionAnnouncements.end()) {
1075 12997 : std::vector<CSigSesAnn> msgs;
1076 12997 : msgs.reserve(it1->second.size());
1077 34828 : for (auto& sigSesAnn : it1->second) {
1078 21831 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QSIGSESANN signHash=%s, sessionId=%d, node=%d\n",
1079 : sigSesAnn.buildSignHash().ToString(), sigSesAnn.getSessionId(), pnode->GetId());
1080 21831 : msgs.emplace_back(sigSesAnn);
1081 21831 : if (msgs.size() == MAX_MSGS_CNT_QSIGSESANN) {
1082 0 : m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSESANN, msgs));
1083 0 : msgs.clear();
1084 0 : didSend = true;
1085 0 : }
1086 : }
1087 12997 : if (!msgs.empty()) {
1088 12997 : m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSESANN, msgs));
1089 12997 : didSend = true;
1090 12997 : }
1091 12997 : }
1092 :
1093 744332 : if (const auto it = sigSharesToRequest.find(pnode->GetId()); it != sigSharesToRequest.end()) {
1094 1986 : std::vector<CSigSharesInv> msgs;
1095 11415 : for (const auto& [signHash, inv] : it->second) {
1096 3143 : assert(inv.CountSet() != 0);
1097 3143 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QGETSIGSHARES signHash=%s, inv={%s}, node=%d\n",
1098 : signHash.ToString(), inv.ToString(), pnode->GetId());
1099 3143 : msgs.emplace_back(inv);
1100 3143 : if (msgs.size() == MAX_MSGS_CNT_QSIGSHARES) {
1101 0 : m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QGETSIGSHARES, msgs));
1102 0 : msgs.clear();
1103 0 : didSend = true;
1104 0 : }
1105 : }
1106 1986 : if (!msgs.empty()) {
1107 1986 : m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QGETSIGSHARES, msgs));
1108 1986 : didSend = true;
1109 1986 : }
1110 1986 : }
1111 :
1112 744332 : if (const auto jt = sigShareBatchesToSend.find(pnode->GetId()); jt != sigShareBatchesToSend.end()) {
1113 11463 : size_t totalSigsCount = 0;
1114 11463 : std::vector<CBatchedSigShares> msgs;
1115 49295 : for (const auto& [signHash, inv] : jt->second) {
1116 18916 : assert(!inv.sigShares.empty());
1117 18916 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QBSIGSHARES signHash=%s, inv={%s}, node=%d\n",
1118 : signHash.ToString(), inv.ToInvString(), pnode->GetId());
1119 37832 : if (totalSigsCount + inv.sigShares.size() > MAX_MSGS_TOTAL_BATCHED_SIGS) {
1120 0 : m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QBSIGSHARES, msgs));
1121 0 : msgs.clear();
1122 0 : totalSigsCount = 0;
1123 0 : didSend = true;
1124 0 : }
1125 18916 : totalSigsCount += inv.sigShares.size();
1126 18916 : msgs.emplace_back(inv);
1127 : }
1128 11463 : if (!msgs.empty()) {
1129 11463 : m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QBSIGSHARES, std::move(msgs)));
1130 11463 : didSend = true;
1131 11463 : }
1132 11463 : }
1133 :
1134 744332 : if (const auto kt = sigSharesToAnnounce.find(pnode->GetId()); kt != sigSharesToAnnounce.end()) {
1135 7281 : std::vector<CSigSharesInv> msgs;
1136 43890 : for (const auto& [signHash, inv] : kt->second) {
1137 12203 : assert(inv.CountSet() != 0);
1138 12203 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QSIGSHARESINV signHash=%s, inv={%s}, node=%d\n",
1139 : signHash.ToString(), inv.ToString(), pnode->GetId());
1140 12203 : msgs.emplace_back(inv);
1141 12203 : if (msgs.size() == MAX_MSGS_CNT_QSIGSHARES) {
1142 0 : m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSHARESINV, msgs));
1143 0 : msgs.clear();
1144 0 : didSend = true;
1145 0 : }
1146 : }
1147 7281 : if (!msgs.empty()) {
1148 7281 : m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSHARESINV, msgs));
1149 7281 : didSend = true;
1150 7281 : }
1151 7281 : }
1152 :
1153 744332 : auto lt = sigSharesToSend.find(pnode->GetId());
1154 744332 : if (lt != sigSharesToSend.end()) {
1155 5624 : std::vector<CSigShare> msgs;
1156 12032 : for (auto& sigShare : lt->second) {
1157 6408 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QSIGSHARE signHash=%s, node=%d\n",
1158 : sigShare.GetSignHash().ToString(), pnode->GetId());
1159 6408 : msgs.emplace_back(std::move(sigShare));
1160 6408 : if (msgs.size() == MAX_MSGS_SIG_SHARES) {
1161 0 : m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSHARE, msgs));
1162 0 : msgs.clear();
1163 0 : didSend = true;
1164 0 : }
1165 : }
1166 5624 : if (!msgs.empty()) {
1167 5624 : m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSHARE, msgs));
1168 5624 : didSend = true;
1169 5624 : }
1170 5624 : }
1171 : }
1172 :
1173 216234 : return didSend;
1174 216234 : }
1175 :
1176 34226 : bool CSigSharesManager::GetSessionInfoByRecvId(NodeId nodeId, uint32_t sessionId, CSigSharesNodeState::SessionInfo& retInfo)
1177 : {
1178 34226 : LOCK(cs);
1179 34226 : return nodeStates[nodeId].GetSessionInfoByRecvId(sessionId, retInfo);
1180 34226 : }
1181 :
1182 18652 : CSigShare CSigSharesManager::RebuildSigShare(const CSigSharesNodeState::SessionInfo& session, const std::pair<uint16_t, CBLSLazySignature>& in)
1183 : {
1184 55956 : const auto& [member, sig] = in;
1185 55956 : CSigShare sigShare(session.llmqType, session.quorumHash, session.id, session.msgHash, member, sig);
1186 18652 : sigShare.UpdateKey();
1187 18652 : return sigShare;
1188 18652 : }
1189 :
1190 216234 : void CSigSharesManager::Cleanup()
1191 : {
1192 216234 : constexpr auto CLEANUP_INTERVAL{5s};
1193 216234 : if (!cleanupThrottler.TryCleanup(CLEANUP_INTERVAL)) {
1194 206298 : return;
1195 : }
1196 :
1197 : // This map is first filled with all quorums found in all sig shares. Then we remove all inactive quorums and
1198 : // loop through all sig shares again to find the ones belonging to the inactive quorums. We then delete the
1199 : // sessions belonging to the sig shares. At the same time, we use this map as a cache when we later need to resolve
1200 : // quorumHash -> quorumPtr (as GetQuorum() requires cs_main, leading to deadlocks with cs held)
1201 9936 : std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher> quorums;
1202 :
1203 : {
1204 9936 : LOCK(cs);
1205 35073 : sigShares.ForEach([&quorums](const SigShareKey&, const CSigShare& sigShare) {
1206 25137 : quorums.try_emplace(std::make_pair(sigShare.getLlmqType(), sigShare.getQuorumHash()), nullptr);
1207 25137 : });
1208 9936 : }
1209 :
1210 : // Find quorums which became inactive
1211 13525 : for (auto it = quorums.begin(); it != quorums.end(); ) {
1212 3589 : if (IsQuorumActive(it->first.first, qman, it->first.second)) {
1213 3376 : auto quorum = qman.GetQuorum(it->first.first, it->first.second);
1214 3376 : if (quorum) {
1215 3376 : it->second = quorum;
1216 3376 : ++it;
1217 3376 : continue;
1218 : }
1219 3376 : }
1220 213 : it = quorums.erase(it);
1221 : }
1222 :
1223 : {
1224 : // Now delete sessions which are for inactive quorums
1225 9936 : LOCK(cs);
1226 9936 : Uint256HashSet inactiveQuorumSessions;
1227 35139 : sigShares.ForEach([&quorums, &inactiveQuorumSessions](const SigShareKey&, const CSigShare& sigShare) {
1228 25203 : if (quorums.count(std::make_pair(sigShare.getLlmqType(), sigShare.getQuorumHash())) == 0) {
1229 1913 : inactiveQuorumSessions.emplace(sigShare.GetSignHash());
1230 1913 : }
1231 25203 : });
1232 11130 : for (const auto& signHash : inactiveQuorumSessions) {
1233 1194 : RemoveSigSharesForSession(signHash);
1234 : }
1235 9936 : }
1236 :
1237 : {
1238 9936 : LOCK(cs);
1239 :
1240 : // Remove sessions which were successfully recovered
1241 9936 : Uint256HashSet doneSessions;
1242 33226 : sigShares.ForEach([&doneSessions, this](const SigShareKey&, const CSigShare& sigShare) {
1243 23290 : if (doneSessions.count(sigShare.GetSignHash()) != 0) {
1244 6 : return;
1245 : }
1246 23284 : if (sigman.HasRecoveredSigForSession(sigShare.GetSignHash())) {
1247 47 : doneSessions.emplace(sigShare.GetSignHash());
1248 47 : }
1249 23290 : });
1250 9983 : for (const auto& signHash : doneSessions) {
1251 47 : RemoveSigSharesForSession(signHash);
1252 : }
1253 :
1254 : // Remove sessions which timed out
1255 9936 : Uint256HashSet timeoutSessions;
1256 9936 : int64_t now = GetTime<std::chrono::seconds>().count();
1257 24449 : for (const auto& [signHash, lastSeenTime] : timeSeenForSessions) {
1258 29026 : if (now - lastSeenTime >= SESSION_NEW_SHARES_TIMEOUT) {
1259 1803 : timeoutSessions.emplace(signHash);
1260 1803 : }
1261 : }
1262 11739 : for (const auto& signHash : timeoutSessions) {
1263 :
1264 1803 : if (const size_t count = sigShares.CountForSignHash(signHash); count > 0) {
1265 1803 : const auto* m = sigShares.GetAllForSignHash(signHash);
1266 1803 : assert(m);
1267 :
1268 1803 : const auto& oneSigShare = m->begin()->second;
1269 :
1270 1803 : std::string strMissingMembers;
1271 1803 : if (LogAcceptDebug(BCLog::LLMQ_SIGS)) {
1272 1803 : if (const auto quorumIt = quorums.find(std::make_pair(oneSigShare.getLlmqType(), oneSigShare.getQuorumHash())); quorumIt != quorums.end()) {
1273 1803 : const auto& quorum = quorumIt->second;
1274 9333 : for (const auto i : util::irange(quorum->members.size())) {
1275 7530 : if (m->count((uint16_t)i) == 0) {
1276 4830 : const auto& dmn = quorum->members[i];
1277 4830 : strMissingMembers += strprintf("\n %s", dmn->proTxHash.ToString());
1278 4830 : }
1279 : }
1280 1803 : }
1281 1803 : }
1282 :
1283 1803 : LogPrintLevel(BCLog::LLMQ_SIGS, BCLog::Level::Info, /* Continued */
1284 : "CSigSharesManager::%s -- signing session timed out. signHash=%s, id=%s, msgHash=%s, "
1285 : "sigShareCount=%d, missingMembers=%s\n",
1286 : __func__, signHash.ToString(), oneSigShare.getId().ToString(),
1287 : oneSigShare.getMsgHash().ToString(), count, strMissingMembers);
1288 1803 : } else {
1289 0 : LogPrintLevel(BCLog::LLMQ_SIGS, BCLog::Level::Info, /* Continued */
1290 : "CSigSharesManager::%s -- signing session timed out. signHash=%s, sigShareCount=%d\n",
1291 : __func__, signHash.ToString(), count);
1292 : }
1293 1803 : RemoveSigSharesForSession(signHash);
1294 : }
1295 9936 : }
1296 :
1297 : // Find node states for peers that disappeared from CConnman
1298 9936 : std::unordered_set<NodeId> nodeStatesToDelete;
1299 : {
1300 9936 : LOCK(cs);
1301 21585 : for (const auto& [nodeId, _] : nodeStates) {
1302 11649 : nodeStatesToDelete.emplace(nodeId);
1303 : }
1304 9936 : }
1305 39912 : m_connman.ForEachNode([&nodeStatesToDelete](const CNode* pnode) { nodeStatesToDelete.erase(pnode->GetId()); });
1306 :
1307 : // Now delete these node states
1308 9936 : LOCK(cs);
1309 10392 : for (const auto& nodeId : nodeStatesToDelete) {
1310 456 : auto it = nodeStates.find(nodeId);
1311 456 : if (it == nodeStates.end()) {
1312 0 : continue;
1313 : }
1314 : // remove global requested state to force a re-request from another node
1315 : // TODO: remove NO_THREAD_SAFETY_ANALYSIS
1316 : // using here template ForEach makes impossible to use lock annotation
1317 456 : it->second.requestedSigShares.ForEach([this](const SigShareKey& k, bool) NO_THREAD_SAFETY_ANALYSIS {
1318 0 : AssertLockHeld(cs);
1319 0 : sigSharesRequested.Erase(k);
1320 0 : });
1321 456 : nodeStates.erase(nodeId);
1322 : }
1323 216234 : }
1324 :
1325 30006 : void CSigSharesManager::RemoveSigSharesForSession(const uint256& signHash)
1326 : {
1327 30006 : AssertLockHeld(cs);
1328 :
1329 118730 : for (auto& [_, nodeState] : nodeStates) {
1330 88724 : nodeState.RemoveSession(signHash);
1331 : }
1332 :
1333 30006 : sigSharesRequested.EraseAllForSignHash(signHash);
1334 30006 : sigSharesQueuedToAnnounce.EraseAllForSignHash(signHash);
1335 30006 : sigShares.EraseAllForSignHash(signHash);
1336 30006 : signedSessions.erase(signHash);
1337 30006 : timeSeenForSessions.erase(signHash);
1338 30006 : }
1339 :
1340 216234 : void CSigSharesManager::RemoveNodesIf(std::function<bool(NodeId)> predicate)
1341 : {
1342 216234 : LOCK(cs);
1343 493395 : for (auto it = nodeStates.begin(); it != nodeStates.end();) {
1344 277161 : if (predicate(it->first)) {
1345 : // re-request sigshares from other nodes
1346 : // TODO: remove NO_THREAD_SAFETY_ANALYSIS
1347 : // using here template ForEach makes impossible to use lock annotation
1348 2 : it->second.requestedSigShares.ForEach([this](const SigShareKey& k, int64_t) NO_THREAD_SAFETY_ANALYSIS {
1349 0 : AssertLockHeld(cs);
1350 0 : sigSharesRequested.Erase(k);
1351 0 : });
1352 2 : it = nodeStates.erase(it);
1353 2 : } else {
1354 277159 : ++it;
1355 : }
1356 : }
1357 216234 : }
1358 :
1359 9 : void CSigSharesManager::MarkAsBanned(NodeId nodeId)
1360 : {
1361 9 : if (nodeId == -1) {
1362 0 : return;
1363 : }
1364 :
1365 9 : LOCK(cs);
1366 9 : auto it = nodeStates.find(nodeId);
1367 9 : if (it == nodeStates.end()) {
1368 0 : return;
1369 : }
1370 :
1371 9 : auto& nodeState = it->second;
1372 : // Whatever we requested from him, let's request it from someone else now
1373 : // TODO: remove NO_THREAD_SAFETY_ANALYSIS
1374 : // using here template ForEach makes impossible to use lock annotation
1375 9 : nodeState.requestedSigShares.ForEach([this](const SigShareKey& k, int64_t) NO_THREAD_SAFETY_ANALYSIS {
1376 0 : AssertLockHeld(cs);
1377 0 : sigSharesRequested.Erase(k);
1378 0 : });
1379 9 : nodeState.requestedSigShares.Clear();
1380 9 : nodeState.banned = true;
1381 9 : }
1382 :
1383 708911 : std::vector<PendingSignatureData> CSigSharesManager::DispatchPendingSigns()
1384 : {
1385 : // Swap out entire vector to avoid lock thrashing
1386 708911 : std::vector<PendingSignatureData> signs;
1387 :
1388 708911 : LOCK(cs_pendingSigns);
1389 708911 : signs.swap(pendingSigns);
1390 :
1391 708911 : return signs;
1392 708911 : }
1393 :
1394 0 : bool CSigSharesManager::IsAnyPendingProcessing() const
1395 : {
1396 0 : LOCK(cs);
1397 : // Check if there's work, spawn a helper if so
1398 0 : return std::any_of(nodeStates.begin(), nodeStates.end(),
1399 0 : [](const auto& entry) { return !entry.second.pendingIncomingSigShares.Empty(); });
1400 0 : }
1401 :
1402 18412 : std::shared_ptr<CRecoveredSig> CSigSharesManager::SignAndProcessSingleShare(PendingSignatureData work)
1403 : {
1404 18412 : auto opt_sigShare = CreateSigShare(*work.quorum, work.id, work.msgHash);
1405 :
1406 18412 : if (opt_sigShare.has_value() && opt_sigShare->sigShare.Get().IsValid()) {
1407 18358 : auto& sigShare = *opt_sigShare;
1408 18358 : auto rs = ProcessSigShare(sigShare, work.quorum);
1409 :
1410 18358 : if (IsAllMembersConnectedEnabled(work.quorum->params.type, m_sporkman)) {
1411 7083 : LOCK(cs);
1412 7083 : auto& session = signedSessions[sigShare.GetSignHash()];
1413 7083 : session.sigShare = std::move(sigShare);
1414 7083 : session.quorum = work.quorum;
1415 7083 : session.nextAttemptTime = 0;
1416 7083 : session.attempt = 0;
1417 7083 : }
1418 18358 : return rs;
1419 18358 : }
1420 54 : return nullptr;
1421 18414 : }
1422 :
1423 18419 : void CSigSharesManager::AsyncSign(CQuorumCPtr quorum, const uint256& id, const uint256& msgHash)
1424 : {
1425 18419 : LOCK(cs_pendingSigns);
1426 18419 : pendingSigns.emplace_back(std::move(quorum), id, msgHash);
1427 18419 : }
1428 :
1429 249 : std::optional<CSigShare> CSigSharesManager::CreateSigShareForSingleMember(const CQuorum& quorum, const uint256& id, const uint256& msgHash) const
1430 : {
1431 249 : cxxtimer::Timer t(true);
1432 249 : auto activeMasterNodeProTxHash = m_mn_activeman.GetProTxHash();
1433 :
1434 249 : int memberIdx = quorum.GetMemberIndex(activeMasterNodeProTxHash);
1435 249 : if (memberIdx == -1) {
1436 : // this should really not happen (IsValidMember gave true)
1437 0 : return std::nullopt;
1438 : }
1439 :
1440 249 : CSigShare sigShare(quorum.params.type, quorum.qc->quorumHash, id, msgHash, uint16_t(memberIdx), {});
1441 249 : uint256 signHash = sigShare.buildSignHash().Get();
1442 :
1443 : // TODO: This one should be SIGN by QUORUM key, not by OPERATOR key
1444 : // see TODO in CDKGSession::FinalizeSingleCommitment for details
1445 241 : auto bls_scheme = bls::bls_legacy_scheme.load();
1446 241 : sigShare.sigShare.Set(m_mn_activeman.Sign(signHash, bls_scheme), bls_scheme);
1447 :
1448 243 : if (!sigShare.sigShare.Get().IsValid()) {
1449 0 : LogPrintf("CSigSharesManager::%s -- failed to sign sigShare. signHash=%s, id=%s, msgHash=%s, time=%s\n",
1450 : __func__, signHash.ToString(), sigShare.getId().ToString(), sigShare.getMsgHash().ToString(),
1451 : t.count());
1452 0 : return std::nullopt;
1453 : }
1454 :
1455 242 : sigShare.UpdateKey();
1456 :
1457 242 : LogPrint(BCLog::LLMQ_SIGS, /* Continued */
1458 : "CSigSharesManager::%s -- created sigShare. signHash=%s, id=%s, msgHash=%s, llmqType=%d, quorum=%s, "
1459 : "time=%s\n",
1460 : __func__, signHash.ToString(), sigShare.getId().ToString(), sigShare.getMsgHash().ToString(),
1461 : std23::to_underlying(quorum.params.type), quorum.qc->quorumHash.ToString(), t.count());
1462 :
1463 243 : return sigShare;
1464 255 : }
1465 :
1466 18556 : std::optional<CSigShare> CSigSharesManager::CreateSigShare(const CQuorum& quorum, const uint256& id, const uint256& msgHash) const
1467 : {
1468 18556 : auto activeMasterNodeProTxHash = m_mn_activeman.GetProTxHash();
1469 :
1470 18556 : if (!quorum.IsValidMember(activeMasterNodeProTxHash)) {
1471 2 : return std::nullopt;
1472 : }
1473 :
1474 18554 : if (quorum.params.is_single_member()) {
1475 241 : return CreateSigShareForSingleMember(quorum, id, msgHash);
1476 : }
1477 18313 : cxxtimer::Timer t(true);
1478 18313 : const CBLSSecretKey& skShare = quorum.GetSkShare();
1479 18313 : if (!skShare.IsValid()) {
1480 54 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- we don't have our skShare for quorum %s\n", __func__, quorum.qc->quorumHash.ToString());
1481 54 : return std::nullopt;
1482 : }
1483 :
1484 18074 : int memberIdx = quorum.GetMemberIndex(activeMasterNodeProTxHash);
1485 18106 : if (memberIdx == -1) {
1486 : // this should really not happen (IsValidMember gave true)
1487 0 : return std::nullopt;
1488 : }
1489 :
1490 18106 : CSigShare sigShare(quorum.params.type, quorum.qc->quorumHash, id, msgHash, uint16_t(memberIdx), {});
1491 18259 : uint256 signHash = sigShare.buildSignHash().Get();
1492 :
1493 18063 : auto bls_scheme = bls::bls_legacy_scheme.load();
1494 18063 : sigShare.sigShare.Set(skShare.Sign(signHash, bls_scheme), bls_scheme);
1495 18117 : if (!sigShare.sigShare.Get().IsValid()) {
1496 0 : LogPrintf("CSigSharesManager::%s -- failed to sign sigShare. signHash=%s, id=%s, msgHash=%s, time=%s\n", __func__,
1497 : signHash.ToString(), sigShare.getId().ToString(), sigShare.getMsgHash().ToString(), t.count());
1498 0 : return std::nullopt;
1499 : }
1500 :
1501 18094 : sigShare.UpdateKey();
1502 :
1503 18115 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- created sigShare. signHash=%s, id=%s, msgHash=%s, llmqType=%d, quorum=%s, time=%s\n", __func__,
1504 : signHash.ToString(), sigShare.getId().ToString(), sigShare.getMsgHash().ToString(), std23::to_underlying(quorum.params.type), quorum.qc->quorumHash.ToString(), t.count());
1505 :
1506 18121 : return sigShare;
1507 19114 : }
1508 :
1509 : // causes all known sigShares to be re-announced
1510 887 : void CSigSharesManager::ForceReAnnouncement(const CQuorum& quorum, Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash)
1511 : {
1512 887 : if (IsAllMembersConnectedEnabled(llmqType, m_sporkman)) {
1513 9 : return;
1514 : }
1515 :
1516 878 : LOCK(cs);
1517 878 : auto signHash = SignHash(llmqType, quorum.qc->quorumHash, id, msgHash).Get();
1518 878 : if (const auto *const sigs = sigShares.GetAllForSignHash(signHash)) {
1519 96 : for (const auto& [quorumMemberIndex, _] : *sigs) {
1520 : // re-announce every sigshare to every node
1521 114 : sigSharesQueuedToAnnounce.Add(std::make_pair(signHash, quorumMemberIndex), true);
1522 : }
1523 39 : }
1524 2879 : for (auto& [_, nodeState] : nodeStates) {
1525 2001 : auto* session = nodeState.GetSessionBySignHash(signHash);
1526 2001 : if (session == nullptr) {
1527 1904 : continue;
1528 : }
1529 : // pretend that the other node doesn't know about any shares so that we re-announce everything
1530 97 : session->knows.SetAll(false);
1531 : // we need to use a new session id as we don't know if the other node has run into a timeout already
1532 97 : session->sendSessionId = UNINITIALIZED_SESSION_ID;
1533 : }
1534 887 : }
1535 :
1536 26962 : RecoveredSigResult CSigSharesManager::HandleNewRecoveredSig(const llmq::CRecoveredSig& recoveredSig)
1537 : {
1538 26962 : auto signHash = recoveredSig.buildSignHash().Get();
1539 26962 : LOCK(cs);
1540 26962 : RemoveSigSharesForSession(signHash);
1541 26962 : return std::monostate{};
1542 26962 : }
1543 : } // namespace llmq
|