Dash Core Source Documentation (0.16.0.1)

Find detailed information regarding the Dash Core source code.

quorums_signing_shares.cpp
Go to the documentation of this file.
1 // Copyright (c) 2018-2019 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/quorums_signing.h>
7 #include <llmq/quorums_utils.h>
8 
10 #include <bls/bls_batchverifier.h>
11 #include <init.h>
12 #include <net_processing.h>
13 #include <netmessagemaker.h>
14 #include <spork.h>
15 #include <validation.h>
16 
17 #include <cxxtimer.hpp>
18 
19 namespace llmq
20 {
21 
23 
25 {
26  key.first = CLLMQUtils::BuildSignHash(*this);
27  key.second = quorumMember;
28 }
29 
30 std::string CSigSesAnn::ToString() const
31 {
32  return strprintf("sessionId=%d, llmqType=%d, quorumHash=%s, id=%s, msgHash=%s",
33  sessionId, llmqType, quorumHash.ToString(), id.ToString(), msgHash.ToString());
34 }
35 
37 {
38  for (size_t i = 0; i < inv.size(); i++) {
39  if (inv2.inv[i]) {
40  inv[i] = inv2.inv[i];
41  }
42  }
43 }
44 
46 {
47  return (size_t)std::count(inv.begin(), inv.end(), true);
48 }
49 
50 std::string CSigSharesInv::ToString() const
51 {
52  std::string str = "(";
53  bool first = true;
54  for (size_t i = 0; i < inv.size(); i++) {
55  if (!inv[i]) {
56  continue;
57  }
58 
59  if (!first) {
60  str += ",";
61  }
62  first = false;
63  str += strprintf("%d", i);
64  }
65  str += ")";
66  return str;
67 }
68 
69 void CSigSharesInv::Init(size_t size)
70 {
71  inv.resize(size, false);
72 }
73 
74 bool CSigSharesInv::IsSet(uint16_t quorumMember) const
75 {
76  assert(quorumMember < inv.size());
77  return inv[quorumMember];
78 }
79 
80 void CSigSharesInv::Set(uint16_t quorumMember, bool v)
81 {
82  assert(quorumMember < inv.size());
83  inv[quorumMember] = v;
84 }
85 
87 {
88  for (size_t i = 0; i < inv.size(); i++) {
89  inv[i] = v;
90  }
91 }
92 
93 std::string CBatchedSigShares::ToInvString() const
94 {
95  CSigSharesInv inv;
96  // 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()
97  inv.Init(400);
98  for (size_t i = 0; i < sigShares.size(); i++) {
99  inv.inv[sigShares[i].first] = true;
100  }
101  return inv.ToString();
102 }
103 
104 template<typename T>
105 static void InitSession(CSigSharesNodeState::Session& s, const uint256& signHash, T& from)
106 {
107  const auto& params = Params().GetConsensus().llmqs.at((Consensus::LLMQType)from.llmqType);
108 
109  s.llmqType = (Consensus::LLMQType)from.llmqType;
110  s.quorumHash = from.quorumHash;
111  s.id = from.id;
112  s.msgHash = from.msgHash;
113  s.signHash = signHash;
114  s.announced.Init((size_t)params.size);
115  s.requested.Init((size_t)params.size);
116  s.knows.Init((size_t)params.size);
117 }
118 
120 {
121  auto& s = sessions[sigShare.GetSignHash()];
122  if (s.announced.inv.empty()) {
123  InitSession(s, sigShare.GetSignHash(), sigShare);
124  }
125  return s;
126 }
127 
129 {
130  auto signHash = CLLMQUtils::BuildSignHash((Consensus::LLMQType)ann.llmqType, ann.quorumHash, ann.id, ann.msgHash);
131  auto& s = sessions[signHash];
132  if (s.announced.inv.empty()) {
133  InitSession(s, signHash, ann);
134  }
135  return s;
136 }
137 
139 {
140  auto it = sessions.find(signHash);
141  if (it == sessions.end()) {
142  return nullptr;
143  }
144  return &it->second;
145 }
146 
148 {
149  auto it = sessionByRecvId.find(sessionId);
150  if (it == sessionByRecvId.end()) {
151  return nullptr;
152  }
153  return it->second;
154 }
155 
157 {
158  auto s = GetSessionByRecvId(sessionId);
159  if (!s) {
160  return false;
161  }
162  retInfo.llmqType = s->llmqType;
163  retInfo.quorumHash = s->quorumHash;
164  retInfo.id = s->id;
165  retInfo.msgHash = s->msgHash;
166  retInfo.signHash = s->signHash;
167  retInfo.quorum = s->quorum;
168 
169  return true;
170 }
171 
173 {
174  auto it = sessions.find(signHash);
175  if (it != sessions.end()) {
176  sessionByRecvId.erase(it->second.recvSessionId);
177  sessions.erase(it);
178  }
180  pendingIncomingSigShares.EraseAllForSignHash(signHash);
181 }
182 
184 
186 {
188 }
189 
191 {
192 }
193 
195 {
196  // can't start new thread if we have one running already
197  if (workThread.joinable()) {
198  assert(false);
199  }
200 
201  workThread = std::thread(&TraceThread<std::function<void()> >,
202  "sigshares",
203  std::function<void()>(std::bind(&CSigSharesManager::WorkThreadMain, this)));
204 }
205 
207 {
208  // make sure to call InterruptWorkerThread() first
209  if (!workInterrupt) {
210  assert(false);
211  }
212 
213  if (workThread.joinable()) {
214  workThread.join();
215  }
216 }
217 
219 {
221 }
222 
224 {
226 }
227 
229 {
230  workInterrupt();
231 }
232 
233 void CSigSharesManager::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
234 {
235  // non-masternodes are not interested in sigshares
237  return;
238  }
239 
241  if (strCommand == NetMsgType::QSIGSHARE) {
242  std::vector<CSigShare> sigShares;
243  vRecv >> sigShares;
244 
245  if (sigShares.size() > MAX_MSGS_SIG_SHARES) {
246  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- too many sigs in QSIGSHARE message. cnt=%d, max=%d, node=%d\n", __func__, sigShares.size(), MAX_MSGS_SIG_SHARES, pfrom->GetId());
247  BanNode(pfrom->GetId());
248  return;
249  }
250 
251  for (auto& sigShare : sigShares) {
252  ProcessMessageSigShare(pfrom->GetId(), sigShare, connman);
253  }
254  }
255  }
256 
257  if (strCommand == NetMsgType::QSIGSESANN) {
258  std::vector<CSigSesAnn> msgs;
259  vRecv >> msgs;
260  if (msgs.size() > MAX_MSGS_CNT_QSIGSESANN) {
261  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- too many announcements in QSIGSESANN message. cnt=%d, max=%d, node=%d\n", __func__, msgs.size(), MAX_MSGS_CNT_QSIGSESANN, pfrom->GetId());
262  BanNode(pfrom->GetId());
263  return;
264  }
265  for (auto& ann : msgs) {
266  if (!ProcessMessageSigSesAnn(pfrom, ann, connman)) {
267  BanNode(pfrom->GetId());
268  return;
269  }
270  }
271  } else if (strCommand == NetMsgType::QSIGSHARESINV) {
272  std::vector<CSigSharesInv> msgs;
273  vRecv >> msgs;
274  if (msgs.size() > MAX_MSGS_CNT_QSIGSHARESINV) {
275  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- too many invs in QSIGSHARESINV message. cnt=%d, max=%d, node=%d\n", __func__, msgs.size(), MAX_MSGS_CNT_QSIGSHARESINV, pfrom->GetId());
276  BanNode(pfrom->GetId());
277  return;
278  }
279  for (auto& inv : msgs) {
280  if (!ProcessMessageSigSharesInv(pfrom, inv, connman)) {
281  BanNode(pfrom->GetId());
282  return;
283  }
284  }
285  } else if (strCommand == NetMsgType::QGETSIGSHARES) {
286  std::vector<CSigSharesInv> msgs;
287  vRecv >> msgs;
288  if (msgs.size() > MAX_MSGS_CNT_QGETSIGSHARES) {
289  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- too many invs in QGETSIGSHARES message. cnt=%d, max=%d, node=%d\n", __func__, msgs.size(), MAX_MSGS_CNT_QGETSIGSHARES, pfrom->GetId());
290  BanNode(pfrom->GetId());
291  return;
292  }
293  for (auto& inv : msgs) {
294  if (!ProcessMessageGetSigShares(pfrom, inv, connman)) {
295  BanNode(pfrom->GetId());
296  return;
297  }
298  }
299  } else if (strCommand == NetMsgType::QBSIGSHARES) {
300  std::vector<CBatchedSigShares> msgs;
301  vRecv >> msgs;
302  size_t totalSigsCount = 0;
303  for (auto& bs : msgs) {
304  totalSigsCount += bs.sigShares.size();
305  }
306  if (totalSigsCount > MAX_MSGS_TOTAL_BATCHED_SIGS) {
307  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- too many sigs in QBSIGSHARES message. cnt=%d, max=%d, node=%d\n", __func__, msgs.size(), MAX_MSGS_TOTAL_BATCHED_SIGS, pfrom->GetId());
308  BanNode(pfrom->GetId());
309  return;
310  }
311  for (auto& bs : msgs) {
312  if (!ProcessMessageBatchedSigShares(pfrom, bs, connman)) {
313  BanNode(pfrom->GetId());
314  return;
315  }
316  }
317  }
318 }
319 
321 {
322  auto llmqType = (Consensus::LLMQType)ann.llmqType;
323  if (!Params().GetConsensus().llmqs.count(llmqType)) {
324  return false;
325  }
326  if (ann.sessionId == (uint32_t)-1 || ann.quorumHash.IsNull() || ann.id.IsNull() || ann.msgHash.IsNull()) {
327  return false;
328  }
329 
330  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- ann={%s}, node=%d\n", __func__, ann.ToString(), pfrom->GetId());
331 
332  auto quorum = quorumManager->GetQuorum(llmqType, ann.quorumHash);
333  if (!quorum) {
334  // TODO should we ban here?
335  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- quorum %s not found, node=%d\n", __func__,
336  ann.quorumHash.ToString(), pfrom->GetId());
337  return true; // let's still try other announcements from the same message
338  }
339 
340  auto signHash = CLLMQUtils::BuildSignHash(llmqType, ann.quorumHash, ann.id, ann.msgHash);
341 
342  LOCK(cs);
343  auto& nodeState = nodeStates[pfrom->GetId()];
344  auto& session = nodeState.GetOrCreateSessionFromAnn(ann);
345  nodeState.sessionByRecvId.erase(session.recvSessionId);
346  nodeState.sessionByRecvId.erase(ann.sessionId);
347  session.recvSessionId = ann.sessionId;
348  session.quorum = quorum;
349  nodeState.sessionByRecvId.emplace(ann.sessionId, &session);
350 
351  return true;
352 }
353 
355 {
356  size_t quorumSize = (size_t)Params().GetConsensus().llmqs.at(llmqType).size;
357 
358  if (inv.inv.size() != quorumSize) {
359  return false;
360  }
361  return true;
362 }
363 
365 {
367  if (!GetSessionInfoByRecvId(pfrom->GetId(), inv.sessionId, sessionInfo)) {
368  return true;
369  }
370 
371  if (!VerifySigSharesInv(pfrom->GetId(), sessionInfo.llmqType, inv)) {
372  return false;
373  }
374 
375  // TODO for PoSe, we should consider propagating shares even if we already have a recovered sig
377  return true;
378  }
379 
380  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- signHash=%s, inv={%s}, node=%d\n", __func__,
381  sessionInfo.signHash.ToString(), inv.ToString(), pfrom->GetId());
382 
383  if (sessionInfo.quorum->quorumVvec == nullptr) {
384  // TODO we should allow to ask other nodes for the quorum vvec if we missed it in the DKG
385  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- we don't have the quorum vvec for %s, not requesting sig shares. node=%d\n", __func__,
386  sessionInfo.quorumHash.ToString(), pfrom->GetId());
387  return true;
388  }
389 
390  LOCK(cs);
391  auto& nodeState = nodeStates[pfrom->GetId()];
392  auto session = nodeState.GetSessionByRecvId(inv.sessionId);
393  if (!session) {
394  return true;
395  }
396  session->announced.Merge(inv);
397  session->knows.Merge(inv);
398  return true;
399 }
400 
402 {
404  if (!GetSessionInfoByRecvId(pfrom->GetId(), inv.sessionId, sessionInfo)) {
405  return true;
406  }
407 
408  if (!VerifySigSharesInv(pfrom->GetId(), sessionInfo.llmqType, inv)) {
409  return false;
410  }
411 
412  // TODO for PoSe, we should consider propagating shares even if we already have a recovered sig
414  return true;
415  }
416 
417  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- signHash=%s, inv={%s}, node=%d\n", __func__,
418  sessionInfo.signHash.ToString(), inv.ToString(), pfrom->GetId());
419 
420  LOCK(cs);
421  auto& nodeState = nodeStates[pfrom->GetId()];
422  auto session = nodeState.GetSessionByRecvId(inv.sessionId);
423  if (!session) {
424  return true;
425  }
426  session->requested.Merge(inv);
427  session->knows.Merge(inv);
428  return true;
429 }
430 
432 {
434  if (!GetSessionInfoByRecvId(pfrom->GetId(), batchedSigShares.sessionId, sessionInfo)) {
435  return true;
436  }
437 
438  bool ban = false;
439  if (!PreVerifyBatchedSigShares(pfrom->GetId(), sessionInfo, batchedSigShares, ban)) {
440  return !ban;
441  }
442 
443  std::vector<CSigShare> sigShares;
444  sigShares.reserve(batchedSigShares.sigShares.size());
445 
446  {
447  LOCK(cs);
448  auto& nodeState = nodeStates[pfrom->GetId()];
449 
450  for (size_t i = 0; i < batchedSigShares.sigShares.size(); i++) {
451  CSigShare sigShare = RebuildSigShare(sessionInfo, batchedSigShares, i);
452  nodeState.requestedSigShares.Erase(sigShare.GetKey());
453 
454  // TODO track invalid sig shares received for PoSe?
455  // It's important to only skip seen *valid* sig shares here. If a node sends us a
456  // batch of mostly valid sig shares with a single invalid one and thus batched
457  // verification fails, we'd skip the valid ones in the future if received from other nodes
458  if (this->sigShares.Has(sigShare.GetKey())) {
459  continue;
460  }
461 
462  // TODO for PoSe, we should consider propagating shares even if we already have a recovered sig
464  continue;
465  }
466 
467  sigShares.emplace_back(sigShare);
468  }
469  }
470 
471  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- signHash=%s, shares=%d, new=%d, inv={%s}, node=%d\n", __func__,
472  sessionInfo.signHash.ToString(), batchedSigShares.sigShares.size(), sigShares.size(), batchedSigShares.ToInvString(), pfrom->GetId());
473 
474  if (sigShares.empty()) {
475  return true;
476  }
477 
478  LOCK(cs);
479  auto& nodeState = nodeStates[pfrom->GetId()];
480  for (auto& s : sigShares) {
481  nodeState.pendingIncomingSigShares.Add(s.GetKey(), s);
482  }
483  return true;
484 }
485 
487 {
488  auto quorum = quorumManager->GetQuorum(sigShare.llmqType, sigShare.quorumHash);
489  if (!quorum) {
490  return;
491  }
492  if (!CLLMQUtils::IsQuorumActive(sigShare.llmqType, quorum->qc.quorumHash)) {
493  // quorum is too old
494  return;
495  }
496  if (!quorum->IsMember(activeMasternodeInfo.proTxHash)) {
497  // we're not a member so we can't verify it (we actually shouldn't have received it)
498  return;
499  }
500  if (quorum->quorumVvec == nullptr) {
501  // TODO we should allow to ask other nodes for the quorum vvec if we missed it in the DKG
502  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- we don't have the quorum vvec for %s, no verification possible. node=%d\n", __func__,
503  quorum->qc.quorumHash.ToString(), fromId);
504  return;
505  }
506 
507  if (sigShare.quorumMember >= quorum->members.size()) {
508  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- quorumMember out of bounds\n", __func__);
509  BanNode(fromId);
510  return;
511  }
512  if (!quorum->qc.validMembers[sigShare.quorumMember]) {
513  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- quorumMember not valid\n", __func__);
514  BanNode(fromId);
515  return;
516  }
517 
518  {
519  LOCK(cs);
520 
521  if (sigShares.Has(sigShare.GetKey())) {
522  return;
523  }
524 
526  return;
527  }
528 
529  auto& nodeState = nodeStates[fromId];
530  nodeState.pendingIncomingSigShares.Add(sigShare.GetKey(), sigShare);
531  }
532 
533  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- signHash=%s, id=%s, msgHash=%s, member=%d, node=%d\n", __func__,
534  sigShare.GetSignHash().ToString(), sigShare.id.ToString(), sigShare.msgHash.ToString(), sigShare.quorumMember, fromId);
535 }
536 
537 bool CSigSharesManager::PreVerifyBatchedSigShares(NodeId nodeId, const CSigSharesNodeState::SessionInfo& session, const CBatchedSigShares& batchedSigShares, bool& retBan)
538 {
539  retBan = false;
540 
541  if (!CLLMQUtils::IsQuorumActive(session.llmqType, session.quorum->qc.quorumHash)) {
542  // quorum is too old
543  return false;
544  }
545  if (!session.quorum->IsMember(activeMasternodeInfo.proTxHash)) {
546  // we're not a member so we can't verify it (we actually shouldn't have received it)
547  return false;
548  }
549  if (session.quorum->quorumVvec == nullptr) {
550  // TODO we should allow to ask other nodes for the quorum vvec if we missed it in the DKG
551  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- we don't have the quorum vvec for %s, no verification possible. node=%d\n", __func__,
552  session.quorumHash.ToString(), nodeId);
553  return false;
554  }
555 
556  std::unordered_set<uint16_t> dupMembers;
557 
558  for (size_t i = 0; i < batchedSigShares.sigShares.size(); i++) {
559  auto quorumMember = batchedSigShares.sigShares[i].first;
560  if (!dupMembers.emplace(quorumMember).second) {
561  retBan = true;
562  return false;
563  }
564 
565  if (quorumMember >= session.quorum->members.size()) {
566  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- quorumMember out of bounds\n", __func__);
567  retBan = true;
568  return false;
569  }
570  if (!session.quorum->qc.validMembers[quorumMember]) {
571  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- quorumMember not valid\n", __func__);
572  retBan = true;
573  return false;
574  }
575  }
576  return true;
577 }
578 
580  size_t maxUniqueSessions,
581  std::unordered_map<NodeId, std::vector<CSigShare>>& retSigShares,
582  std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& retQuorums)
583 {
584  {
585  LOCK(cs);
586  if (nodeStates.empty()) {
587  return;
588  }
589 
590  // This will iterate node states in random order and pick one sig share at a time. This avoids processing
591  // of large batches at once from the same node while other nodes also provided shares. If we wouldn't do this,
592  // other nodes would be able to poison us with a large batch with N-1 valid shares and the last one being
593  // invalid, making batch verification fail and revert to per-share verification, which in turn would slow down
594  // the whole verification process
595 
596  std::unordered_set<std::pair<NodeId, uint256>, StaticSaltedHasher> uniqueSignHashes;
598  return uniqueSignHashes.size() < maxUniqueSessions;
599  }, [&](NodeId nodeId, CSigSharesNodeState& ns) {
600  if (ns.pendingIncomingSigShares.Empty()) {
601  return false;
602  }
603  auto& sigShare = *ns.pendingIncomingSigShares.GetFirst();
604 
605  bool alreadyHave = this->sigShares.Has(sigShare.GetKey());
606  if (!alreadyHave) {
607  uniqueSignHashes.emplace(nodeId, sigShare.GetSignHash());
608  retSigShares[nodeId].emplace_back(sigShare);
609  }
610  ns.pendingIncomingSigShares.Erase(sigShare.GetKey());
611  return !ns.pendingIncomingSigShares.Empty();
612  }, rnd);
613 
614  if (retSigShares.empty()) {
615  return;
616  }
617  }
618 
619  {
620  LOCK(cs_main);
621 
622  // For the convenience of the caller, also build a map of quorumHash -> quorum
623 
624  for (auto& p : retSigShares) {
625  for (auto& sigShare : p.second) {
626  auto llmqType = (Consensus::LLMQType) sigShare.llmqType;
627 
628  auto k = std::make_pair(llmqType, sigShare.quorumHash);
629  if (retQuorums.count(k)) {
630  continue;
631  }
632 
633  CQuorumCPtr quorum = quorumManager->GetQuorum(llmqType, sigShare.quorumHash);
634  assert(quorum != nullptr);
635  retQuorums.emplace(k, quorum);
636  }
637  }
638  }
639 }
640 
642 {
643  std::unordered_map<NodeId, std::vector<CSigShare>> sigSharesByNodes;
644  std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher> quorums;
645 
646  CollectPendingSigSharesToVerify(32, sigSharesByNodes, quorums);
647  if (sigSharesByNodes.empty()) {
648  return false;
649  }
650 
651  // It's ok to perform insecure batched verification here as we verify against the quorum public key shares,
652  // which are not craftable by individual entities, making the rogue public key attack impossible
653  CBLSBatchVerifier<NodeId, SigShareKey> batchVerifier(false, true);
654 
655  cxxtimer::Timer prepareTimer(true);
656  size_t verifyCount = 0;
657  for (auto& p : sigSharesByNodes) {
658  auto nodeId = p.first;
659  auto& v = p.second;
660 
661  for (auto& sigShare : v) {
662  if (quorumSigningManager->HasRecoveredSigForId((Consensus::LLMQType)sigShare.llmqType, sigShare.id)) {
663  continue;
664  }
665 
666  // we didn't check this earlier because we use a lazy BLS signature and tried to avoid doing the expensive
667  // deserialization in the message thread
668  if (!sigShare.sigShare.Get().IsValid()) {
669  BanNode(nodeId);
670  // don't process any additional shares from this node
671  break;
672  }
673 
674  auto quorum = quorums.at(std::make_pair((Consensus::LLMQType)sigShare.llmqType, sigShare.quorumHash));
675  auto pubKeyShare = quorum->GetPubKeyShare(sigShare.quorumMember);
676 
677  if (!pubKeyShare.IsValid()) {
678  // this should really not happen (we already ensured we have the quorum vvec,
679  // so we should also be able to create all pubkey shares)
680  LogPrintf("CSigSharesManager::%s -- pubKeyShare is invalid, which should not be possible here\n", __func__);
681  assert(false);
682  }
683 
684  batchVerifier.PushMessage(nodeId, sigShare.GetKey(), sigShare.GetSignHash(), sigShare.sigShare.Get(), pubKeyShare);
685  verifyCount++;
686  }
687  }
688  prepareTimer.stop();
689 
690  cxxtimer::Timer verifyTimer(true);
691  batchVerifier.Verify();
692  verifyTimer.stop();
693 
694  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- verified sig shares. count=%d, pt=%d, vt=%d, nodes=%d\n", __func__, verifyCount, prepareTimer.count(), verifyTimer.count(), sigSharesByNodes.size());
695 
696  for (auto& p : sigSharesByNodes) {
697  auto nodeId = p.first;
698  auto& v = p.second;
699 
700  if (batchVerifier.badSources.count(nodeId)) {
701  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- invalid sig shares from other node, banning peer=%d\n",
702  __func__, nodeId);
703  // this will also cause re-requesting of the shares that were sent by this node
704  BanNode(nodeId);
705  continue;
706  }
707 
708  ProcessPendingSigSharesFromNode(nodeId, v, quorums, connman);
709  }
710 
711  return true;
712 }
713 
714 // It's ensured that no duplicates are passed to this method
716  const std::vector<CSigShare>& sigShares,
717  const std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& quorums,
718  CConnman& connman)
719 {
720  auto& nodeState = nodeStates[nodeId];
721 
722  cxxtimer::Timer t(true);
723  for (auto& sigShare : sigShares) {
724  auto quorumKey = std::make_pair((Consensus::LLMQType)sigShare.llmqType, sigShare.quorumHash);
725  ProcessSigShare(nodeId, sigShare, connman, quorums.at(quorumKey));
726  }
727  t.stop();
728 
729  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- processed sigShare batch. shares=%d, time=%d, node=%d\n", __func__,
730  sigShares.size(), t.count(), nodeId);
731 }
732 
733 // sig shares are already verified when entering this method
734 void CSigSharesManager::ProcessSigShare(NodeId nodeId, const CSigShare& sigShare, CConnman& connman, const CQuorumCPtr& quorum)
735 {
736  auto llmqType = quorum->params.type;
737 
738  bool canTryRecovery = false;
739 
740  // prepare node set for direct-push in case this is our sig share
741  std::set<NodeId> quorumNodes;
743  if (sigShare.quorumMember == quorum->GetMemberIndex(activeMasternodeInfo.proTxHash)) {
744  quorumNodes = connman.GetMasternodeQuorumNodes((Consensus::LLMQType) sigShare.llmqType, sigShare.quorumHash);
745  }
746  }
747 
748  if (quorumSigningManager->HasRecoveredSigForId(llmqType, sigShare.id)) {
749  return;
750  }
751 
752  {
753  LOCK(cs);
754 
755  if (!sigShares.Add(sigShare.GetKey(), sigShare)) {
756  return;
757  }
759  sigSharesToAnnounce.Add(sigShare.GetKey(), true);
760  }
761 
762  // Update the time we've seen the last sigShare
764 
765  if (!quorumNodes.empty()) {
766  // don't announce and wait for other nodes to request this share and directly send it to them
767  // there is no way the other nodes know about this share as this is the one created on this node
768  for (auto otherNodeId : quorumNodes) {
769  auto& nodeState = nodeStates[otherNodeId];
770  auto& session = nodeState.GetOrCreateSessionFromShare(sigShare);
771  session.quorum = quorum;
772  session.requested.Set(sigShare.quorumMember, true);
773  session.knows.Set(sigShare.quorumMember, true);
774  }
775  }
776 
777  size_t sigShareCount = sigShares.CountForSignHash(sigShare.GetSignHash());
778  if (sigShareCount >= quorum->params.threshold) {
779  canTryRecovery = true;
780  }
781  }
782 
783  if (canTryRecovery) {
784  TryRecoverSig(quorum, sigShare.id, sigShare.msgHash, connman);
785  }
786 }
787 
788 void CSigSharesManager::TryRecoverSig(const CQuorumCPtr& quorum, const uint256& id, const uint256& msgHash, CConnman& connman)
789 {
791  return;
792  }
793 
794  std::vector<CBLSSignature> sigSharesForRecovery;
795  std::vector<CBLSId> idsForRecovery;
796  {
797  LOCK(cs);
798 
799  auto k = std::make_pair(quorum->params.type, id);
800 
801  auto signHash = CLLMQUtils::BuildSignHash(quorum->params.type, quorum->qc.quorumHash, id, msgHash);
802  auto sigShares = this->sigShares.GetAllForSignHash(signHash);
803  if (!sigShares) {
804  return;
805  }
806 
807  sigSharesForRecovery.reserve((size_t) quorum->params.threshold);
808  idsForRecovery.reserve((size_t) quorum->params.threshold);
809  for (auto it = sigShares->begin(); it != sigShares->end() && sigSharesForRecovery.size() < quorum->params.threshold; ++it) {
810  auto& sigShare = it->second;
811  sigSharesForRecovery.emplace_back(sigShare.sigShare.Get());
812  idsForRecovery.emplace_back(CBLSId::FromHash(quorum->members[sigShare.quorumMember]->proTxHash));
813  }
814 
815  // check if we can recover the final signature
816  if (sigSharesForRecovery.size() < quorum->params.threshold) {
817  return;
818  }
819  }
820 
821  // now recover it
822  cxxtimer::Timer t(true);
823  CBLSSignature recoveredSig;
824  if (!recoveredSig.Recover(sigSharesForRecovery, idsForRecovery)) {
825  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- failed to recover signature. id=%s, msgHash=%s, time=%d\n", __func__,
826  id.ToString(), msgHash.ToString(), t.count());
827  return;
828  }
829 
830  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- recovered signature. id=%s, msgHash=%s, time=%d\n", __func__,
831  id.ToString(), msgHash.ToString(), t.count());
832 
833  CRecoveredSig rs;
834  rs.llmqType = quorum->params.type;
835  rs.quorumHash = quorum->qc.quorumHash;
836  rs.id = id;
837  rs.msgHash = msgHash;
838  rs.sig.Set(recoveredSig);
839  rs.UpdateHash();
840 
841  // There should actually be no need to verify the self-recovered signatures as it should always succeed. Let's
842  // however still verify it from time to time, so that we have a chance to catch bugs. We do only this sporadic
843  // verification because this is unbatched and thus slow verification that happens here.
844  if (((recoveredSigsCounter++) % 100) == 0) {
845  auto signHash = CLLMQUtils::BuildSignHash(rs);
846  bool valid = recoveredSig.VerifyInsecure(quorum->qc.quorumPublicKey, signHash);
847  if (!valid) {
848  // this should really not happen as we have verified all signature shares before
849  LogPrintf("CSigSharesManager::%s -- own recovered signature is invalid. id=%s, msgHash=%s\n", __func__,
850  id.ToString(), msgHash.ToString());
851  return;
852  }
853  }
854 
856 }
857 
859 {
860  assert(attempt < quorum->members.size());
861 
862  std::vector<std::pair<uint256, CDeterministicMNCPtr>> v;
863  v.reserve(quorum->members.size());
864  for (const auto& dmn : quorum->members) {
865  auto h = ::SerializeHash(std::make_pair(dmn->proTxHash, id));
866  v.emplace_back(h, dmn);
867  }
868  std::sort(v.begin(), v.end());
869 
870  return v[attempt].second;
871 }
872 
873 void CSigSharesManager::CollectSigSharesToRequest(std::unordered_map<NodeId, std::unordered_map<uint256, CSigSharesInv, StaticSaltedHasher>>& sigSharesToRequest)
874 {
876 
877  int64_t now = GetAdjustedTime();
878  const size_t maxRequestsForNode = 32;
879 
880  // avoid requesting from same nodes all the time
881  std::vector<NodeId> shuffledNodeIds;
882  shuffledNodeIds.reserve(nodeStates.size());
883  for (auto& p : nodeStates) {
884  if (p.second.sessions.empty()) {
885  continue;
886  }
887  shuffledNodeIds.emplace_back(p.first);
888  }
889  std::random_shuffle(shuffledNodeIds.begin(), shuffledNodeIds.end(), rnd);
890 
891  for (auto& nodeId : shuffledNodeIds) {
892  auto& nodeState = nodeStates[nodeId];
893 
894  if (nodeState.banned) {
895  continue;
896  }
897 
898  nodeState.requestedSigShares.EraseIf([&](const SigShareKey& k, int64_t t) {
899  if (now - t >= SIG_SHARE_REQUEST_TIMEOUT) {
900  // timeout while waiting for this one, so retry it with another node
901  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::CollectSigSharesToRequest -- timeout while waiting for %s-%d, node=%d\n",
902  k.first.ToString(), k.second, nodeId);
903  return true;
904  }
905  return false;
906  });
907 
908  decltype(sigSharesToRequest.begin()->second)* invMap = nullptr;
909 
910  for (auto& p2 : nodeState.sessions) {
911  auto& signHash = p2.first;
912  auto& session = p2.second;
913 
914  if (CLLMQUtils::IsAllMembersConnectedEnabled(session.llmqType)) {
915  continue;
916  }
917 
919  continue;
920  }
921 
922  for (size_t i = 0; i < session.announced.inv.size(); i++) {
923  if (!session.announced.inv[i]) {
924  continue;
925  }
926  auto k = std::make_pair(signHash, (uint16_t) i);
927  if (sigShares.Has(k)) {
928  // we already have it
929  session.announced.inv[i] = false;
930  continue;
931  }
932  if (nodeState.requestedSigShares.Size() >= maxRequestsForNode) {
933  // too many pending requests for this node
934  break;
935  }
936  auto p = sigSharesRequested.Get(k);
937  if (p) {
938  if (now - p->second >= SIG_SHARE_REQUEST_TIMEOUT && nodeId != p->first) {
939  // other node timed out, re-request from this node
940  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- other node timeout while waiting for %s-%d, re-request from=%d, node=%d\n", __func__,
941  k.first.ToString(), k.second, nodeId, p->first);
942  } else {
943  continue;
944  }
945  }
946  // if we got this far we should do a request
947 
948  // track when we initiated the request so that we can detect timeouts
949  nodeState.requestedSigShares.Add(k, now);
950 
951  // don't request it from other nodes until a timeout happens
952  auto& r = sigSharesRequested.GetOrAdd(k);
953  r.first = nodeId;
954  r.second = now;
955 
956  if (!invMap) {
957  invMap = &sigSharesToRequest[nodeId];
958  }
959  auto& inv = (*invMap)[signHash];
960  if (inv.inv.empty()) {
961  const auto& params = Params().GetConsensus().llmqs.at((Consensus::LLMQType)session.llmqType);
962  inv.Init((size_t)params.size);
963  }
964  inv.inv[k.second] = true;
965 
966  // dont't request it again from this node
967  session.announced.inv[i] = false;
968  }
969  }
970  }
971 }
972 
973 void CSigSharesManager::CollectSigSharesToSend(std::unordered_map<NodeId, std::unordered_map<uint256, CBatchedSigShares, StaticSaltedHasher>>& sigSharesToSend)
974 {
976 
977  for (auto& p : nodeStates) {
978  auto nodeId = p.first;
979  auto& nodeState = p.second;
980 
981  if (nodeState.banned) {
982  continue;
983  }
984 
985  decltype(sigSharesToSend.begin()->second)* sigSharesToSend2 = nullptr;
986 
987  for (auto& p2 : nodeState.sessions) {
988  auto& signHash = p2.first;
989  auto& session = p2.second;
990 
991  if (CLLMQUtils::IsAllMembersConnectedEnabled(session.llmqType)) {
992  continue;
993  }
994 
996  continue;
997  }
998 
999  CBatchedSigShares batchedSigShares;
1000 
1001  for (size_t i = 0; i < session.requested.inv.size(); i++) {
1002  if (!session.requested.inv[i]) {
1003  continue;
1004  }
1005  session.requested.inv[i] = false;
1006 
1007  auto k = std::make_pair(signHash, (uint16_t)i);
1008  const CSigShare* sigShare = sigShares.Get(k);
1009  if (!sigShare) {
1010  // he requested something we don'have
1011  session.requested.inv[i] = false;
1012  continue;
1013  }
1014 
1015  batchedSigShares.sigShares.emplace_back((uint16_t)i, sigShare->sigShare);
1016  }
1017 
1018  if (!batchedSigShares.sigShares.empty()) {
1019  if (sigSharesToSend2 == nullptr) {
1020  // only create the map if we actually add a batched sig
1021  sigSharesToSend2 = &sigSharesToSend[nodeId];
1022  }
1023  (*sigSharesToSend2).emplace(signHash, std::move(batchedSigShares));
1024  }
1025  }
1026  }
1027 }
1028 
1029 void CSigSharesManager::CollectSigSharesToSendConcentrated(std::unordered_map<NodeId, std::vector<CSigShare>>& sigSharesToSend, const std::vector<CNode*>& vNodes)
1030 {
1031  AssertLockHeld(cs);
1032 
1033  std::unordered_map<uint256, CNode*> proTxToNode;
1034  for (const auto& pnode : vNodes) {
1035  if (pnode->verifiedProRegTxHash.IsNull()) {
1036  continue;
1037  }
1038  proTxToNode.emplace(pnode->verifiedProRegTxHash, pnode);
1039  }
1040 
1041  auto curTime = GetTime<std::chrono::milliseconds>().count();
1042 
1043  for (auto& p : signedSessions) {
1044  if (!CLLMQUtils::IsAllMembersConnectedEnabled(p.second.quorum->params.type)) {
1045  continue;
1046  }
1047 
1048  if (p.second.attempt > p.second.quorum->params.recoveryMembers) {
1049  continue;
1050  }
1051 
1052  if (curTime >= p.second.nextAttemptTime) {
1053  int64_t waitTime = exp2(p.second.attempt) * EXP_SEND_FOR_RECOVERY_TIMEOUT;
1054  waitTime = std::min(MAX_SEND_FOR_RECOVERY_TIMEOUT, waitTime);
1055  p.second.nextAttemptTime = curTime + waitTime;
1056  auto dmn = SelectMemberForRecovery(p.second.quorum, p.second.sigShare.id, p.second.attempt);
1057  p.second.attempt++;
1058 
1059  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- signHash=%s, sending to %s, attempt=%d\n", __func__,
1060  p.second.sigShare.GetSignHash().ToString(), dmn->proTxHash.ToString(), p.second.attempt);
1061 
1062  auto it = proTxToNode.find(dmn->proTxHash);
1063  if (it == proTxToNode.end()) {
1064  continue;
1065  }
1066 
1067  auto& m = sigSharesToSend[it->second->GetId()];
1068  m.emplace_back(p.second.sigShare);
1069  }
1070  }
1071 }
1072 
1073 void CSigSharesManager::CollectSigSharesToAnnounce(std::unordered_map<NodeId, std::unordered_map<uint256, CSigSharesInv, StaticSaltedHasher>>& sigSharesToAnnounce)
1074 {
1075  AssertLockHeld(cs);
1076 
1077  std::unordered_map<std::pair<Consensus::LLMQType, uint256>, std::unordered_set<NodeId>, StaticSaltedHasher> quorumNodesMap;
1078 
1079  this->sigSharesToAnnounce.ForEach([&](const SigShareKey& sigShareKey, bool) {
1080  auto& signHash = sigShareKey.first;
1081  auto quorumMember = sigShareKey.second;
1082  const CSigShare* sigShare = sigShares.Get(sigShareKey);
1083  if (!sigShare) {
1084  return;
1085  }
1086 
1087  // announce to the nodes which we know through the intra-quorum-communication system
1088  auto quorumKey = std::make_pair((Consensus::LLMQType)sigShare->llmqType, sigShare->quorumHash);
1089  auto it = quorumNodesMap.find(quorumKey);
1090  if (it == quorumNodesMap.end()) {
1091  auto nodeIds = g_connman->GetMasternodeQuorumNodes(quorumKey.first, quorumKey.second);
1092  it = quorumNodesMap.emplace(std::piecewise_construct, std::forward_as_tuple(quorumKey), std::forward_as_tuple(nodeIds.begin(), nodeIds.end())).first;
1093  }
1094 
1095  auto& quorumNodes = it->second;
1096 
1097  for (auto& nodeId : quorumNodes) {
1098  auto& nodeState = nodeStates[nodeId];
1099 
1100  if (nodeState.banned) {
1101  continue;
1102  }
1103 
1104  auto& session = nodeState.GetOrCreateSessionFromShare(*sigShare);
1105 
1106  if (session.knows.inv[quorumMember]) {
1107  // he already knows that one
1108  continue;
1109  }
1110 
1111  auto& inv = sigSharesToAnnounce[nodeId][signHash];
1112  if (inv.inv.empty()) {
1113  const auto& params = Params().GetConsensus().llmqs.at((Consensus::LLMQType)sigShare->llmqType);
1114  inv.Init((size_t)params.size);
1115  }
1116  inv.inv[quorumMember] = true;
1117  session.knows.inv[quorumMember] = true;
1118  }
1119  });
1120 
1121  // don't announce these anymore
1122  this->sigSharesToAnnounce.Clear();
1123 }
1124 
1126 {
1127  std::unordered_map<NodeId, std::unordered_map<uint256, CSigSharesInv, StaticSaltedHasher>> sigSharesToRequest;
1128  std::unordered_map<NodeId, std::unordered_map<uint256, CBatchedSigShares, StaticSaltedHasher>> sigShareBatchesToSend;
1129  std::unordered_map<NodeId, std::vector<CSigShare>> sigSharesToSend;
1130  std::unordered_map<NodeId, std::unordered_map<uint256, CSigSharesInv, StaticSaltedHasher>> sigSharesToAnnounce;
1131  std::unordered_map<NodeId, std::vector<CSigSesAnn>> sigSessionAnnouncements;
1132 
1133  auto addSigSesAnnIfNeeded = [&](NodeId nodeId, const uint256& signHash) {
1134  auto& nodeState = nodeStates[nodeId];
1135  auto session = nodeState.GetSessionBySignHash(signHash);
1136  assert(session);
1137  if (session->sendSessionId == (uint32_t)-1) {
1138  session->sendSessionId = nodeState.nextSendSessionId++;
1139 
1140  CSigSesAnn sigSesAnn;
1141  sigSesAnn.sessionId = session->sendSessionId;
1142  sigSesAnn.llmqType = session->llmqType;
1143  sigSesAnn.quorumHash = session->quorumHash;
1144  sigSesAnn.id = session->id;
1145  sigSesAnn.msgHash = session->msgHash;
1146 
1147  sigSessionAnnouncements[nodeId].emplace_back(sigSesAnn);
1148  }
1149  return session->sendSessionId;
1150  };
1151 
1152  std::vector<CNode*> vNodesCopy = g_connman->CopyNodeVector(CConnman::FullyConnectedOnly);
1153 
1154  {
1155  LOCK(cs);
1156  CollectSigSharesToRequest(sigSharesToRequest);
1157  CollectSigSharesToSend(sigShareBatchesToSend);
1159  CollectSigSharesToSendConcentrated(sigSharesToSend, vNodesCopy);
1160 
1161  for (auto& p : sigSharesToRequest) {
1162  for (auto& p2 : p.second) {
1163  p2.second.sessionId = addSigSesAnnIfNeeded(p.first, p2.first);
1164  }
1165  }
1166  for (auto& p : sigShareBatchesToSend) {
1167  for (auto& p2 : p.second) {
1168  p2.second.sessionId = addSigSesAnnIfNeeded(p.first, p2.first);
1169  }
1170  }
1171  for (auto& p : sigSharesToAnnounce) {
1172  for (auto& p2 : p.second) {
1173  p2.second.sessionId = addSigSesAnnIfNeeded(p.first, p2.first);
1174  }
1175  }
1176  }
1177 
1178  bool didSend = false;
1179 
1180  for (auto& pnode : vNodesCopy) {
1181  CNetMsgMaker msgMaker(pnode->GetSendVersion());
1182 
1183  auto it1 = sigSessionAnnouncements.find(pnode->GetId());
1184  if (it1 != sigSessionAnnouncements.end()) {
1185  std::vector<CSigSesAnn> msgs;
1186  msgs.reserve(it1->second.size());
1187  for (auto& sigSesAnn : it1->second) {
1188  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QSIGSESANN signHash=%s, sessionId=%d, node=%d\n",
1189  CLLMQUtils::BuildSignHash(sigSesAnn).ToString(), sigSesAnn.sessionId, pnode->GetId());
1190  msgs.emplace_back(sigSesAnn);
1191  if (msgs.size() == MAX_MSGS_CNT_QSIGSESANN) {
1192  g_connman->PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSESANN, msgs));
1193  msgs.clear();
1194  didSend = true;
1195  }
1196  }
1197  if (!msgs.empty()) {
1198  g_connman->PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSESANN, msgs));
1199  didSend = true;
1200  }
1201  }
1202 
1203  auto it = sigSharesToRequest.find(pnode->GetId());
1204  if (it != sigSharesToRequest.end()) {
1205  std::vector<CSigSharesInv> msgs;
1206  for (auto& p : it->second) {
1207  assert(p.second.CountSet() != 0);
1208  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QGETSIGSHARES signHash=%s, inv={%s}, node=%d\n",
1209  p.first.ToString(), p.second.ToString(), pnode->GetId());
1210  msgs.emplace_back(std::move(p.second));
1211  if (msgs.size() == MAX_MSGS_CNT_QGETSIGSHARES) {
1212  g_connman->PushMessage(pnode, msgMaker.Make(NetMsgType::QGETSIGSHARES, msgs));
1213  msgs.clear();
1214  didSend = true;
1215  }
1216  }
1217  if (!msgs.empty()) {
1218  g_connman->PushMessage(pnode, msgMaker.Make(NetMsgType::QGETSIGSHARES, msgs));
1219  didSend = true;
1220  }
1221  }
1222 
1223  auto jt = sigShareBatchesToSend.find(pnode->GetId());
1224  if (jt != sigShareBatchesToSend.end()) {
1225  size_t totalSigsCount = 0;
1226  std::vector<CBatchedSigShares> msgs;
1227  for (auto& p : jt->second) {
1228  assert(!p.second.sigShares.empty());
1229  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QBSIGSHARES signHash=%s, inv={%s}, node=%d\n",
1230  p.first.ToString(), p.second.ToInvString(), pnode->GetId());
1231  if (totalSigsCount + p.second.sigShares.size() > MAX_MSGS_TOTAL_BATCHED_SIGS) {
1232  g_connman->PushMessage(pnode, msgMaker.Make(NetMsgType::QBSIGSHARES, msgs));
1233  msgs.clear();
1234  totalSigsCount = 0;
1235  didSend = true;
1236  }
1237  totalSigsCount += p.second.sigShares.size();
1238  msgs.emplace_back(std::move(p.second));
1239 
1240  }
1241  if (!msgs.empty()) {
1242  g_connman->PushMessage(pnode, msgMaker.Make(NetMsgType::QBSIGSHARES, std::move(msgs)));
1243  didSend = true;
1244  }
1245  }
1246 
1247  auto kt = sigSharesToAnnounce.find(pnode->GetId());
1248  if (kt != sigSharesToAnnounce.end()) {
1249  std::vector<CSigSharesInv> msgs;
1250  for (auto& p : kt->second) {
1251  assert(p.second.CountSet() != 0);
1252  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QSIGSHARESINV signHash=%s, inv={%s}, node=%d\n",
1253  p.first.ToString(), p.second.ToString(), pnode->GetId());
1254  msgs.emplace_back(std::move(p.second));
1255  if (msgs.size() == MAX_MSGS_CNT_QSIGSHARESINV) {
1256  g_connman->PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSHARESINV, msgs));
1257  msgs.clear();
1258  didSend = true;
1259  }
1260  }
1261  if (!msgs.empty()) {
1262  g_connman->PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSHARESINV, msgs));
1263  didSend = true;
1264  }
1265  }
1266 
1267  auto lt = sigSharesToSend.find(pnode->GetId());
1268  if (lt != sigSharesToSend.end()) {
1269  std::vector<CSigShare> msgs;
1270  for (auto& sigShare : lt->second) {
1271  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QSIGSHARE signHash=%s, node=%d\n",
1272  sigShare.GetSignHash().ToString(), pnode->GetId());
1273  msgs.emplace_back(std::move(sigShare));
1274  if (msgs.size() == MAX_MSGS_SIG_SHARES) {
1275  g_connman->PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSHARE, msgs));
1276  msgs.clear();
1277  didSend = true;
1278  }
1279  }
1280  if (!msgs.empty()) {
1281  g_connman->PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSHARE, msgs));
1282  didSend = true;
1283  }
1284  }
1285  }
1286 
1287  // looped through all nodes, release them
1288  g_connman->ReleaseNodeVector(vNodesCopy);
1289 
1290  return didSend;
1291 }
1292 
1294 {
1295  LOCK(cs);
1296  return nodeStates[nodeId].GetSessionInfoByRecvId(sessionId, retInfo);
1297 }
1298 
1300 {
1301  assert(idx < batchedSigShares.sigShares.size());
1302  auto& s = batchedSigShares.sigShares[idx];
1303  CSigShare sigShare;
1304  sigShare.llmqType = session.llmqType;
1305  sigShare.quorumHash = session.quorumHash;
1306  sigShare.quorumMember = s.first;
1307  sigShare.id = session.id;
1308  sigShare.msgHash = session.msgHash;
1309  sigShare.sigShare = s.second;
1310  sigShare.UpdateKey();
1311  return sigShare;
1312 }
1313 
1315 {
1316  int64_t now = GetAdjustedTime();
1317  if (now - lastCleanupTime < 5) {
1318  return;
1319  }
1320 
1321  // This map is first filled with all quorums found in all sig shares. Then we remove all inactive quorums and
1322  // loop through all sig shares again to find the ones belonging to the inactive quorums. We then delete the
1323  // sessions belonging to the sig shares. At the same time, we use this map as a cache when we later need to resolve
1324  // quorumHash -> quorumPtr (as GetQuorum() requires cs_main, leading to deadlocks with cs held)
1325  std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher> quorums;
1326 
1327  {
1328  LOCK(cs);
1329  sigShares.ForEach([&](const SigShareKey& k, const CSigShare& sigShare) {
1330  quorums.emplace(std::make_pair((Consensus::LLMQType) sigShare.llmqType, sigShare.quorumHash), nullptr);
1331  });
1332  }
1333 
1334  // Find quorums which became inactive
1335  for (auto it = quorums.begin(); it != quorums.end(); ) {
1336  if (CLLMQUtils::IsQuorumActive(it->first.first, it->first.second)) {
1337  it->second = quorumManager->GetQuorum(it->first.first, it->first.second);
1338  ++it;
1339  } else {
1340  it = quorums.erase(it);
1341  }
1342  }
1343 
1344  {
1345  // Now delete sessions which are for inactive quorums
1346  LOCK(cs);
1347  std::unordered_set<uint256, StaticSaltedHasher> inactiveQuorumSessions;
1348  sigShares.ForEach([&](const SigShareKey& k, const CSigShare& sigShare) {
1349  if (!quorums.count(std::make_pair((Consensus::LLMQType)sigShare.llmqType, sigShare.quorumHash))) {
1350  inactiveQuorumSessions.emplace(sigShare.GetSignHash());
1351  }
1352  });
1353  for (auto& signHash : inactiveQuorumSessions) {
1354  RemoveSigSharesForSession(signHash);
1355  }
1356  }
1357 
1358  {
1359  LOCK(cs);
1360 
1361  // Remove sessions which were succesfully recovered
1362  std::unordered_set<uint256, StaticSaltedHasher> doneSessions;
1363  sigShares.ForEach([&](const SigShareKey& k, const CSigShare& sigShare) {
1364  if (doneSessions.count(sigShare.GetSignHash())) {
1365  return;
1366  }
1368  doneSessions.emplace(sigShare.GetSignHash());
1369  }
1370  });
1371  for (auto& signHash : doneSessions) {
1372  RemoveSigSharesForSession(signHash);
1373  }
1374 
1375  // Remove sessions which timed out
1376  std::unordered_set<uint256, StaticSaltedHasher> timeoutSessions;
1377  for (auto& p : timeSeenForSessions) {
1378  auto& signHash = p.first;
1379  int64_t lastSeenTime = p.second;
1380 
1381  if (now - lastSeenTime >= SESSION_NEW_SHARES_TIMEOUT) {
1382  timeoutSessions.emplace(signHash);
1383  }
1384  }
1385  for (auto& signHash : timeoutSessions) {
1386  size_t count = sigShares.CountForSignHash(signHash);
1387 
1388  if (count > 0) {
1389  auto m = sigShares.GetAllForSignHash(signHash);
1390  assert(m);
1391 
1392  auto& oneSigShare = m->begin()->second;
1393 
1394  std::string strMissingMembers;
1396  auto quorumIt = quorums.find(std::make_pair((Consensus::LLMQType)oneSigShare.llmqType, oneSigShare.quorumHash));
1397  if (quorumIt != quorums.end()) {
1398  auto& quorum = quorumIt->second;
1399  for (size_t i = 0; i < quorum->members.size(); i++) {
1400  if (!m->count((uint16_t)i)) {
1401  auto& dmn = quorum->members[i];
1402  strMissingMembers += strprintf("\n %s", dmn->proTxHash.ToString());
1403  }
1404  }
1405  }
1406  }
1407 
1408  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- signing session timed out. signHash=%s, id=%s, msgHash=%s, sigShareCount=%d, missingMembers=%s\n", __func__,
1409  signHash.ToString(), oneSigShare.id.ToString(), oneSigShare.msgHash.ToString(), count, strMissingMembers);
1410  } else {
1411  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- signing session timed out. signHash=%s, sigShareCount=%d\n", __func__,
1412  signHash.ToString(), count);
1413  }
1414  RemoveSigSharesForSession(signHash);
1415  }
1416  }
1417 
1418  // Find node states for peers that disappeared from CConnman
1419  std::unordered_set<NodeId> nodeStatesToDelete;
1420  for (auto& p : nodeStates) {
1421  nodeStatesToDelete.emplace(p.first);
1422  }
1423  g_connman->ForEachNode([&](CNode* pnode) {
1424  nodeStatesToDelete.erase(pnode->GetId());
1425  });
1426 
1427  // Now delete these node states
1428  LOCK(cs);
1429  for (auto nodeId : nodeStatesToDelete) {
1430  auto& nodeState = nodeStates[nodeId];
1431  // remove global requested state to force a re-request from another node
1432  nodeState.requestedSigShares.ForEach([&](const SigShareKey& k, bool) {
1434  });
1435  nodeStates.erase(nodeId);
1436  }
1437 
1439 }
1440 
1442 {
1443  for (auto& p : nodeStates) {
1444  auto& ns = p.second;
1445  ns.RemoveSession(signHash);
1446  }
1447 
1450  sigShares.EraseAllForSignHash(signHash);
1451  signedSessions.erase(signHash);
1452  timeSeenForSessions.erase(signHash);
1453 }
1454 
1456 {
1457  // Called regularly to cleanup local node states for banned nodes
1458 
1459  LOCK2(cs_main, cs);
1460  std::unordered_set<NodeId> toRemove;
1461  for (auto it = nodeStates.begin(); it != nodeStates.end();) {
1462  if (IsBanned(it->first)) {
1463  // re-request sigshares from other nodes
1464  it->second.requestedSigShares.ForEach([&](const SigShareKey& k, int64_t) {
1466  });
1467  it = nodeStates.erase(it);
1468  } else {
1469  ++it;
1470  }
1471  }
1472 }
1473 
1475 {
1476  if (nodeId == -1) {
1477  return;
1478  }
1479 
1480  {
1481  LOCK(cs_main);
1482  Misbehaving(nodeId, 100);
1483  }
1484 
1485  LOCK(cs);
1486  auto it = nodeStates.find(nodeId);
1487  if (it == nodeStates.end()) {
1488  return;
1489  }
1490  auto& nodeState = it->second;
1491 
1492  // Whatever we requested from him, let's request it from someone else now
1493  nodeState.requestedSigShares.ForEach([&](const SigShareKey& k, int64_t) {
1495  });
1496  nodeState.requestedSigShares.Clear();
1497 
1498  nodeState.banned = true;
1499 }
1500 
1502 {
1503  int64_t lastSendTime = 0;
1504 
1505  while (!workInterrupt) {
1506  if (!quorumSigningManager || !g_connman) {
1507  if (!workInterrupt.sleep_for(std::chrono::milliseconds(100))) {
1508  return;
1509  }
1510  continue;
1511  }
1512 
1513  bool didWork = false;
1514 
1517  didWork |= ProcessPendingSigShares(*g_connman);
1518  didWork |= SignPendingSigShares();
1519 
1520  if (GetTimeMillis() - lastSendTime > 100) {
1521  SendMessages();
1522  lastSendTime = GetTimeMillis();
1523  }
1524 
1525  Cleanup();
1527 
1528  // TODO Wakeup when pending signing is needed?
1529  if (!didWork) {
1530  if (!workInterrupt.sleep_for(std::chrono::milliseconds(100))) {
1531  return;
1532  }
1533  }
1534  }
1535 }
1536 
1537 void CSigSharesManager::AsyncSign(const CQuorumCPtr& quorum, const uint256& id, const uint256& msgHash)
1538 {
1539  LOCK(cs);
1540  pendingSigns.emplace_back(quorum, id, msgHash);
1541 }
1542 
1544 {
1545  std::vector<std::tuple<const CQuorumCPtr, uint256, uint256>> v;
1546  {
1547  LOCK(cs);
1548  v = std::move(pendingSigns);
1549  }
1550 
1551  for (auto& t : v) {
1552  Sign(std::get<0>(t), std::get<1>(t), std::get<2>(t));
1553  }
1554 
1555  return !v.empty();
1556 }
1557 
1558 void CSigSharesManager::Sign(const CQuorumCPtr& quorum, const uint256& id, const uint256& msgHash)
1559 {
1560  cxxtimer::Timer t(true);
1561 
1562  if (!quorum->IsValidMember(activeMasternodeInfo.proTxHash)) {
1563  return;
1564  }
1565 
1566  CBLSSecretKey skShare = quorum->GetSkShare();
1567  if (!skShare.IsValid()) {
1568  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- we don't have our skShare for quorum %s\n", __func__, quorum->qc.quorumHash.ToString());
1569  return;
1570  }
1571 
1572  int memberIdx = quorum->GetMemberIndex(activeMasternodeInfo.proTxHash);
1573  if (memberIdx == -1) {
1574  // this should really not happen (IsValidMember gave true)
1575  return;
1576  }
1577 
1578  CSigShare sigShare;
1579  sigShare.llmqType = quorum->params.type;
1580  sigShare.quorumHash = quorum->qc.quorumHash;
1581  sigShare.id = id;
1582  sigShare.msgHash = msgHash;
1583  sigShare.quorumMember = (uint16_t)memberIdx;
1584  uint256 signHash = CLLMQUtils::BuildSignHash(sigShare);
1585 
1586  sigShare.sigShare.Set(skShare.Sign(signHash));
1587  if (!sigShare.sigShare.Get().IsValid()) {
1588  LogPrintf("CSigSharesManager::%s -- failed to sign sigShare. signHash=%s, id=%s, msgHash=%s, time=%s\n", __func__,
1589  signHash.ToString(), sigShare.id.ToString(), sigShare.msgHash.ToString(), t.count());
1590  return;
1591  }
1592 
1593  sigShare.UpdateKey();
1594 
1595  LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- signed sigShare. signHash=%s, id=%s, msgHash=%s, llmqType=%d, quorum=%s, time=%s\n", __func__,
1596  signHash.ToString(), sigShare.id.ToString(), sigShare.msgHash.ToString(), quorum->params.type, quorum->qc.quorumHash.ToString(), t.count());
1597  ProcessSigShare(-1, sigShare, *g_connman, quorum);
1598 
1600  LOCK(cs);
1601  auto& session = signedSessions[sigShare.GetSignHash()];
1602  session.sigShare = sigShare;
1603  session.quorum = quorum;
1604  session.nextAttemptTime = 0;
1605  session.attempt = 0;
1606  }
1607 }
1608 
1609 // causes all known sigShares to be re-announced
1611 {
1613  return;
1614  }
1615 
1616  LOCK(cs);
1617  auto signHash = CLLMQUtils::BuildSignHash(llmqType, quorum->qc.quorumHash, id, msgHash);
1618  auto sigs = sigShares.GetAllForSignHash(signHash);
1619  if (sigs) {
1620  for (auto& p : *sigs) {
1621  // re-announce every sigshare to every node
1622  sigSharesToAnnounce.Add(std::make_pair(signHash, p.first), true);
1623  }
1624  }
1625  for (auto& p : nodeStates) {
1626  CSigSharesNodeState& nodeState = p.second;
1627  auto session = nodeState.GetSessionBySignHash(signHash);
1628  if (!session) {
1629  continue;
1630  }
1631  // pretend that the other node doesn't know about any shares so that we re-announce everything
1632  session->knows.SetAll(false);
1633  // we need to use a new session id as we don't know if the other node has run into a timeout already
1634  session->sendSessionId = (uint32_t)-1;
1635  }
1636 }
1637 
1639 {
1640  LOCK(cs);
1642 }
1643 
1644 } // namespace llmq
bool ProcessMessageBatchedSigShares(CNode *pfrom, const CBatchedSigShares &batchedSigShares, CConnman &connman)
void PushMessage(const SourceId &sourceId, const MessageId &msgId, const uint256 &msgHash, const CBLSSignature &sig, const CBLSPublicKey &pubKey)
std::string ToString() const
void Set(uint16_t quorumMember, bool v)
const char * QBSIGSHARES
Definition: protocol.cpp:72
bool sleep_for(std::chrono::milliseconds rel_time)
std::unordered_map< uint32_t, Session * > sessionByRecvId
CSigSharesManager * quorumSigSharesManager
UniValue quorum(const JSONRPCRequest &request)
Definition: rpcquorums.cpp:492
static uint256 BuildSignHash(Consensus::LLMQType llmqType, const uint256 &quorumHash, const uint256 &id, const uint256 &msgHash)
void TraceThread(const std::string name, Callable func)
Definition: util.h:436
bool ProcessMessageGetSigShares(CNode *pfrom, const CSigSharesInv &inv, CConnman &connman)
static const int64_t SESSION_NEW_SHARES_TIMEOUT
Session * GetSessionBySignHash(const uint256 &signHash)
void ProcessRecoveredSig(NodeId nodeId, const CRecoveredSig &recoveredSig, const CQuorumCPtr &quorum, CConnman &connman)
std::set< NodeId > GetMasternodeQuorumNodes(Consensus::LLMQType llmqType, const uint256 &quorumHash) const
Definition: net.cpp:3344
void RemoveSigSharesForSession(const uint256 &signHash)
#define strprintf
Definition: tinyformat.h:1066
const char * QSIGSESANN
Definition: protocol.cpp:69
std::atomic< uint32_t > recoveredSigsCounter
static bool IsQuorumActive(Consensus::LLMQType llmqType, const uint256 &quorumHash)
void Erase(const SigShareKey &k)
bool ProcessPendingSigShares(CConnman &connman)
void RegisterRecoveredSigsListener(CRecoveredSigsListener *l)
CCriticalSection cs_main
Definition: validation.cpp:213
void RemoveSession(const uint256 &signHash)
Session & GetOrCreateSessionFromShare(const CSigShare &sigShare)
bool ProcessMessageSigSharesInv(CNode *pfrom, const CSigSharesInv &inv, CConnman &connman)
std::vector< bool > inv
bool VerifyInsecure(const CBLSPublicKey &pubKey, const uint256 &hash) const
Definition: bls.cpp:335
Consensus::LLMQType llmqType
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:103
std::shared_ptr< const CDeterministicMN > CDeterministicMNCPtr
const char * QSIGSHARE
Definition: protocol.cpp:74
void CollectSigSharesToAnnounce(std::unordered_map< NodeId, std::unordered_map< uint256, CSigSharesInv, StaticSaltedHasher >> &sigSharesToAnnounce)
SigShareMap< bool > sigSharesToAnnounce
static CBLSId FromHash(const uint256 &hash)
Definition: bls.cpp:51
static constexpr const CFullyConnectedOnly FullyConnectedOnly
Definition: net.h:219
bool ProcessPendingRecoveredSigs(CConnman &connman)
bool IsSet(uint16_t quorumMember) const
bool IsNull() const
Definition: uint256.h:33
static bool LogAcceptCategory(uint64_t category)
Definition: util.h:152
SigShareMap< int64_t > requestedSigShares
void UnregisterRecoveredSigsListener(CRecoveredSigsListener *l)
CQuorumManager * quorumManager
Definition: quorums.cpp:30
bool PreVerifyBatchedSigShares(NodeId nodeId, const CSigSharesNodeState::SessionInfo &session, const CBatchedSigShares &batchedSigShares, bool &retBan)
static CDeterministicMNCPtr SelectMemberForRecovery(const CQuorumCPtr &quorum, const uint256 &id, int attempt)
std::unordered_map< uint256, CSignedSession, StaticSaltedHasher > signedSessions
uint256 SerializeHash(const T &obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION)
Compute the 256-bit hash of an object&#39;s serialization.
Definition: hash.h:254
void Misbehaving(NodeId pnode, int howmuch, const std::string &message)
Increase a node&#39;s misbehavior score.
bool fMasternodeMode
Definition: util.cpp:93
SigShareMap< std::pair< NodeId, int64_t > > sigSharesRequested
LLMQType
Definition: params.h:48
#define LOCK2(cs1, cs2)
Definition: sync.h:179
std::size_t size_t
Definition: bits.hpp:21
This class works as a stopwatch.
Definition: cxxtimer.hpp:38
#define LogPrintf(...)
Definition: util.h:203
T * Get(const SigShareKey &k)
CActiveMasternodeInfo activeMasternodeInfo
size_type size() const
Definition: streams.h:194
std::unordered_map< uint256, Session, StaticSaltedHasher > sessions
std::vector< std::tuple< const CQuorumCPtr, uint256, uint256 > > pendingSigns
#define LOCK(cs)
Definition: sync.h:178
void ForceReAnnouncement(const CQuorumCPtr &quorum, Consensus::LLMQType llmqType, const uint256 &id, const uint256 &msgHash)
static void InitSession(CSigSharesNodeState::Session &s, const uint256 &signHash, T &from)
T & GetOrAdd(const SigShareKey &k)
const BLSObject & Get() const
Definition: bls.h:402
void CollectSigSharesToSendConcentrated(std::unordered_map< NodeId, std::vector< CSigShare >> &sigSharesToSend, const std::vector< CNode *> &vNodes)
SigShareMap< CSigShare > pendingIncomingSigShares
bool IsBanned(NodeId pnode)
int64_t NodeId
Definition: net.h:109
Definition: net.h:136
void ProcessMessage(CNode *pnode, const std::string &strCommand, CDataStream &vRecv, CConnman &connman)
std::string ToString() const
Definition: uint256.cpp:62
NodeId GetId() const
Definition: net.h:973
CBLSLazySignature sigShare
std::pair< uint256, uint16_t > SigShareKey
static const int64_t SIG_SHARE_REQUEST_TIMEOUT
std::map< LLMQType, LLMQParams > llmqs
Definition: params.h:189
std::set< SourceId > badSources
void Set(const BLSObject &_obj)
Definition: bls.h:394
void Merge(const CSigSharesInv &inv2)
const char * QSIGSHARESINV
Definition: protocol.cpp:70
SigShareMap< CSigShare > sigShares
const int64_t EXP_SEND_FOR_RECOVERY_TIMEOUT
static void IterateNodesRandom(NodesContainer &nodeStates, Continue &&cont, Callback &&callback, FastRandomContext &rnd)
Definition: quorums_utils.h:46
CSporkManager sporkManager
Definition: spork.cpp:29
#define LogPrint(category,...)
Definition: util.h:214
const uint256 & GetSignHash() const
256-bit opaque blob.
Definition: uint256.h:123
enum VType type() const
Definition: univalue.h:174
const char * QGETSIGSHARES
Definition: protocol.cpp:71
void CollectSigSharesToRequest(std::unordered_map< NodeId, std::unordered_map< uint256, CSigSharesInv, StaticSaltedHasher >> &sigSharesToRequest)
void AsyncSign(const CQuorumCPtr &quorum, const uint256 &id, const uint256 &msgHash)
void ProcessPendingSigSharesFromNode(NodeId nodeId, const std::vector< CSigShare > &sigShares, const std::unordered_map< std::pair< Consensus::LLMQType, uint256 >, CQuorumCPtr, StaticSaltedHasher > &quorums, CConnman &connman)
bool HasRecoveredSigForId(Consensus::LLMQType llmqType, const uint256 &id)
bool HasRecoveredSigForSession(const uint256 &signHash)
bool GetSessionInfoByRecvId(NodeId nodeId, uint32_t sessionId, CSigSharesNodeState::SessionInfo &retInfo)
Consensus::LLMQType llmqType
const CChainParams & Params()
Return the currently selected parameters.
int64_t GetTimeMillis()
Returns the system time (not mockable)
Definition: utiltime.cpp:56
std::shared_ptr< const CQuorum > CQuorumCPtr
Definition: quorums.h:73
int64_t GetAdjustedTime()
Definition: timedata.cpp:35
bool Add(const SigShareKey &k, const T &v)
void TryRecoverSig(const CQuorumCPtr &quorum, const uint256 &id, const uint256 &msgHash, CConnman &connman)
std::unordered_map< uint256, int64_t, StaticSaltedHasher > timeSeenForSessions
std::unique_ptr< CConnman > g_connman
Definition: init.cpp:97
static int count
Definition: tests.c:45
duration_t::rep count() const
Return the elapsed time.
Definition: cxxtimer.hpp:170
bool GetSessionInfoByRecvId(uint32_t sessionId, SessionInfo &retInfo)
CQuorumCPtr GetQuorum(Consensus::LLMQType llmqType, const uint256 &quorumHash)
Definition: quorums.cpp:337
Session * GetSessionByRecvId(uint32_t sessionId)
CSigningManager * quorumSigningManager
void EraseAllForSignHash(const uint256 &signHash)
void stop()
Stop/pause the timer.
Definition: cxxtimer.hpp:152
void ProcessSigShare(NodeId nodeId, const CSigShare &sigShare, CConnman &connman, const CQuorumCPtr &quorum)
Session & GetOrCreateSessionFromAnn(const CSigSesAnn &ann)
std::vector< std::pair< uint16_t, CBLSLazySignature > > sigShares
void HandleNewRecoveredSig(const CRecoveredSig &recoveredSig)
size_t size() const
Definition: univalue.h:69
void CollectSigSharesToSend(std::unordered_map< NodeId, std::unordered_map< uint256, CBatchedSigShares, StaticSaltedHasher >> &sigSharesToSend)
const SigShareKey & GetKey() const
bool IsSporkActive(SporkId nSporkID)
IsSporkActive returns a bool for time-based sporks, and should be used to determine whether the spork...
Definition: spork.cpp:211
Information about a peer.
Definition: net.h:800
const Consensus::Params & GetConsensus() const
Definition: chainparams.h:54
bool Recover(const std::vector< CBLSSignature > &sigs, const std::vector< CBLSId > &ids)
Definition: bls.cpp:393
AssertLockHeld(g_cs_orphans)
CSigShare RebuildSigShare(const CSigSharesNodeState::SessionInfo &session, const CBatchedSigShares &batchedSigShares, size_t idx)
std::string ToString() const
bool ProcessMessageSigSesAnn(CNode *pfrom, const CSigSesAnn &ann, CConnman &connman)
void CollectPendingSigSharesToVerify(size_t maxUniqueSessions, std::unordered_map< NodeId, std::vector< CSigShare >> &retSigShares, std::unordered_map< std::pair< Consensus::LLMQType, uint256 >, CQuorumCPtr, StaticSaltedHasher > &retQuorums)
static bool IsAllMembersConnectedEnabled(Consensus::LLMQType llmqType)
bool IsValid() const
Definition: bls.h:94
Consensus::LLMQType llmqType
std::unordered_map< NodeId, CSigSharesNodeState > nodeStates
const int64_t MAX_SEND_FOR_RECOVERY_TIMEOUT
void ProcessMessageSigShare(NodeId fromId, const CSigShare &sigShare, CConnman &connman)
void Sign(const CQuorumCPtr &quorum, const uint256 &id, const uint256 &msgHash)
CBLSSignature Sign(const uint256 &hash) const
Definition: bls.cpp:160
bool VerifySigSharesInv(NodeId from, Consensus::LLMQType llmqType, const CSigSharesInv &inv)
Released under the MIT license