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 0 : void CSigShare::UpdateKey()
30 : {
31 0 : key.first = this->buildSignHash().Get();
32 0 : key.second = quorumMember;
33 0 : }
34 :
35 0 : std::string CSigSesAnn::ToString() const
36 : {
37 0 : return strprintf("sessionId=%d, llmqType=%d, quorumHash=%s, id=%s, msgHash=%s",
38 0 : sessionId, std23::to_underlying(getLlmqType()), getQuorumHash().ToString(), getId().ToString(), getMsgHash().ToString());
39 0 : }
40 :
41 0 : void CSigSharesInv::Merge(const CSigSharesInv& inv2)
42 : {
43 0 : for (const auto i : util::irange(inv.size())) {
44 0 : if (inv2.inv[i]) {
45 0 : inv[i] = inv2.inv[i];
46 0 : }
47 : }
48 0 : }
49 :
50 0 : size_t CSigSharesInv::CountSet() const
51 : {
52 0 : return (size_t)std::count(inv.begin(), inv.end(), true);
53 : }
54 :
55 0 : std::string CSigSharesInv::ToString() const
56 : {
57 0 : std::string str = "(";
58 0 : bool first = true;
59 0 : for (const auto i : util::irange(inv.size())) {
60 0 : if (!inv[i]) {
61 0 : continue;
62 : }
63 :
64 0 : if (!first) {
65 0 : str += ",";
66 0 : }
67 0 : first = false;
68 0 : str += strprintf("%d", i);
69 : }
70 0 : str += ")";
71 0 : return str;
72 0 : }
73 :
74 0 : void CSigSharesInv::Init(size_t size)
75 : {
76 0 : inv.resize(size, false);
77 0 : }
78 :
79 0 : void CSigSharesInv::Set(uint16_t quorumMember, bool v)
80 : {
81 0 : assert(quorumMember < inv.size());
82 0 : inv[quorumMember] = v;
83 0 : }
84 :
85 0 : void CSigSharesInv::SetAll(bool v)
86 : {
87 0 : std::fill(inv.begin(), inv.end(), v);
88 0 : }
89 :
90 0 : std::string CBatchedSigShares::ToInvString() const
91 : {
92 0 : 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 0 : inv.Init(400);
95 0 : for (const auto& sigShare : sigShares) {
96 0 : inv.inv[sigShare.first] = true;
97 : }
98 0 : return inv.ToString();
99 0 : }
100 :
101 0 : static void InitSession(CSigSharesNodeState::Session& s, const llmq::SignHash& signHash, CSigBase from)
102 : {
103 0 : const auto& llmq_params_opt = Params().GetLLMQ(from.getLlmqType());
104 0 : assert(llmq_params_opt.has_value());
105 0 : const auto& llmq_params = llmq_params_opt.value();
106 :
107 0 : s.llmqType = from.getLlmqType();
108 0 : s.quorumHash = from.getQuorumHash();
109 0 : s.id = from.getId();
110 0 : s.msgHash = from.getMsgHash();
111 0 : s.signHash = signHash;
112 0 : s.announced.Init((size_t)llmq_params.size);
113 0 : s.requested.Init((size_t)llmq_params.size);
114 0 : s.knows.Init((size_t)llmq_params.size);
115 0 : }
116 :
117 0 : CSigSharesNodeState::Session& CSigSharesNodeState::GetOrCreateSessionFromShare(const llmq::CSigShare& sigShare)
118 : {
119 0 : auto& s = sessions[sigShare.GetSignHash()];
120 0 : if (s.announced.inv.empty()) {
121 0 : InitSession(s, sigShare.buildSignHash(), sigShare);
122 0 : }
123 0 : return s;
124 : }
125 :
126 0 : CSigSharesNodeState::Session& CSigSharesNodeState::GetOrCreateSessionFromAnn(const llmq::CSigSesAnn& ann)
127 : {
128 0 : auto signHash = ann.buildSignHash();
129 0 : auto& s = sessions[signHash.Get()];
130 0 : if (s.announced.inv.empty()) {
131 0 : InitSession(s, signHash, ann);
132 0 : }
133 0 : return s;
134 : }
135 :
136 0 : CSigSharesNodeState::Session* CSigSharesNodeState::GetSessionBySignHash(const uint256& signHash)
137 : {
138 0 : auto it = sessions.find(signHash);
139 0 : if (it == sessions.end()) {
140 0 : return nullptr;
141 : }
142 0 : return &it->second;
143 0 : }
144 :
145 0 : CSigSharesNodeState::Session* CSigSharesNodeState::GetSessionByRecvId(uint32_t sessionId)
146 : {
147 0 : auto it = sessionByRecvId.find(sessionId);
148 0 : if (it == sessionByRecvId.end()) {
149 0 : return nullptr;
150 : }
151 0 : return it->second;
152 0 : }
153 :
154 0 : bool CSigSharesNodeState::GetSessionInfoByRecvId(uint32_t sessionId, SessionInfo& retInfo)
155 : {
156 0 : const auto* s = GetSessionByRecvId(sessionId);
157 0 : if (s == nullptr) {
158 0 : return false;
159 : }
160 0 : retInfo.llmqType = s->llmqType;
161 0 : retInfo.quorumHash = s->quorumHash;
162 0 : retInfo.id = s->id;
163 0 : retInfo.msgHash = s->msgHash;
164 0 : retInfo.signHash = s->signHash;
165 0 : retInfo.quorum = s->quorum;
166 :
167 0 : return true;
168 0 : }
169 :
170 0 : void CSigSharesNodeState::RemoveSession(const uint256& signHash)
171 : {
172 0 : if (const auto it = sessions.find(signHash); it != sessions.end()) {
173 0 : sessionByRecvId.erase(it->second.recvSessionId);
174 0 : sessions.erase(it);
175 0 : }
176 0 : requestedSigShares.EraseAllForSignHash(signHash);
177 0 : pendingIncomingSigShares.EraseAllForSignHash(signHash);
178 0 : }
179 :
180 : //////////////////////
181 :
182 0 : CSigSharesManager::CSigSharesManager(CConnman& connman, const ChainstateManager& chainman, CSigningManager& _sigman,
183 : const CActiveMasternodeManager& mn_activeman, const CQuorumManager& _qman,
184 : const CSporkManager& sporkman) :
185 0 : m_connman{connman},
186 0 : m_chainman{chainman},
187 0 : sigman{_sigman},
188 0 : m_mn_activeman{mn_activeman},
189 0 : qman{_qman},
190 0 : m_sporkman{sporkman}
191 0 : {
192 0 : }
193 :
194 0 : CSigSharesManager::~CSigSharesManager() = default;
195 :
196 0 : void CSigSharesManager::RegisterRecoveryInterface()
197 : {
198 0 : sigman.RegisterRecoveredSigsListener(this);
199 0 : }
200 :
201 0 : void CSigSharesManager::UnregisterRecoveryInterface()
202 : {
203 0 : sigman.UnregisterRecoveredSigsListener(this);
204 0 : }
205 :
206 0 : bool CSigSharesManager::ProcessMessageSigSesAnn(const CNode& pfrom, const CSigSesAnn& ann)
207 : {
208 0 : auto llmqType = ann.getLlmqType();
209 0 : if (!Params().GetLLMQ(llmqType).has_value()) {
210 0 : return false;
211 : }
212 0 : if (ann.getSessionId() == UNINITIALIZED_SESSION_ID || ann.getQuorumHash().IsNull() || ann.getId().IsNull() || ann.getMsgHash().IsNull()) {
213 0 : return false;
214 : }
215 :
216 0 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- ann={%s}, node=%d\n", __func__, ann.ToString(), pfrom.GetId());
217 :
218 0 : auto quorum = qman.GetQuorum(llmqType, ann.getQuorumHash());
219 0 : 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 0 : LOCK(cs);
227 0 : auto& nodeState = nodeStates[pfrom.GetId()];
228 0 : auto& session = nodeState.GetOrCreateSessionFromAnn(ann);
229 0 : nodeState.sessionByRecvId.erase(session.recvSessionId);
230 0 : nodeState.sessionByRecvId.erase(ann.getSessionId());
231 0 : session.recvSessionId = ann.getSessionId();
232 0 : session.quorum = quorum;
233 0 : nodeState.sessionByRecvId.try_emplace(ann.getSessionId(), &session);
234 :
235 0 : return true;
236 0 : }
237 :
238 0 : static bool VerifySigSharesInv(Consensus::LLMQType llmqType, const CSigSharesInv& inv)
239 : {
240 0 : const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
241 0 : return llmq_params_opt.has_value() && (inv.inv.size() == size_t(llmq_params_opt->size));
242 : }
243 :
244 0 : bool CSigSharesManager::ProcessMessageSigShares(const CNode& pfrom, const CSigSharesInv& inv, const std::string& msg_type)
245 : {
246 0 : CSigSharesNodeState::SessionInfo sessionInfo;
247 0 : if (!GetSessionInfoByRecvId(pfrom.GetId(), inv.sessionId, sessionInfo)) {
248 0 : return true;
249 : }
250 :
251 0 : 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 0 : if (sigman.HasRecoveredSigForSession(sessionInfo.signHash.Get())) {
257 0 : return true;
258 : }
259 :
260 0 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- signHash=%s, inv={%s}, node=%d\n", __func__,
261 : sessionInfo.signHash.ToString(), inv.ToString(), pfrom.GetId());
262 :
263 0 : 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 0 : LOCK(cs);
271 0 : auto& nodeState = nodeStates[pfrom.GetId()];
272 0 : auto* session = nodeState.GetSessionByRecvId(inv.sessionId);
273 0 : if (session == nullptr) {
274 0 : return true;
275 : }
276 0 : if (msg_type == NetMsgType::QSIGSHARESINV) {
277 0 : session->announced.Merge(inv);
278 0 : } else { // msg_type == NetMsgType::QGETSIGSHARES
279 0 : session->requested.Merge(inv);
280 : }
281 :
282 0 : session->knows.Merge(inv);
283 0 : return true;
284 0 : }
285 :
286 : // Failure is not issue, we should not ban node
287 0 : static bool PreVerifySigShareQuorum(const CActiveMasternodeManager& mn_activeman, const CQuorumManager& quorum_manager,
288 : const CQuorumCPtr& quorum, Consensus::LLMQType llmqType)
289 : {
290 0 : if (!IsQuorumActive(llmqType, quorum_manager, quorum->qc->quorumHash)) {
291 : // quorum is too old
292 0 : return false;
293 : }
294 0 : 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 0 : return false;
297 : }
298 0 : if (!quorum->HasVerificationVector()) {
299 : // TODO we should allow to ask other nodes for the quorum vvec if we missed it in the DKG
300 0 : 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 0 : return false;
303 : }
304 0 : return true;
305 0 : }
306 :
307 : // Ban node if PreVerifyBatchedSigShares failed
308 0 : bool PreVerifyBatchedSigShares(const CSigSharesNodeState::SessionInfo& session, const CBatchedSigShares& batchedSigShares)
309 : {
310 0 : std::unordered_set<uint16_t> dupMembers;
311 :
312 0 : for (const auto& [quorumMember, _] : batchedSigShares.sigShares) {
313 0 : if (!dupMembers.emplace(quorumMember).second) {
314 0 : return false;
315 : }
316 :
317 0 : 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 0 : 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 0 : return true;
327 0 : }
328 :
329 0 : bool CSigSharesManager::ProcessMessageBatchedSigShares(const CNode& pfrom, const CBatchedSigShares& batchedSigShares)
330 : {
331 0 : CSigSharesNodeState::SessionInfo sessionInfo;
332 0 : if (!GetSessionInfoByRecvId(pfrom.GetId(), batchedSigShares.sessionId, sessionInfo)) {
333 0 : return true;
334 : }
335 :
336 0 : if (!PreVerifySigShareQuorum(m_mn_activeman, qman, sessionInfo.quorum, sessionInfo.llmqType)) {
337 0 : return true;
338 : }
339 :
340 0 : if (!PreVerifyBatchedSigShares(sessionInfo, batchedSigShares)) {
341 0 : return false; // ban node
342 : }
343 :
344 0 : std::vector<CSigShare> sigSharesToProcess;
345 0 : sigSharesToProcess.reserve(batchedSigShares.sigShares.size());
346 :
347 : {
348 0 : LOCK(cs);
349 0 : auto& nodeState = nodeStates[pfrom.GetId()];
350 :
351 0 : for (const auto& sigSharetmp : batchedSigShares.sigShares) {
352 0 : CSigShare sigShare = RebuildSigShare(sessionInfo, sigSharetmp);
353 0 : 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 0 : if (sigShares.Has(sigShare.GetKey())) {
360 0 : continue;
361 : }
362 :
363 : // TODO for PoSe, we should consider propagating shares even if we already have a recovered sig
364 0 : if (sigman.HasRecoveredSigForId(sigShare.getLlmqType(), sigShare.getId())) {
365 0 : continue;
366 : }
367 :
368 0 : sigSharesToProcess.emplace_back(sigShare);
369 0 : }
370 0 : }
371 :
372 0 : 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 0 : if (sigSharesToProcess.empty()) {
376 0 : return true;
377 : }
378 :
379 0 : LOCK(cs);
380 0 : auto& nodeState = nodeStates[pfrom.GetId()];
381 0 : for (const auto& s : sigSharesToProcess) {
382 0 : nodeState.pendingIncomingSigShares.Add(s.GetKey(), s);
383 : }
384 0 : return true;
385 0 : }
386 :
387 0 : bool CSigSharesManager::ProcessMessageSigShare(NodeId fromId, const CSigShare& sigShare)
388 : {
389 0 : auto quorum = qman.GetQuorum(sigShare.getLlmqType(), sigShare.getQuorumHash());
390 0 : if (!quorum) {
391 0 : return true;
392 : }
393 0 : if (!PreVerifySigShareQuorum(m_mn_activeman, qman, quorum, sigShare.getLlmqType())) {
394 0 : return true;
395 : }
396 :
397 0 : 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 0 : 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 0 : const auto signHash = sigShare.GetSignHash();
407 0 : const bool alreadyRecovered = sigman.HasRecoveredSigForId(sigShare.getLlmqType(), sigShare.getId()) ||
408 0 : sigman.HasRecoveredSigForSession(signHash);
409 :
410 : {
411 0 : LOCK(cs);
412 :
413 0 : if (alreadyRecovered) {
414 0 : 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 0 : return true;
420 : }
421 :
422 0 : if (sigShares.Has(sigShare.GetKey())) {
423 0 : return true;
424 : }
425 :
426 0 : auto& nodeState = nodeStates[fromId];
427 0 : nodeState.pendingIncomingSigShares.Add(sigShare.GetKey(), sigShare);
428 0 : }
429 :
430 0 : 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 0 : return true;
433 0 : }
434 :
435 0 : 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 0 : bool more_work{false};
440 :
441 : {
442 0 : LOCK(cs);
443 0 : if (nodeStates.empty()) {
444 0 : 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 0 : std::unordered_set<std::pair<NodeId, uint256>, StaticSaltedHasher> uniqueSignHashes;
453 0 : IterateNodesRandom(
454 0 : nodeStates,
455 0 : [&]() {
456 0 : return uniqueSignHashes.size() < maxUniqueSessions;
457 : // TODO: remove NO_THREAD_SAFETY_ANALYSIS
458 : // using here template IterateNodesRandom makes impossible to use lock annotation
459 : },
460 0 : [&](NodeId nodeId, CSigSharesNodeState& ns) NO_THREAD_SAFETY_ANALYSIS {
461 0 : if (ns.pendingIncomingSigShares.Empty()) {
462 0 : return false;
463 : }
464 0 : const auto& sigShare = *ns.pendingIncomingSigShares.GetFirst();
465 :
466 0 : AssertLockHeld(cs);
467 0 : if (const bool alreadyHave = this->sigShares.Has(sigShare.GetKey()); !alreadyHave) {
468 0 : uniqueSignHashes.emplace(nodeId, sigShare.GetSignHash());
469 0 : retSigShares[nodeId].emplace_back(sigShare);
470 0 : }
471 0 : ns.pendingIncomingSigShares.Erase(sigShare.GetKey());
472 0 : return !ns.pendingIncomingSigShares.Empty();
473 0 : },
474 0 : rnd);
475 :
476 0 : if (retSigShares.empty()) {
477 0 : return false;
478 : }
479 :
480 : // Determine if there is still work left in any node state after pulling this batch
481 0 : more_work = std::any_of(nodeStates.begin(), nodeStates.end(),
482 0 : [](const auto& entry) {
483 0 : const auto& ns = entry.second;
484 0 : return !ns.pendingIncomingSigShares.Empty();
485 : });
486 0 : }
487 :
488 : // For the convenience of the caller, also build a map of quorumHash -> quorum
489 :
490 0 : for (const auto& [_, vecSigShares] : retSigShares) {
491 0 : for (const auto& sigShare : vecSigShares) {
492 0 : auto llmqType = sigShare.getLlmqType();
493 :
494 0 : auto k = std::make_pair(llmqType, sigShare.getQuorumHash());
495 0 : if (retQuorums.count(k) != 0) {
496 0 : continue;
497 : }
498 :
499 0 : 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 0 : 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 0 : retQuorums.try_emplace(k, quorum);
509 0 : }
510 : }
511 :
512 0 : return more_work;
513 0 : }
514 :
515 : // It's ensured that no duplicates are passed to this method
516 0 : 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 0 : cxxtimer::Timer t(true);
521 0 : std::vector<std::shared_ptr<CRecoveredSig>> recovered_sigs;
522 0 : for (const auto& sigShare : sigSharesToProcess) {
523 0 : auto quorumKey = std::make_pair(sigShare.getLlmqType(), sigShare.getQuorumHash());
524 0 : auto rs = ProcessSigShare(sigShare, quorums.at(quorumKey));
525 0 : if (rs != nullptr) {
526 0 : recovered_sigs.emplace_back(std::move(rs));
527 0 : }
528 0 : }
529 0 : t.stop();
530 :
531 0 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- processed sigShare batch. shares=%d, time=%ds\n", __func__,
532 : sigSharesToProcess.size(), t.count());
533 0 : return recovered_sigs;
534 0 : }
535 :
536 : // sig shares are already verified when entering this method
537 0 : std::shared_ptr<CRecoveredSig> CSigSharesManager::ProcessSigShare(const CSigShare& sigShare, const CQuorumCPtr& quorum)
538 : {
539 0 : auto llmqType = quorum->params.type;
540 :
541 0 : const bool isAllMembersConnectedEnabled = IsAllMembersConnectedEnabled(llmqType, m_sporkman);
542 :
543 : // prepare node set for direct-push in case this is our sig share
544 0 : std::vector<NodeId> quorumNodes;
545 0 : if (!isAllMembersConnectedEnabled &&
546 0 : sigShare.getQuorumMember() == quorum->GetMemberIndex(m_mn_activeman.GetProTxHash())) {
547 0 : quorumNodes = m_connman.GetMasternodeQuorumNodes(sigShare.getLlmqType(), sigShare.getQuorumHash());
548 0 : }
549 :
550 0 : if (sigman.HasRecoveredSigForId(llmqType, sigShare.getId())) {
551 0 : return nullptr;
552 : }
553 :
554 0 : bool canTryRecovery = false;
555 : {
556 0 : LOCK(cs);
557 :
558 0 : if (!sigShares.Add(sigShare.GetKey(), sigShare)) {
559 0 : return nullptr;
560 : }
561 0 : if (!isAllMembersConnectedEnabled) {
562 0 : sigSharesQueuedToAnnounce.Add(sigShare.GetKey(), true);
563 0 : }
564 :
565 : // Update the time we've seen the last sigShare
566 0 : 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 0 : for (auto otherNodeId : quorumNodes) {
571 0 : auto& nodeState = nodeStates[otherNodeId];
572 0 : auto& session = nodeState.GetOrCreateSessionFromShare(sigShare);
573 0 : session.quorum = quorum;
574 0 : session.requested.Set(sigShare.getQuorumMember(), true);
575 0 : session.knows.Set(sigShare.getQuorumMember(), true);
576 : }
577 :
578 0 : size_t sigShareCount = sigShares.CountForSignHash(sigShare.GetSignHash());
579 0 : if (sigShareCount >= size_t(quorum->params.threshold)) {
580 0 : canTryRecovery = true;
581 0 : }
582 0 : }
583 0 : if (!canTryRecovery) return nullptr;
584 :
585 0 : return TryRecoverSig(*quorum, sigShare.getId(), sigShare.getMsgHash());
586 0 : }
587 :
588 0 : std::shared_ptr<CRecoveredSig> CSigSharesManager::TryRecoverSig(const CQuorum& quorum, const uint256& id,
589 : const uint256& msgHash)
590 : {
591 0 : if (sigman.HasRecoveredSigForId(quorum.params.type, id)) {
592 0 : return nullptr;
593 : }
594 :
595 0 : std::vector<CBLSSignature> sigSharesForRecovery;
596 0 : std::vector<CBLSId> idsForRecovery;
597 : {
598 0 : LOCK(cs);
599 :
600 0 : auto signHash = SignHash(quorum.params.type, quorum.qc->quorumHash, id, msgHash).Get();
601 0 : const auto* sigSharesForSignHash = sigShares.GetAllForSignHash(signHash);
602 0 : if (sigSharesForSignHash == nullptr) {
603 0 : return nullptr;
604 : }
605 :
606 0 : std::shared_ptr<CRecoveredSig> singleMemberRecoveredSig;
607 0 : if (quorum.params.is_single_member()) {
608 0 : 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 0 : const auto& sigShare = sigSharesForSignHash->begin()->second;
616 0 : CBLSSignature recoveredSig = sigShare.sigShare.Get();
617 0 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- recover single-node signature. id=%s, msgHash=%s\n",
618 : __func__, id.ToString(), msgHash.ToString());
619 :
620 0 : singleMemberRecoveredSig = std::make_shared<CRecoveredSig>(quorum.params.type, quorum.qc->quorumHash, id, msgHash,
621 : recoveredSig);
622 0 : }
623 :
624 0 : sigSharesForRecovery.reserve((size_t) quorum.params.threshold);
625 0 : idsForRecovery.reserve((size_t) quorum.params.threshold);
626 0 : for (auto it = sigSharesForSignHash->begin(); it != sigSharesForSignHash->end() && sigSharesForRecovery.size() < size_t(quorum.params.threshold); ++it) {
627 0 : const auto& sigShare = it->second;
628 0 : sigSharesForRecovery.emplace_back(sigShare.sigShare.Get());
629 0 : idsForRecovery.emplace_back(quorum.members[sigShare.getQuorumMember()]->proTxHash);
630 0 : }
631 :
632 : // check if we can recover the final signature
633 0 : if (sigSharesForRecovery.size() < size_t(quorum.params.threshold)) {
634 0 : return nullptr;
635 : }
636 0 : if (quorum.params.is_single_member()) {
637 0 : return singleMemberRecoveredSig; // end of single-quorum processing
638 : }
639 0 : }
640 :
641 : // now recover it
642 0 : cxxtimer::Timer t(true);
643 0 : CBLSSignature recoveredSig;
644 0 : if (!recoveredSig.Recover(sigSharesForRecovery, idsForRecovery)) {
645 0 : 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 0 : return nullptr;
648 : }
649 :
650 0 : 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 0 : 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 0 : if (((recoveredSigsCounter++) % 100) == 0) {
659 0 : auto signHash = rs->buildSignHash();
660 0 : bool valid = recoveredSig.VerifyInsecure(quorum.qc->quorumPublicKey, signHash.Get());
661 0 : 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 0 : }
668 0 : return rs;
669 0 : }
670 :
671 0 : CDeterministicMNCPtr CSigSharesManager::SelectMemberForRecovery(const CQuorum& quorum, const uint256 &id, int attempt)
672 : {
673 0 : assert(attempt < quorum.params.recoveryMembers);
674 :
675 0 : std::vector<std::pair<uint256, CDeterministicMNCPtr>> v;
676 0 : v.reserve(quorum.members.size());
677 0 : for (const auto& dmn : quorum.members) {
678 0 : auto h = ::SerializeHash(std::make_pair(dmn->proTxHash, id));
679 0 : v.emplace_back(h, dmn);
680 : }
681 0 : std::sort(v.begin(), v.end());
682 :
683 0 : return v[attempt % v.size()].second;
684 0 : }
685 :
686 0 : 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 0 : AssertLockNotHeld(cs_pendingSigns);
691 :
692 0 : if (m_mn_activeman.GetProTxHash().IsNull()) return false;
693 :
694 0 : auto quorum = [&]() {
695 0 : 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 0 : const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
703 0 : assert(llmq_params_opt.has_value());
704 0 : return SelectQuorumForSigning(llmq_params_opt.value(), m_chainman.ActiveChain(), qman, id);
705 : } else {
706 0 : return qman.GetQuorum(llmqType, quorumHash);
707 : }
708 0 : }();
709 :
710 0 : if (!quorum) {
711 0 : LogPrint(BCLog::LLMQ, "CSigningManager::%s -- failed to select quorum. id=%s, msgHash=%s\n", __func__,
712 : id.ToString(), msgHash.ToString());
713 0 : return false;
714 : }
715 :
716 0 : if (!quorum->IsValidMember(m_mn_activeman.GetProTxHash())) {
717 0 : return false;
718 : }
719 :
720 : {
721 0 : auto& db = sigman.GetDb();
722 0 : bool hasVoted = db.HasVotedOnId(llmqType, id);
723 0 : if (hasVoted) {
724 0 : uint256 prevMsgHash;
725 0 : db.GetVoteForId(llmqType, id, prevMsgHash);
726 0 : if (msgHash != prevMsgHash) {
727 0 : if (allowDiffMsgHashSigning) {
728 0 : 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 0 : hasVoted = false;
732 0 : } else {
733 0 : 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 0 : return false;
737 : }
738 0 : } else if (allowReSign) {
739 0 : LogPrint(BCLog::LLMQ, "%s -- already voted for id=%s and msgHash=%s. Resigning!\n", __func__,
740 : id.ToString(), prevMsgHash.ToString());
741 0 : } else {
742 0 : LogPrint(BCLog::LLMQ, "%s -- already voted for id=%s and msgHash=%s. Not voting again.\n", __func__,
743 : id.ToString(), prevMsgHash.ToString());
744 0 : return false;
745 : }
746 0 : }
747 :
748 0 : if (db.HasRecoveredSigForId(llmqType, id)) {
749 : // no need to sign it if we already have a recovered sig
750 0 : return true;
751 : }
752 0 : if (!hasVoted) {
753 0 : db.WriteVoteForId(llmqType, id, msgHash);
754 0 : }
755 : }
756 :
757 0 : if (allowReSign) {
758 : // make us re-announce all known shares (other nodes might have run into a timeout)
759 0 : ForceReAnnouncement(*quorum, llmqType, id, msgHash);
760 0 : }
761 0 : AsyncSign(std::move(quorum), id, msgHash);
762 :
763 0 : return true;
764 0 : }
765 :
766 0 : void CSigSharesManager::CollectSigSharesToRequest(std::unordered_map<NodeId, Uint256HashMap<CSigSharesInv>>& sigSharesToRequest)
767 : {
768 0 : AssertLockHeld(cs);
769 :
770 0 : int64_t now = GetTime<std::chrono::seconds>().count();
771 0 : const size_t maxRequestsForNode = 32;
772 :
773 : // avoid requesting from same nodes all the time
774 0 : std::vector<NodeId> shuffledNodeIds;
775 0 : shuffledNodeIds.reserve(nodeStates.size());
776 0 : for (const auto& [nodeId, nodeState] : nodeStates) {
777 0 : if (nodeState.sessions.empty()) {
778 0 : continue;
779 : }
780 0 : shuffledNodeIds.emplace_back(nodeId);
781 : }
782 0 : Shuffle(shuffledNodeIds.begin(), shuffledNodeIds.end(), rnd);
783 :
784 0 : for (const auto& nodeId : shuffledNodeIds) {
785 0 : auto& nodeState = nodeStates[nodeId];
786 :
787 0 : if (nodeState.banned) {
788 0 : continue;
789 : }
790 :
791 0 : nodeState.requestedSigShares.EraseIf([&now, &nodeId](const SigShareKey& k, int64_t t) {
792 0 : if (now - t >= SIG_SHARE_REQUEST_TIMEOUT) {
793 : // timeout while waiting for this one, so retry it with another node
794 0 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::CollectSigSharesToRequest -- timeout while waiting for %s-%d, node=%d\n",
795 : k.first.ToString(), k.second, nodeId);
796 0 : return true;
797 : }
798 0 : return false;
799 0 : });
800 :
801 0 : decltype(sigSharesToRequest.begin()->second)* invMap = nullptr;
802 :
803 0 : for (auto& [signHash, session] : nodeState.sessions) {
804 0 : if (IsAllMembersConnectedEnabled(session.llmqType, m_sporkman)) {
805 0 : continue;
806 : }
807 :
808 0 : if (sigman.HasRecoveredSigForSession(signHash)) {
809 0 : continue;
810 : }
811 :
812 0 : for (const auto i : util::irange(session.announced.inv.size())) {
813 0 : if (!session.announced.inv[i]) {
814 0 : continue;
815 : }
816 0 : auto k = std::make_pair(signHash, (uint16_t) i);
817 0 : if (sigShares.Has(k)) {
818 : // we already have it
819 0 : session.announced.inv[i] = false;
820 0 : continue;
821 : }
822 0 : if (nodeState.requestedSigShares.Size() >= maxRequestsForNode) {
823 : // too many pending requests for this node
824 0 : break;
825 : }
826 0 : if (auto *const p = sigSharesRequested.Get(k)) {
827 0 : if (now - p->second >= SIG_SHARE_REQUEST_TIMEOUT && nodeId != p->first) {
828 : // other node timed out, re-request from this node
829 0 : 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 0 : } else {
832 0 : continue;
833 : }
834 0 : }
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 0 : nodeState.requestedSigShares.Add(k, now);
839 :
840 : // don't request it from other nodes until a timeout happens
841 0 : auto& r = sigSharesRequested.GetOrAdd(k);
842 0 : r.first = nodeId;
843 0 : r.second = now;
844 :
845 0 : if (invMap == nullptr) {
846 0 : invMap = &sigSharesToRequest[nodeId];
847 0 : }
848 0 : auto& inv = (*invMap)[signHash];
849 0 : if (inv.inv.empty()) {
850 0 : const auto& llmq_params_opt = Params().GetLLMQ(session.llmqType);
851 0 : assert(llmq_params_opt.has_value());
852 0 : inv.Init(llmq_params_opt->size);
853 0 : }
854 0 : inv.inv[k.second] = true;
855 :
856 : // don't request it again from this node
857 0 : session.announced.inv[i] = false;
858 : }
859 : }
860 : }
861 0 : }
862 :
863 0 : void CSigSharesManager::CollectSigSharesToSend(std::unordered_map<NodeId, Uint256HashMap<CBatchedSigShares>>& sigSharesToSend)
864 : {
865 0 : AssertLockHeld(cs);
866 :
867 0 : for (auto& [nodeId, nodeState] : nodeStates) {
868 0 : if (nodeState.banned) {
869 0 : continue;
870 : }
871 :
872 0 : decltype(sigSharesToSend.begin()->second)* sigSharesToSend2 = nullptr;
873 :
874 0 : for (auto& [signHash, session] : nodeState.sessions) {
875 0 : if (IsAllMembersConnectedEnabled(session.llmqType, m_sporkman)) {
876 0 : continue;
877 : }
878 :
879 0 : if (sigman.HasRecoveredSigForSession(signHash)) {
880 0 : continue;
881 : }
882 :
883 0 : CBatchedSigShares batchedSigShares;
884 :
885 0 : for (const auto i : util::irange(session.requested.inv.size())) {
886 0 : if (!session.requested.inv[i]) {
887 0 : continue;
888 : }
889 0 : session.requested.inv[i] = false;
890 :
891 0 : auto k = std::make_pair(signHash, (uint16_t)i);
892 0 : const CSigShare* sigShare = sigShares.Get(k);
893 0 : if (sigShare == nullptr) {
894 : // he requested something we don't have
895 0 : session.requested.inv[i] = false;
896 0 : continue;
897 : }
898 :
899 0 : batchedSigShares.sigShares.emplace_back((uint16_t)i, sigShare->sigShare);
900 : }
901 :
902 0 : if (!batchedSigShares.sigShares.empty()) {
903 0 : if (sigSharesToSend2 == nullptr) {
904 : // only create the map if we actually add a batched sig
905 0 : sigSharesToSend2 = &sigSharesToSend[nodeId];
906 0 : }
907 0 : sigSharesToSend2->try_emplace(signHash, std::move(batchedSigShares));
908 0 : }
909 0 : }
910 : }
911 0 : }
912 :
913 0 : void CSigSharesManager::CollectSigSharesToSendConcentrated(std::unordered_map<NodeId, std::vector<CSigShare>>& sigSharesToSend, const std::vector<CNode*>& vNodes)
914 : {
915 0 : AssertLockHeld(cs);
916 :
917 0 : Uint256HashMap<CNode*> proTxToNode;
918 0 : for (const auto& pnode : vNodes) {
919 0 : auto verifiedProRegTxHash = pnode->GetVerifiedProRegTxHash();
920 0 : if (verifiedProRegTxHash.IsNull()) {
921 0 : continue;
922 : }
923 0 : proTxToNode.try_emplace(verifiedProRegTxHash, pnode);
924 : }
925 :
926 0 : auto curTime = GetTime<std::chrono::milliseconds>().count();
927 :
928 0 : for (auto& [_, signedSession] : signedSessions) {
929 0 : if (!IsAllMembersConnectedEnabled(signedSession.quorum->params.type, m_sporkman)) {
930 0 : continue;
931 : }
932 :
933 0 : if (signedSession.attempt >= signedSession.quorum->params.recoveryMembers) {
934 0 : continue;
935 : }
936 :
937 0 : if (curTime >= signedSession.nextAttemptTime) {
938 0 : int64_t waitTime = exp2(signedSession.attempt) * EXP_SEND_FOR_RECOVERY_TIMEOUT;
939 0 : waitTime = std::min(MAX_SEND_FOR_RECOVERY_TIMEOUT, waitTime);
940 0 : signedSession.nextAttemptTime = curTime + waitTime;
941 0 : auto dmn = SelectMemberForRecovery(*signedSession.quorum, signedSession.sigShare.getId(), signedSession.attempt);
942 0 : signedSession.attempt++;
943 :
944 0 : 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 0 : auto it = proTxToNode.find(dmn->proTxHash);
948 0 : if (it == proTxToNode.end()) {
949 0 : continue;
950 : }
951 :
952 0 : auto& m = sigSharesToSend[it->second->GetId()];
953 0 : m.emplace_back(signedSession.sigShare);
954 0 : }
955 : }
956 0 : }
957 :
958 0 : void CSigSharesManager::CollectSigSharesToAnnounce(std::unordered_map<NodeId, Uint256HashMap<CSigSharesInv>>& sigSharesToAnnounce)
959 : {
960 0 : AssertLockHeld(cs);
961 :
962 0 : 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 0 : sigSharesQueuedToAnnounce.ForEach([this, &quorumNodesMap, &sigSharesToAnnounce](const SigShareKey& sigShareKey,
967 : bool) NO_THREAD_SAFETY_ANALYSIS {
968 0 : AssertLockHeld(cs);
969 0 : const auto& signHash = sigShareKey.first;
970 0 : auto quorumMember = sigShareKey.second;
971 0 : const CSigShare* sigShare = sigShares.Get(sigShareKey);
972 0 : if (sigShare == nullptr) {
973 0 : return;
974 : }
975 :
976 : // announce to the nodes which we know through the intra-quorum-communication system
977 0 : auto quorumKey = std::make_pair(sigShare->getLlmqType(), sigShare->getQuorumHash());
978 0 : auto it = quorumNodesMap.find(quorumKey);
979 0 : if (it == quorumNodesMap.end()) {
980 0 : auto nodeIds = m_connman.GetMasternodeQuorumNodes(quorumKey.first, quorumKey.second);
981 0 : it = quorumNodesMap.emplace(std::piecewise_construct, std::forward_as_tuple(quorumKey), std::forward_as_tuple(nodeIds.begin(), nodeIds.end())).first;
982 0 : }
983 :
984 0 : const auto& quorumNodes = it->second;
985 :
986 0 : for (const auto& nodeId : quorumNodes) {
987 0 : auto& nodeState = nodeStates[nodeId];
988 :
989 0 : if (nodeState.banned) {
990 0 : continue;
991 : }
992 :
993 0 : auto& session = nodeState.GetOrCreateSessionFromShare(*sigShare);
994 :
995 0 : if (session.knows.inv[quorumMember]) {
996 : // he already knows that one
997 0 : continue;
998 : }
999 :
1000 0 : auto& inv = sigSharesToAnnounce[nodeId][signHash];
1001 0 : if (inv.inv.empty()) {
1002 0 : const auto& llmq_params_opt = Params().GetLLMQ(sigShare->getLlmqType());
1003 0 : assert(llmq_params_opt.has_value());
1004 0 : inv.Init(llmq_params_opt->size);
1005 0 : }
1006 0 : inv.inv[quorumMember] = true;
1007 0 : session.knows.inv[quorumMember] = true;
1008 : }
1009 0 : });
1010 :
1011 : // don't announce these anymore
1012 0 : sigSharesQueuedToAnnounce.Clear();
1013 0 : }
1014 :
1015 0 : bool CSigSharesManager::SendMessages()
1016 : {
1017 0 : std::unordered_map<NodeId, Uint256HashMap<CSigSharesInv>> sigSharesToRequest;
1018 0 : std::unordered_map<NodeId, Uint256HashMap<CBatchedSigShares>> sigShareBatchesToSend;
1019 0 : std::unordered_map<NodeId, std::vector<CSigShare>> sigSharesToSend;
1020 0 : std::unordered_map<NodeId, Uint256HashMap<CSigSharesInv>> sigSharesToAnnounce;
1021 0 : std::unordered_map<NodeId, std::vector<CSigSesAnn>> sigSessionAnnouncements;
1022 :
1023 0 : auto addSigSesAnnIfNeeded = [&](NodeId nodeId, const uint256& signHash) EXCLUSIVE_LOCKS_REQUIRED(cs) {
1024 0 : AssertLockHeld(cs);
1025 0 : auto& nodeState = nodeStates[nodeId];
1026 0 : auto* session = nodeState.GetSessionBySignHash(signHash);
1027 0 : assert(session);
1028 0 : while (session->sendSessionId == UNINITIALIZED_SESSION_ID) {
1029 0 : const uint32_t session_id{GetRand<uint32_t>()};
1030 0 : if (std::ranges::all_of(nodeState.sessions,
1031 0 : [&session_id](const auto& s) { return s.second.sendSessionId != session_id; })) {
1032 : // No session is using this id yet
1033 0 : session->sendSessionId = session_id;
1034 0 : sigSessionAnnouncements[nodeId].emplace_back(
1035 0 : CSigSesAnn(/*sessionId=*/session->sendSessionId, /*llmqType=*/session->llmqType,
1036 0 : /*quorumHash=*/session->quorumHash, /*id=*/session->id, /*msgHash=*/session->msgHash));
1037 0 : }
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 0 : return session->sendSessionId;
1042 : };
1043 :
1044 0 : const CConnman::NodesSnapshot snap{m_connman, /* cond = */ CConnman::FullyConnectedOnly};
1045 : {
1046 0 : LOCK(cs);
1047 0 : CollectSigSharesToRequest(sigSharesToRequest);
1048 0 : CollectSigSharesToSend(sigShareBatchesToSend);
1049 0 : CollectSigSharesToAnnounce(sigSharesToAnnounce);
1050 0 : CollectSigSharesToSendConcentrated(sigSharesToSend, snap.Nodes());
1051 :
1052 0 : for (auto& [nodeId, sigShareMap] : sigSharesToRequest) {
1053 0 : for (auto& [hash, sigShareInv] : sigShareMap) {
1054 0 : sigShareInv.sessionId = addSigSesAnnIfNeeded(nodeId, hash);
1055 : }
1056 : }
1057 0 : for (auto& [nodeId, sigShareBatchesMap] : sigShareBatchesToSend) {
1058 0 : for (auto& [hash, sigShareBatch] : sigShareBatchesMap) {
1059 0 : sigShareBatch.sessionId = addSigSesAnnIfNeeded(nodeId, hash);
1060 : }
1061 : }
1062 0 : for (auto& [nodeId, sigShareMap] : sigSharesToAnnounce) {
1063 0 : for (auto& [hash, sigShareInv] : sigShareMap) {
1064 0 : sigShareInv.sessionId = addSigSesAnnIfNeeded(nodeId, hash);
1065 : }
1066 : }
1067 0 : }
1068 :
1069 0 : bool didSend = false;
1070 :
1071 0 : for (auto& pnode : snap.Nodes()) {
1072 0 : CNetMsgMaker msgMaker(pnode->GetCommonVersion());
1073 :
1074 0 : if (const auto it1 = sigSessionAnnouncements.find(pnode->GetId()); it1 != sigSessionAnnouncements.end()) {
1075 0 : std::vector<CSigSesAnn> msgs;
1076 0 : msgs.reserve(it1->second.size());
1077 0 : for (auto& sigSesAnn : it1->second) {
1078 0 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QSIGSESANN signHash=%s, sessionId=%d, node=%d\n",
1079 : sigSesAnn.buildSignHash().ToString(), sigSesAnn.getSessionId(), pnode->GetId());
1080 0 : msgs.emplace_back(sigSesAnn);
1081 0 : 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 0 : if (!msgs.empty()) {
1088 0 : m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSESANN, msgs));
1089 0 : didSend = true;
1090 0 : }
1091 0 : }
1092 :
1093 0 : if (const auto it = sigSharesToRequest.find(pnode->GetId()); it != sigSharesToRequest.end()) {
1094 0 : std::vector<CSigSharesInv> msgs;
1095 0 : for (const auto& [signHash, inv] : it->second) {
1096 0 : assert(inv.CountSet() != 0);
1097 0 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QGETSIGSHARES signHash=%s, inv={%s}, node=%d\n",
1098 : signHash.ToString(), inv.ToString(), pnode->GetId());
1099 0 : msgs.emplace_back(inv);
1100 0 : 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 0 : if (!msgs.empty()) {
1107 0 : m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QGETSIGSHARES, msgs));
1108 0 : didSend = true;
1109 0 : }
1110 0 : }
1111 :
1112 0 : if (const auto jt = sigShareBatchesToSend.find(pnode->GetId()); jt != sigShareBatchesToSend.end()) {
1113 0 : size_t totalSigsCount = 0;
1114 0 : std::vector<CBatchedSigShares> msgs;
1115 0 : for (const auto& [signHash, inv] : jt->second) {
1116 0 : assert(!inv.sigShares.empty());
1117 0 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QBSIGSHARES signHash=%s, inv={%s}, node=%d\n",
1118 : signHash.ToString(), inv.ToInvString(), pnode->GetId());
1119 0 : 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 0 : totalSigsCount += inv.sigShares.size();
1126 0 : msgs.emplace_back(inv);
1127 : }
1128 0 : if (!msgs.empty()) {
1129 0 : m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QBSIGSHARES, std::move(msgs)));
1130 0 : didSend = true;
1131 0 : }
1132 0 : }
1133 :
1134 0 : if (const auto kt = sigSharesToAnnounce.find(pnode->GetId()); kt != sigSharesToAnnounce.end()) {
1135 0 : std::vector<CSigSharesInv> msgs;
1136 0 : for (const auto& [signHash, inv] : kt->second) {
1137 0 : assert(inv.CountSet() != 0);
1138 0 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QSIGSHARESINV signHash=%s, inv={%s}, node=%d\n",
1139 : signHash.ToString(), inv.ToString(), pnode->GetId());
1140 0 : msgs.emplace_back(inv);
1141 0 : 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 0 : if (!msgs.empty()) {
1148 0 : m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSHARESINV, msgs));
1149 0 : didSend = true;
1150 0 : }
1151 0 : }
1152 :
1153 0 : auto lt = sigSharesToSend.find(pnode->GetId());
1154 0 : if (lt != sigSharesToSend.end()) {
1155 0 : std::vector<CSigShare> msgs;
1156 0 : for (auto& sigShare : lt->second) {
1157 0 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QSIGSHARE signHash=%s, node=%d\n",
1158 : sigShare.GetSignHash().ToString(), pnode->GetId());
1159 0 : msgs.emplace_back(std::move(sigShare));
1160 0 : 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 0 : if (!msgs.empty()) {
1167 0 : m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSHARE, msgs));
1168 0 : didSend = true;
1169 0 : }
1170 0 : }
1171 : }
1172 :
1173 0 : return didSend;
1174 0 : }
1175 :
1176 0 : bool CSigSharesManager::GetSessionInfoByRecvId(NodeId nodeId, uint32_t sessionId, CSigSharesNodeState::SessionInfo& retInfo)
1177 : {
1178 0 : LOCK(cs);
1179 0 : return nodeStates[nodeId].GetSessionInfoByRecvId(sessionId, retInfo);
1180 0 : }
1181 :
1182 0 : CSigShare CSigSharesManager::RebuildSigShare(const CSigSharesNodeState::SessionInfo& session, const std::pair<uint16_t, CBLSLazySignature>& in)
1183 : {
1184 0 : const auto& [member, sig] = in;
1185 0 : CSigShare sigShare(session.llmqType, session.quorumHash, session.id, session.msgHash, member, sig);
1186 0 : sigShare.UpdateKey();
1187 0 : return sigShare;
1188 0 : }
1189 :
1190 0 : void CSigSharesManager::Cleanup()
1191 : {
1192 0 : constexpr auto CLEANUP_INTERVAL{5s};
1193 0 : if (!cleanupThrottler.TryCleanup(CLEANUP_INTERVAL)) {
1194 0 : 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 0 : std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher> quorums;
1202 :
1203 : {
1204 0 : LOCK(cs);
1205 0 : sigShares.ForEach([&quorums](const SigShareKey&, const CSigShare& sigShare) {
1206 0 : quorums.try_emplace(std::make_pair(sigShare.getLlmqType(), sigShare.getQuorumHash()), nullptr);
1207 0 : });
1208 0 : }
1209 :
1210 : // Find quorums which became inactive
1211 0 : for (auto it = quorums.begin(); it != quorums.end(); ) {
1212 0 : if (IsQuorumActive(it->first.first, qman, it->first.second)) {
1213 0 : auto quorum = qman.GetQuorum(it->first.first, it->first.second);
1214 0 : if (quorum) {
1215 0 : it->second = quorum;
1216 0 : ++it;
1217 0 : continue;
1218 : }
1219 0 : }
1220 0 : it = quorums.erase(it);
1221 : }
1222 :
1223 : {
1224 : // Now delete sessions which are for inactive quorums
1225 0 : LOCK(cs);
1226 0 : Uint256HashSet inactiveQuorumSessions;
1227 0 : sigShares.ForEach([&quorums, &inactiveQuorumSessions](const SigShareKey&, const CSigShare& sigShare) {
1228 0 : if (quorums.count(std::make_pair(sigShare.getLlmqType(), sigShare.getQuorumHash())) == 0) {
1229 0 : inactiveQuorumSessions.emplace(sigShare.GetSignHash());
1230 0 : }
1231 0 : });
1232 0 : for (const auto& signHash : inactiveQuorumSessions) {
1233 0 : RemoveSigSharesForSession(signHash);
1234 : }
1235 0 : }
1236 :
1237 : {
1238 0 : LOCK(cs);
1239 :
1240 : // Remove sessions which were successfully recovered
1241 0 : Uint256HashSet doneSessions;
1242 0 : sigShares.ForEach([&doneSessions, this](const SigShareKey&, const CSigShare& sigShare) {
1243 0 : if (doneSessions.count(sigShare.GetSignHash()) != 0) {
1244 0 : return;
1245 : }
1246 0 : if (sigman.HasRecoveredSigForSession(sigShare.GetSignHash())) {
1247 0 : doneSessions.emplace(sigShare.GetSignHash());
1248 0 : }
1249 0 : });
1250 0 : for (const auto& signHash : doneSessions) {
1251 0 : RemoveSigSharesForSession(signHash);
1252 : }
1253 :
1254 : // Remove sessions which timed out
1255 0 : Uint256HashSet timeoutSessions;
1256 0 : int64_t now = GetTime<std::chrono::seconds>().count();
1257 0 : for (const auto& [signHash, lastSeenTime] : timeSeenForSessions) {
1258 0 : if (now - lastSeenTime >= SESSION_NEW_SHARES_TIMEOUT) {
1259 0 : timeoutSessions.emplace(signHash);
1260 0 : }
1261 : }
1262 0 : for (const auto& signHash : timeoutSessions) {
1263 :
1264 0 : if (const size_t count = sigShares.CountForSignHash(signHash); count > 0) {
1265 0 : const auto* m = sigShares.GetAllForSignHash(signHash);
1266 0 : assert(m);
1267 :
1268 0 : const auto& oneSigShare = m->begin()->second;
1269 :
1270 0 : std::string strMissingMembers;
1271 0 : if (LogAcceptDebug(BCLog::LLMQ_SIGS)) {
1272 0 : if (const auto quorumIt = quorums.find(std::make_pair(oneSigShare.getLlmqType(), oneSigShare.getQuorumHash())); quorumIt != quorums.end()) {
1273 0 : const auto& quorum = quorumIt->second;
1274 0 : for (const auto i : util::irange(quorum->members.size())) {
1275 0 : if (m->count((uint16_t)i) == 0) {
1276 0 : const auto& dmn = quorum->members[i];
1277 0 : strMissingMembers += strprintf("\n %s", dmn->proTxHash.ToString());
1278 0 : }
1279 : }
1280 0 : }
1281 0 : }
1282 :
1283 0 : 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 0 : } 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 0 : RemoveSigSharesForSession(signHash);
1294 : }
1295 0 : }
1296 :
1297 : // Find node states for peers that disappeared from CConnman
1298 0 : std::unordered_set<NodeId> nodeStatesToDelete;
1299 : {
1300 0 : LOCK(cs);
1301 0 : for (const auto& [nodeId, _] : nodeStates) {
1302 0 : nodeStatesToDelete.emplace(nodeId);
1303 : }
1304 0 : }
1305 0 : m_connman.ForEachNode([&nodeStatesToDelete](const CNode* pnode) { nodeStatesToDelete.erase(pnode->GetId()); });
1306 :
1307 : // Now delete these node states
1308 0 : LOCK(cs);
1309 0 : for (const auto& nodeId : nodeStatesToDelete) {
1310 0 : auto it = nodeStates.find(nodeId);
1311 0 : 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 0 : 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 0 : nodeStates.erase(nodeId);
1322 : }
1323 0 : }
1324 :
1325 0 : void CSigSharesManager::RemoveSigSharesForSession(const uint256& signHash)
1326 : {
1327 0 : AssertLockHeld(cs);
1328 :
1329 0 : for (auto& [_, nodeState] : nodeStates) {
1330 0 : nodeState.RemoveSession(signHash);
1331 : }
1332 :
1333 0 : sigSharesRequested.EraseAllForSignHash(signHash);
1334 0 : sigSharesQueuedToAnnounce.EraseAllForSignHash(signHash);
1335 0 : sigShares.EraseAllForSignHash(signHash);
1336 0 : signedSessions.erase(signHash);
1337 0 : timeSeenForSessions.erase(signHash);
1338 0 : }
1339 :
1340 0 : void CSigSharesManager::RemoveNodesIf(std::function<bool(NodeId)> predicate)
1341 : {
1342 0 : LOCK(cs);
1343 0 : for (auto it = nodeStates.begin(); it != nodeStates.end();) {
1344 0 : 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 0 : 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 0 : it = nodeStates.erase(it);
1353 0 : } else {
1354 0 : ++it;
1355 : }
1356 : }
1357 0 : }
1358 :
1359 0 : void CSigSharesManager::MarkAsBanned(NodeId nodeId)
1360 : {
1361 0 : if (nodeId == -1) {
1362 0 : return;
1363 : }
1364 :
1365 0 : LOCK(cs);
1366 0 : auto it = nodeStates.find(nodeId);
1367 0 : if (it == nodeStates.end()) {
1368 0 : return;
1369 : }
1370 :
1371 0 : 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 0 : 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 0 : nodeState.requestedSigShares.Clear();
1380 0 : nodeState.banned = true;
1381 0 : }
1382 :
1383 0 : std::vector<PendingSignatureData> CSigSharesManager::DispatchPendingSigns()
1384 : {
1385 : // Swap out entire vector to avoid lock thrashing
1386 0 : std::vector<PendingSignatureData> signs;
1387 :
1388 0 : LOCK(cs_pendingSigns);
1389 0 : signs.swap(pendingSigns);
1390 :
1391 0 : return signs;
1392 0 : }
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 0 : std::shared_ptr<CRecoveredSig> CSigSharesManager::SignAndProcessSingleShare(PendingSignatureData work)
1403 : {
1404 0 : auto opt_sigShare = CreateSigShare(*work.quorum, work.id, work.msgHash);
1405 :
1406 0 : if (opt_sigShare.has_value() && opt_sigShare->sigShare.Get().IsValid()) {
1407 0 : auto& sigShare = *opt_sigShare;
1408 0 : auto rs = ProcessSigShare(sigShare, work.quorum);
1409 :
1410 0 : if (IsAllMembersConnectedEnabled(work.quorum->params.type, m_sporkman)) {
1411 0 : LOCK(cs);
1412 0 : auto& session = signedSessions[sigShare.GetSignHash()];
1413 0 : session.sigShare = std::move(sigShare);
1414 0 : session.quorum = work.quorum;
1415 0 : session.nextAttemptTime = 0;
1416 0 : session.attempt = 0;
1417 0 : }
1418 0 : return rs;
1419 0 : }
1420 0 : return nullptr;
1421 0 : }
1422 :
1423 0 : void CSigSharesManager::AsyncSign(CQuorumCPtr quorum, const uint256& id, const uint256& msgHash)
1424 : {
1425 0 : LOCK(cs_pendingSigns);
1426 0 : pendingSigns.emplace_back(std::move(quorum), id, msgHash);
1427 0 : }
1428 :
1429 0 : std::optional<CSigShare> CSigSharesManager::CreateSigShareForSingleMember(const CQuorum& quorum, const uint256& id, const uint256& msgHash) const
1430 : {
1431 0 : cxxtimer::Timer t(true);
1432 0 : auto activeMasterNodeProTxHash = m_mn_activeman.GetProTxHash();
1433 :
1434 0 : int memberIdx = quorum.GetMemberIndex(activeMasterNodeProTxHash);
1435 0 : if (memberIdx == -1) {
1436 : // this should really not happen (IsValidMember gave true)
1437 0 : return std::nullopt;
1438 : }
1439 :
1440 0 : CSigShare sigShare(quorum.params.type, quorum.qc->quorumHash, id, msgHash, uint16_t(memberIdx), {});
1441 0 : 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 0 : auto bls_scheme = bls::bls_legacy_scheme.load();
1446 0 : sigShare.sigShare.Set(m_mn_activeman.Sign(signHash, bls_scheme), bls_scheme);
1447 :
1448 0 : 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 0 : sigShare.UpdateKey();
1456 :
1457 0 : 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 0 : return sigShare;
1464 0 : }
1465 :
1466 0 : std::optional<CSigShare> CSigSharesManager::CreateSigShare(const CQuorum& quorum, const uint256& id, const uint256& msgHash) const
1467 : {
1468 0 : auto activeMasterNodeProTxHash = m_mn_activeman.GetProTxHash();
1469 :
1470 0 : if (!quorum.IsValidMember(activeMasterNodeProTxHash)) {
1471 0 : return std::nullopt;
1472 : }
1473 :
1474 0 : if (quorum.params.is_single_member()) {
1475 0 : return CreateSigShareForSingleMember(quorum, id, msgHash);
1476 : }
1477 0 : cxxtimer::Timer t(true);
1478 0 : const CBLSSecretKey& skShare = quorum.GetSkShare();
1479 0 : if (!skShare.IsValid()) {
1480 0 : LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- we don't have our skShare for quorum %s\n", __func__, quorum.qc->quorumHash.ToString());
1481 0 : return std::nullopt;
1482 : }
1483 :
1484 0 : int memberIdx = quorum.GetMemberIndex(activeMasterNodeProTxHash);
1485 0 : if (memberIdx == -1) {
1486 : // this should really not happen (IsValidMember gave true)
1487 0 : return std::nullopt;
1488 : }
1489 :
1490 0 : CSigShare sigShare(quorum.params.type, quorum.qc->quorumHash, id, msgHash, uint16_t(memberIdx), {});
1491 0 : uint256 signHash = sigShare.buildSignHash().Get();
1492 :
1493 0 : auto bls_scheme = bls::bls_legacy_scheme.load();
1494 0 : sigShare.sigShare.Set(skShare.Sign(signHash, bls_scheme), bls_scheme);
1495 0 : 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 0 : sigShare.UpdateKey();
1502 :
1503 0 : 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 0 : return sigShare;
1507 0 : }
1508 :
1509 : // causes all known sigShares to be re-announced
1510 0 : void CSigSharesManager::ForceReAnnouncement(const CQuorum& quorum, Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash)
1511 : {
1512 0 : if (IsAllMembersConnectedEnabled(llmqType, m_sporkman)) {
1513 0 : return;
1514 : }
1515 :
1516 0 : LOCK(cs);
1517 0 : auto signHash = SignHash(llmqType, quorum.qc->quorumHash, id, msgHash).Get();
1518 0 : if (const auto *const sigs = sigShares.GetAllForSignHash(signHash)) {
1519 0 : for (const auto& [quorumMemberIndex, _] : *sigs) {
1520 : // re-announce every sigshare to every node
1521 0 : sigSharesQueuedToAnnounce.Add(std::make_pair(signHash, quorumMemberIndex), true);
1522 : }
1523 0 : }
1524 0 : for (auto& [_, nodeState] : nodeStates) {
1525 0 : auto* session = nodeState.GetSessionBySignHash(signHash);
1526 0 : if (session == nullptr) {
1527 0 : continue;
1528 : }
1529 : // pretend that the other node doesn't know about any shares so that we re-announce everything
1530 0 : 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 0 : session->sendSessionId = UNINITIALIZED_SESSION_ID;
1533 : }
1534 0 : }
1535 :
1536 0 : RecoveredSigResult CSigSharesManager::HandleNewRecoveredSig(const llmq::CRecoveredSig& recoveredSig)
1537 : {
1538 0 : auto signHash = recoveredSig.buildSignHash().Get();
1539 0 : LOCK(cs);
1540 0 : RemoveSigSharesForSession(signHash);
1541 0 : return std::monostate{};
1542 0 : }
1543 : } // namespace llmq
|