Dash Core Source Documentation (0.16.0.1)

Find detailed information regarding the Dash Core source code.

quorums_signing.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>
6 #include <llmq/quorums_utils.h>
8 
10 #include <bls/bls_batchverifier.h>
11 #include <cxxtimer.hpp>
12 #include <init.h>
13 #include <net_processing.h>
14 #include <netmessagemaker.h>
15 #include <scheduler.h>
16 #include <validation.h>
17 
18 #include <algorithm>
19 #include <limits>
20 #include <unordered_set>
21 
22 namespace llmq
23 {
24 
26 
28 {
30  ret.push_back(Pair("llmqType", (int)llmqType));
31  ret.push_back(Pair("quorumHash", quorumHash.ToString()));
32  ret.push_back(Pair("id", id.ToString()));
33  ret.push_back(Pair("msgHash", msgHash.ToString()));
34  ret.push_back(Pair("sig", sig.Get().ToString()));
35  ret.push_back(Pair("hash", sig.Get().GetHash().ToString()));
36  return ret;
37 }
38 
40  db(_db)
41 {
42  if (Params().NetworkIDString() == CBaseChainParams::TESTNET) {
43  // TODO this can be completely removed after some time (when we're pretty sure the conversion has been run on most testnet MNs)
44  if (db.Exists(std::string("rs_upgraded"))) {
45  return;
46  }
47 
50 
51  db.Write(std::string("rs_upgraded"), (uint8_t)1);
52  }
53 }
54 
55 // This converts time values in "rs_t" from host endiannes to big endiannes, which is required to have proper ordering of the keys
57 {
58  LogPrintf("CRecoveredSigsDb::%s -- converting invalid rs_t keys\n", __func__);
59 
60  std::unique_ptr<CDBIterator> pcursor(db.NewIterator());
61 
62  auto start = std::make_tuple(std::string("rs_t"), (uint32_t)0, (Consensus::LLMQType)0, uint256());
63  pcursor->Seek(start);
64 
65  CDBBatch batch(db);
66  size_t cnt = 0;
67  while (pcursor->Valid()) {
68  decltype(start) k;
69 
70  if (!pcursor->GetKey(k) || std::get<0>(k) != "rs_t") {
71  break;
72  }
73 
74  batch.Erase(k);
75  std::get<1>(k) = htobe32(std::get<1>(k));
76  batch.Write(k, (uint8_t)1);
77 
78  cnt++;
79 
80  pcursor->Next();
81  }
82  pcursor.reset();
83 
84  db.WriteBatch(batch);
85 
86  LogPrintf("CRecoveredSigsDb::%s -- converted %d invalid rs_t keys\n", __func__, cnt);
87 }
88 
89 // This adds rs_vt keys for every rs_v entry to the DB. The time in the key is set to the current time.
90 // This causes cleanup of all these votes a week later.
92 {
93  LogPrintf("CRecoveredSigsDb::%s -- adding rs_vt keys with current time\n", __func__);
94 
95  auto curTime = GetAdjustedTime();
96 
97  std::unique_ptr<CDBIterator> pcursor(db.NewIterator());
98 
99  auto start = std::make_tuple(std::string("rs_v"), (Consensus::LLMQType)0, uint256());
100  pcursor->Seek(start);
101 
102  CDBBatch batch(db);
103  size_t cnt = 0;
104  while (pcursor->Valid()) {
105  decltype(start) k;
106 
107  if (!pcursor->GetKey(k) || std::get<0>(k) != "rs_v") {
108  break;
109  }
110 
111  Consensus::LLMQType llmqType = std::get<1>(k);
112  const uint256& id = std::get<2>(k);
113 
114  auto k2 = std::make_tuple(std::string("rs_vt"), (uint32_t)htobe32(curTime), llmqType, id);
115  batch.Write(k2, (uint8_t)1);
116 
117  cnt++;
118 
119  pcursor->Next();
120  }
121  pcursor.reset();
122 
123  db.WriteBatch(batch);
124 
125  LogPrintf("CRecoveredSigsDb::%s -- added %d rs_vt entries\n", __func__, cnt);
126 }
127 
128 bool CRecoveredSigsDb::HasRecoveredSig(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash)
129 {
130  auto k = std::make_tuple(std::string("rs_r"), llmqType, id, msgHash);
131  return db.Exists(k);
132 }
133 
135 {
136  auto cacheKey = std::make_pair(llmqType, id);
137  bool ret;
138  {
139  LOCK(cs);
140  if (hasSigForIdCache.get(cacheKey, ret)) {
141  return ret;
142  }
143  }
144 
145 
146  auto k = std::make_tuple(std::string("rs_r"), llmqType, id);
147  ret = db.Exists(k);
148 
149  LOCK(cs);
150  hasSigForIdCache.insert(cacheKey, ret);
151  return ret;
152 }
153 
155 {
156  bool ret;
157  {
158  LOCK(cs);
159  if (hasSigForSessionCache.get(signHash, ret)) {
160  return ret;
161  }
162  }
163 
164  auto k = std::make_tuple(std::string("rs_s"), signHash);
165  ret = db.Exists(k);
166 
167  LOCK(cs);
168  hasSigForSessionCache.insert(signHash, ret);
169  return ret;
170 }
171 
173 {
174  bool ret;
175  {
176  LOCK(cs);
177  if (hasSigForHashCache.get(hash, ret)) {
178  return ret;
179  }
180  }
181 
182  auto k = std::make_tuple(std::string("rs_h"), hash);
183  ret = db.Exists(k);
184 
185  LOCK(cs);
186  hasSigForHashCache.insert(hash, ret);
187  return ret;
188 }
189 
191 {
192  auto k = std::make_tuple(std::string("rs_r"), llmqType, id);
193 
195  if (!db.ReadDataStream(k, ds)) {
196  return false;
197  }
198 
199  try {
200  ret.Unserialize(ds);
201  return true;
202  } catch (std::exception&) {
203  return false;
204  }
205 }
206 
208 {
209  auto k1 = std::make_tuple(std::string("rs_h"), hash);
210  std::pair<Consensus::LLMQType, uint256> k2;
211  if (!db.Read(k1, k2)) {
212  return false;
213  }
214 
215  return ReadRecoveredSig(k2.first, k2.second, ret);
216 }
217 
219 {
220  return ReadRecoveredSig(llmqType, id, ret);
221 }
222 
224 {
225  CDBBatch batch(db);
226 
227  uint32_t curTime = GetAdjustedTime();
228 
229  // we put these close to each other to leverage leveldb's key compaction
230  // this way, the second key can be used for fast HasRecoveredSig checks while the first key stores the recSig
231  auto k1 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id);
232  auto k2 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id, recSig.msgHash);
233  batch.Write(k1, recSig);
234  // this key is also used to store the current time, so that we can easily get to the "rs_t" key when we have the id
235  batch.Write(k2, curTime);
236 
237  // store by object hash
238  auto k3 = std::make_tuple(std::string("rs_h"), recSig.GetHash());
239  batch.Write(k3, std::make_pair(recSig.llmqType, recSig.id));
240 
241  // store by signHash
242  auto signHash = CLLMQUtils::BuildSignHash(recSig);
243  auto k4 = std::make_tuple(std::string("rs_s"), signHash);
244  batch.Write(k4, (uint8_t)1);
245 
246  // store by current time. Allows fast cleanup of old recSigs
247  auto k5 = std::make_tuple(std::string("rs_t"), (uint32_t)htobe32(curTime), recSig.llmqType, recSig.id);
248  batch.Write(k5, (uint8_t)1);
249 
250  db.WriteBatch(batch);
251 
252  {
253  int64_t t = GetTimeMillis();
254 
255  LOCK(cs);
256  hasSigForIdCache.insert(std::make_pair((Consensus::LLMQType)recSig.llmqType, recSig.id), true);
257  hasSigForSessionCache.insert(signHash, true);
258  hasSigForHashCache.insert(recSig.GetHash(), true);
259  }
260 }
261 
262 void CRecoveredSigsDb::RemoveRecoveredSig(CDBBatch& batch, Consensus::LLMQType llmqType, const uint256& id, bool deleteHashKey, bool deleteTimeKey)
263 {
265 
266  CRecoveredSig recSig;
267  if (!ReadRecoveredSig(llmqType, id, recSig)) {
268  return;
269  }
270 
271  auto signHash = CLLMQUtils::BuildSignHash(recSig);
272 
273  auto k1 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id);
274  auto k2 = std::make_tuple(std::string("rs_r"), recSig.llmqType, recSig.id, recSig.msgHash);
275  auto k3 = std::make_tuple(std::string("rs_h"), recSig.GetHash());
276  auto k4 = std::make_tuple(std::string("rs_s"), signHash);
277  batch.Erase(k1);
278  batch.Erase(k2);
279  if (deleteHashKey) {
280  batch.Erase(k3);
281  }
282  batch.Erase(k4);
283 
284  if (deleteTimeKey) {
285  CDataStream writeTimeDs(SER_DISK, CLIENT_VERSION);
286  // TODO remove the size() == sizeof(uint32_t) in a future version (when we stop supporting upgrades from < 0.14.1)
287  if (db.ReadDataStream(k2, writeTimeDs) && writeTimeDs.size() == sizeof(uint32_t)) {
288  uint32_t writeTime;
289  writeTimeDs >> writeTime;
290  auto k5 = std::make_tuple(std::string("rs_t"), (uint32_t) htobe32(writeTime), recSig.llmqType, recSig.id);
291  batch.Erase(k5);
292  }
293  }
294 
295  hasSigForIdCache.erase(std::make_pair((Consensus::LLMQType)recSig.llmqType, recSig.id));
296  hasSigForSessionCache.erase(signHash);
297  if (deleteHashKey) {
298  hasSigForHashCache.erase(recSig.GetHash());
299  }
300 }
301 
302 // Completely remove any traces of the recovered sig
304 {
305  LOCK(cs);
306  CDBBatch batch(db);
307  RemoveRecoveredSig(batch, llmqType, id, true, true);
308  db.WriteBatch(batch);
309 }
310 
311 // Remove the recovered sig itself and all keys required to get from id -> recSig
312 // This will leave the byHash key in-place so that HasRecoveredSigForHash still returns true
314 {
315  LOCK(cs);
316  CDBBatch batch(db);
317  RemoveRecoveredSig(batch, llmqType, id, false, false);
318  db.WriteBatch(batch);
319 }
320 
322 {
323  std::unique_ptr<CDBIterator> pcursor(db.NewIterator());
324 
325  auto start = std::make_tuple(std::string("rs_t"), (uint32_t)0, (Consensus::LLMQType)0, uint256());
326  uint32_t endTime = (uint32_t)(GetAdjustedTime() - maxAge);
327  pcursor->Seek(start);
328 
329  std::vector<std::pair<Consensus::LLMQType, uint256>> toDelete;
330  std::vector<decltype(start)> toDelete2;
331 
332  while (pcursor->Valid()) {
333  decltype(start) k;
334 
335  if (!pcursor->GetKey(k) || std::get<0>(k) != "rs_t") {
336  break;
337  }
338  if (be32toh(std::get<1>(k)) >= endTime) {
339  break;
340  }
341 
342  toDelete.emplace_back(std::get<2>(k), std::get<3>(k));
343  toDelete2.emplace_back(k);
344 
345  pcursor->Next();
346  }
347  pcursor.reset();
348 
349  if (toDelete.empty()) {
350  return;
351  }
352 
353  CDBBatch batch(db);
354  {
355  LOCK(cs);
356  for (auto& e : toDelete) {
357  RemoveRecoveredSig(batch, e.first, e.second, true, false);
358 
359  if (batch.SizeEstimate() >= (1 << 24)) {
360  db.WriteBatch(batch);
361  batch.Clear();
362  }
363  }
364  }
365 
366  for (auto& e : toDelete2) {
367  batch.Erase(e);
368  }
369 
370  db.WriteBatch(batch);
371 
372  LogPrint(BCLog::LLMQ, "CRecoveredSigsDb::%d -- deleted %d entries\n", __func__, toDelete.size());
373 }
374 
376 {
377  auto k = std::make_tuple(std::string("rs_v"), llmqType, id);
378  return db.Exists(k);
379 }
380 
382 {
383  auto k = std::make_tuple(std::string("rs_v"), llmqType, id);
384  return db.Read(k, msgHashRet);
385 }
386 
387 void CRecoveredSigsDb::WriteVoteForId(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash)
388 {
389  auto k1 = std::make_tuple(std::string("rs_v"), llmqType, id);
390  auto k2 = std::make_tuple(std::string("rs_vt"), (uint32_t)htobe32(GetAdjustedTime()), llmqType, id);
391 
392  CDBBatch batch(db);
393  batch.Write(k1, msgHash);
394  batch.Write(k2, (uint8_t)1);
395 
396  db.WriteBatch(batch);
397 }
398 
400 {
401  std::unique_ptr<CDBIterator> pcursor(db.NewIterator());
402 
403  auto start = std::make_tuple(std::string("rs_vt"), (uint32_t)0, (Consensus::LLMQType)0, uint256());
404  uint32_t endTime = (uint32_t)(GetAdjustedTime() - maxAge);
405  pcursor->Seek(start);
406 
407  CDBBatch batch(db);
408  size_t cnt = 0;
409  while (pcursor->Valid()) {
410  decltype(start) k;
411 
412  if (!pcursor->GetKey(k) || std::get<0>(k) != "rs_vt") {
413  break;
414  }
415  if (be32toh(std::get<1>(k)) >= endTime) {
416  break;
417  }
418 
419  Consensus::LLMQType llmqType = std::get<2>(k);
420  const uint256& id = std::get<3>(k);
421 
422  batch.Erase(k);
423  batch.Erase(std::make_tuple(std::string("rs_v"), llmqType, id));
424 
425  cnt++;
426 
427  pcursor->Next();
428  }
429  pcursor.reset();
430 
431  if (cnt == 0) {
432  return;
433  }
434 
435  db.WriteBatch(batch);
436 
437  LogPrint(BCLog::LLMQ, "CRecoveredSigsDb::%d -- deleted %d entries\n", __func__, cnt);
438 }
439 
441 
443  db(llmqDb)
444 {
445 }
446 
448 {
449  if (inv.type != MSG_QUORUM_RECOVERED_SIG) {
450  return false;
451  }
452  {
453  LOCK(cs);
454  if (pendingReconstructedRecoveredSigs.count(inv.hash)) {
455  return true;
456  }
457  }
458 
459  return db.HasRecoveredSigForHash(inv.hash);
460 }
461 
463 {
464  if (!db.GetRecoveredSigByHash(hash, ret)) {
465  return false;
466  }
468  // we don't want to propagate sigs from inactive quorums
469  return false;
470  }
471  return true;
472 }
473 
474 void CSigningManager::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
475 {
476  if (strCommand == NetMsgType::QSIGREC) {
477  CRecoveredSig recoveredSig;
478  vRecv >> recoveredSig;
479  ProcessMessageRecoveredSig(pfrom, recoveredSig, connman);
480  }
481 }
482 
483 void CSigningManager::ProcessMessageRecoveredSig(CNode* pfrom, const CRecoveredSig& recoveredSig, CConnman& connman)
484 {
485  bool ban = false;
486  if (!PreVerifyRecoveredSig(pfrom->GetId(), recoveredSig, ban)) {
487  if (ban) {
488  LOCK(cs_main);
489  Misbehaving(pfrom->GetId(), 100);
490  }
491  return;
492  }
493 
494  // It's important to only skip seen *valid* sig shares here. See comment for CBatchedSigShare
495  // We don't receive recovered sigs in batches, but we do batched verification per node on these
496  if (db.HasRecoveredSigForHash(recoveredSig.GetHash())) {
497  return;
498  }
499 
500  LogPrint(BCLog::LLMQ, "CSigningManager::%s -- signHash=%s, id=%s, msgHash=%s, node=%d\n", __func__,
501  CLLMQUtils::BuildSignHash(recoveredSig).ToString(), recoveredSig.id.ToString(), recoveredSig.msgHash.ToString(), pfrom->GetId());
502 
503  LOCK(cs);
504  if (pendingReconstructedRecoveredSigs.count(recoveredSig.GetHash())) {
505  // no need to perform full verification
506  LogPrint(BCLog::LLMQ, "CSigningManager::%s -- already pending reconstructed sig, signHash=%s, id=%s, msgHash=%s, node=%d\n", __func__,
507  CLLMQUtils::BuildSignHash(recoveredSig).ToString(), recoveredSig.id.ToString(), recoveredSig.msgHash.ToString(), pfrom->GetId());
508  return;
509  }
510  pendingRecoveredSigs[pfrom->GetId()].emplace_back(recoveredSig);
511 }
512 
513 bool CSigningManager::PreVerifyRecoveredSig(NodeId nodeId, const CRecoveredSig& recoveredSig, bool& retBan)
514 {
515  retBan = false;
516 
517  auto llmqType = (Consensus::LLMQType)recoveredSig.llmqType;
518  if (!Params().GetConsensus().llmqs.count(llmqType)) {
519  retBan = true;
520  return false;
521  }
522 
523  CQuorumCPtr quorum = quorumManager->GetQuorum(llmqType, recoveredSig.quorumHash);
524 
525  if (!quorum) {
526  LogPrint(BCLog::LLMQ, "CSigningManager::%s -- quorum %s not found, node=%d\n", __func__,
527  recoveredSig.quorumHash.ToString(), nodeId);
528  return false;
529  }
530  if (!CLLMQUtils::IsQuorumActive(llmqType, quorum->qc.quorumHash)) {
531  return false;
532  }
533 
534  return true;
535 }
536 
538  size_t maxUniqueSessions,
539  std::unordered_map<NodeId, std::list<CRecoveredSig>>& retSigShares,
540  std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& retQuorums)
541 {
542  {
543  LOCK(cs);
544  if (pendingRecoveredSigs.empty()) {
545  return;
546  }
547 
548  std::unordered_set<std::pair<NodeId, uint256>, StaticSaltedHasher> uniqueSignHashes;
550  return uniqueSignHashes.size() < maxUniqueSessions;
551  }, [&](NodeId nodeId, std::list<CRecoveredSig>& ns) {
552  if (ns.empty()) {
553  return false;
554  }
555  auto& recSig = *ns.begin();
556 
557  bool alreadyHave = db.HasRecoveredSigForHash(recSig.GetHash());
558  if (!alreadyHave) {
559  uniqueSignHashes.emplace(nodeId, CLLMQUtils::BuildSignHash(recSig));
560  retSigShares[nodeId].emplace_back(recSig);
561  }
562  ns.erase(ns.begin());
563  return !ns.empty();
564  }, rnd);
565 
566  if (retSigShares.empty()) {
567  return;
568  }
569  }
570 
571  for (auto& p : retSigShares) {
572  NodeId nodeId = p.first;
573  auto& v = p.second;
574 
575  for (auto it = v.begin(); it != v.end();) {
576  auto& recSig = *it;
577 
578  Consensus::LLMQType llmqType = (Consensus::LLMQType) recSig.llmqType;
579  auto quorumKey = std::make_pair((Consensus::LLMQType)recSig.llmqType, recSig.quorumHash);
580  if (!retQuorums.count(quorumKey)) {
581  CQuorumCPtr quorum = quorumManager->GetQuorum(llmqType, recSig.quorumHash);
582  if (!quorum) {
583  LogPrint(BCLog::LLMQ, "CSigningManager::%s -- quorum %s not found, node=%d\n", __func__,
584  recSig.quorumHash.ToString(), nodeId);
585  it = v.erase(it);
586  continue;
587  }
588  if (!CLLMQUtils::IsQuorumActive(llmqType, quorum->qc.quorumHash)) {
589  LogPrint(BCLog::LLMQ, "CSigningManager::%s -- quorum %s not active anymore, node=%d\n", __func__,
590  recSig.quorumHash.ToString(), nodeId);
591  it = v.erase(it);
592  continue;
593  }
594 
595  retQuorums.emplace(quorumKey, quorum);
596  }
597 
598  ++it;
599  }
600  }
601 }
602 
604 {
606  {
607  LOCK(cs);
608  m = std::move(pendingReconstructedRecoveredSigs);
609  }
610  for (auto& p : m) {
611  ProcessRecoveredSig(-1, p.second.first, p.second.second, *g_connman);
612  }
613 }
614 
616 {
617  std::unordered_map<NodeId, std::list<CRecoveredSig>> recSigsByNode;
618  std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher> quorums;
619 
621 
622  CollectPendingRecoveredSigsToVerify(32, recSigsByNode, quorums);
623  if (recSigsByNode.empty()) {
624  return false;
625  }
626 
627  // It's ok to perform insecure batched verification here as we verify against the quorum public keys, which are not
628  // craftable by individual entities, making the rogue public key attack impossible
629  CBLSBatchVerifier<NodeId, uint256> batchVerifier(false, false);
630 
631  size_t verifyCount = 0;
632  for (auto& p : recSigsByNode) {
633  NodeId nodeId = p.first;
634  auto& v = p.second;
635 
636  for (auto& recSig : v) {
637  // we didn't verify the lazy signature until now
638  if (!recSig.sig.Get().IsValid()) {
639  batchVerifier.badSources.emplace(nodeId);
640  break;
641  }
642 
643  const auto& quorum = quorums.at(std::make_pair((Consensus::LLMQType)recSig.llmqType, recSig.quorumHash));
644  batchVerifier.PushMessage(nodeId, recSig.GetHash(), CLLMQUtils::BuildSignHash(recSig), recSig.sig.Get(), quorum->qc.quorumPublicKey);
645  verifyCount++;
646  }
647  }
648 
649  cxxtimer::Timer verifyTimer(true);
650  batchVerifier.Verify();
651  verifyTimer.stop();
652 
653  LogPrint(BCLog::LLMQ, "CSigningManager::%s -- verified recovered sig(s). count=%d, vt=%d, nodes=%d\n", __func__, verifyCount, verifyTimer.count(), recSigsByNode.size());
654 
655  std::unordered_set<uint256, StaticSaltedHasher> processed;
656  for (auto& p : recSigsByNode) {
657  NodeId nodeId = p.first;
658  auto& v = p.second;
659 
660  if (batchVerifier.badSources.count(nodeId)) {
661  LOCK(cs_main);
662  LogPrint(BCLog::LLMQ, "CSigningManager::%s -- invalid recSig from other node, banning peer=%d\n", __func__, nodeId);
663  Misbehaving(nodeId, 100);
664  continue;
665  }
666 
667  for (auto& recSig : v) {
668  if (!processed.emplace(recSig.GetHash()).second) {
669  continue;
670  }
671 
672  const auto& quorum = quorums.at(std::make_pair((Consensus::LLMQType)recSig.llmqType, recSig.quorumHash));
673  ProcessRecoveredSig(nodeId, recSig, quorum, connman);
674  }
675  }
676 
677  return true;
678 }
679 
680 // signature must be verified already
681 void CSigningManager::ProcessRecoveredSig(NodeId nodeId, const CRecoveredSig& recoveredSig, const CQuorumCPtr& quorum, CConnman& connman)
682 {
683  auto llmqType = (Consensus::LLMQType)recoveredSig.llmqType;
684 
685  {
686  LOCK(cs_main);
687  EraseObjectRequest(nodeId, CInv(MSG_QUORUM_RECOVERED_SIG, recoveredSig.GetHash()));
688  }
689 
690  if (db.HasRecoveredSigForHash(recoveredSig.GetHash())) {
691  return;
692  }
693 
694  std::vector<CRecoveredSigsListener*> listeners;
695  {
696  LOCK(cs);
697  listeners = recoveredSigsListeners;
698 
699  auto signHash = CLLMQUtils::BuildSignHash(recoveredSig);
700 
701  LogPrint(BCLog::LLMQ, "CSigningManager::%s -- valid recSig. signHash=%s, id=%s, msgHash=%s, node=%d\n", __func__,
702  signHash.ToString(), recoveredSig.id.ToString(), recoveredSig.msgHash.ToString(), nodeId);
703 
704  if (db.HasRecoveredSigForId(llmqType, recoveredSig.id)) {
705  CRecoveredSig otherRecoveredSig;
706  if (db.GetRecoveredSigById(llmqType, recoveredSig.id, otherRecoveredSig)) {
707  auto otherSignHash = CLLMQUtils::BuildSignHash(recoveredSig);
708  if (signHash != otherSignHash) {
709  // this should really not happen, as each masternode is participating in only one vote,
710  // even if it's a member of multiple quorums. so a majority is only possible on one quorum and one msgHash per id
711  LogPrintf("CSigningManager::%s -- conflicting recoveredSig for signHash=%s, id=%s, msgHash=%s, otherSignHash=%s\n", __func__,
712  signHash.ToString(), recoveredSig.id.ToString(), recoveredSig.msgHash.ToString(), otherSignHash.ToString());
713  } else {
714  // Looks like we're trying to process a recSig that is already known. This might happen if the same
715  // recSig comes in through regular QRECSIG messages and at the same time through some other message
716  // which allowed to reconstruct a recSig (e.g. ISLOCK). In this case, just bail out.
717  }
718  return;
719  } else {
720  // This case is very unlikely. It can only happen when cleanup caused this specific recSig to vanish
721  // between the HasRecoveredSigForId and GetRecoveredSigById call. If that happens, treat it as if we
722  // never had that recSig
723  }
724  }
725 
726  db.WriteRecoveredSig(recoveredSig);
727 
728  pendingReconstructedRecoveredSigs.erase(recoveredSig.GetHash());
729  }
730 
731  CInv inv(MSG_QUORUM_RECOVERED_SIG, recoveredSig.GetHash());
732  g_connman->ForEachNode([&](CNode* pnode) {
733  if (pnode->nVersion >= LLMQS_PROTO_VERSION && pnode->fSendRecSigs) {
734  pnode->PushInventory(inv);
735  }
736  });
737 
738  for (auto& l : listeners) {
739  l->HandleNewRecoveredSig(recoveredSig);
740  }
741 }
742 
743 void CSigningManager::PushReconstructedRecoveredSig(const llmq::CRecoveredSig& recoveredSig, const llmq::CQuorumCPtr& quorum)
744 {
745  LOCK(cs);
746  pendingReconstructedRecoveredSigs.emplace(std::piecewise_construct,
747  std::forward_as_tuple(recoveredSig.GetHash()),
748  std::forward_as_tuple(recoveredSig, quorum));
749 }
750 
751 void CSigningManager::TruncateRecoveredSig(Consensus::LLMQType llmqType, const uint256& id)
752 {
753  db.TruncateRecoveredSig(llmqType, id);
754 }
755 
756 void CSigningManager::Cleanup()
757 {
758  int64_t now = GetTimeMillis();
759  if (now - lastCleanupTime < 5000) {
760  return;
761  }
762 
763  int64_t maxAge = gArgs.GetArg("-recsigsmaxage", DEFAULT_MAX_RECOVERED_SIGS_AGE);
764 
765  db.CleanupOldRecoveredSigs(maxAge);
766  db.CleanupOldVotes(maxAge);
767 
768  lastCleanupTime = GetTimeMillis();
769 }
770 
771 void CSigningManager::RegisterRecoveredSigsListener(CRecoveredSigsListener* l)
772 {
773  LOCK(cs);
774  recoveredSigsListeners.emplace_back(l);
775 }
776 
777 void CSigningManager::UnregisterRecoveredSigsListener(CRecoveredSigsListener* l)
778 {
779  LOCK(cs);
780  auto itRem = std::remove(recoveredSigsListeners.begin(), recoveredSigsListeners.end(), l);
781  recoveredSigsListeners.erase(itRem, recoveredSigsListeners.end());
782 }
783 
784 bool CSigningManager::AsyncSignIfMember(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash, bool allowReSign)
785 {
786  auto& params = Params().GetConsensus().llmqs.at(llmqType);
787 
789  return false;
790  }
791 
792  {
793  LOCK(cs);
794 
795  bool hasVoted = db.HasVotedOnId(llmqType, id);
796  if (hasVoted) {
797  uint256 prevMsgHash;
798  db.GetVoteForId(llmqType, id, prevMsgHash);
799  if (msgHash != prevMsgHash) {
800  LogPrintf("CSigningManager::%s -- already voted for id=%s and msgHash=%s. Not voting on conflicting msgHash=%s\n", __func__,
801  id.ToString(), prevMsgHash.ToString(), msgHash.ToString());
802  return false;
803  } else if (allowReSign) {
804  LogPrint(BCLog::LLMQ, "CSigningManager::%s -- already voted for id=%s and msgHash=%s. Resigning!\n", __func__,
805  id.ToString(), prevMsgHash.ToString());
806  } else {
807  LogPrint(BCLog::LLMQ, "CSigningManager::%s -- already voted for id=%s and msgHash=%s. Not voting again.\n", __func__,
808  id.ToString(), prevMsgHash.ToString());
809  return false;
810  }
811  }
812 
813  if (db.HasRecoveredSigForId(llmqType, id)) {
814  // no need to sign it if we already have a recovered sig
815  return true;
816  }
817  if (!hasVoted) {
818  db.WriteVoteForId(llmqType, id, msgHash);
819  }
820  }
821 
822  // This might end up giving different results on different members
823  // This might happen when we are on the brink of confirming a new quorum
824  // This gives a slight risk of not getting enough shares to recover a signature
825  // But at least it shouldn't be possible to get conflicting recovered signatures
826  // TODO fix this by re-signing when the next block arrives, but only when that block results in a change of the quorum list and no recovered signature has been created in the mean time
827  CQuorumCPtr quorum = SelectQuorumForSigning(llmqType, id);
828  if (!quorum) {
829  LogPrint(BCLog::LLMQ, "CSigningManager::%s -- failed to select quorum. id=%s, msgHash=%s\n", __func__, id.ToString(), msgHash.ToString());
830  return false;
831  }
832 
833  if (!quorum->IsValidMember(activeMasternodeInfo.proTxHash)) {
834  return false;
835  }
836 
837  if (allowReSign) {
838  // make us re-announce all known shares (other nodes might have run into a timeout)
839  quorumSigSharesManager->ForceReAnnouncement(quorum, llmqType, id, msgHash);
840  }
841  quorumSigSharesManager->AsyncSign(quorum, id, msgHash);
842 
843  return true;
844 }
845 
846 bool CSigningManager::HasRecoveredSig(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash)
847 {
848  return db.HasRecoveredSig(llmqType, id, msgHash);
849 }
850 
851 bool CSigningManager::HasRecoveredSigForId(Consensus::LLMQType llmqType, const uint256& id)
852 {
853  return db.HasRecoveredSigForId(llmqType, id);
854 }
855 
856 bool CSigningManager::HasRecoveredSigForSession(const uint256& signHash)
857 {
858  return db.HasRecoveredSigForSession(signHash);
859 }
860 
861 bool CSigningManager::GetRecoveredSigForId(Consensus::LLMQType llmqType, const uint256& id, llmq::CRecoveredSig& retRecSig)
862 {
863  if (!db.GetRecoveredSigById(llmqType, id, retRecSig)) {
864  return false;
865  }
866  return true;
867 }
868 
869 bool CSigningManager::IsConflicting(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash)
870 {
871  if (!db.HasRecoveredSigForId(llmqType, id)) {
872  // no recovered sig present, so no conflict
873  return false;
874  }
875 
876  if (!db.HasRecoveredSig(llmqType, id, msgHash)) {
877  // recovered sig is present, but not for the given msgHash. That's a conflict!
878  return true;
879  }
880 
881  // all good
882  return false;
883 }
884 
885 bool CSigningManager::HasVotedOnId(Consensus::LLMQType llmqType, const uint256& id)
886 {
887  return db.HasVotedOnId(llmqType, id);
888 }
889 
890 bool CSigningManager::GetVoteForId(Consensus::LLMQType llmqType, const uint256& id, uint256& msgHashRet)
891 {
892  return db.GetVoteForId(llmqType, id, msgHashRet);
893 }
894 
895 CQuorumCPtr CSigningManager::SelectQuorumForSigning(Consensus::LLMQType llmqType, const uint256& selectionHash, int signHeight, int signOffset)
896 {
897  auto& llmqParams = Params().GetConsensus().llmqs.at(llmqType);
898  size_t poolSize = (size_t)llmqParams.signingActiveQuorumCount;
899 
900  CBlockIndex* pindexStart;
901  {
902  LOCK(cs_main);
903  if (signHeight == -1) {
904  signHeight = chainActive.Height();
905  }
906  int startBlockHeight = signHeight - signOffset;
907  if (startBlockHeight > chainActive.Height()) {
908  return {};
909  }
910  pindexStart = chainActive[startBlockHeight];
911  }
912 
913  auto quorums = quorumManager->ScanQuorums(llmqType, pindexStart, poolSize);
914  if (quorums.empty()) {
915  return nullptr;
916  }
917 
918  std::vector<std::pair<uint256, size_t>> scores;
919  scores.reserve(quorums.size());
920  for (size_t i = 0; i < quorums.size(); i++) {
921  CHashWriter h(SER_NETWORK, 0);
922  h << llmqType;
923  h << quorums[i]->qc.quorumHash;
924  h << selectionHash;
925  scores.emplace_back(h.GetHash(), i);
926  }
927  std::sort(scores.begin(), scores.end());
928  return quorums[scores.front().second];
929 }
930 
931 bool CSigningManager::VerifyRecoveredSig(Consensus::LLMQType llmqType, int signedAtHeight, const uint256& id, const uint256& msgHash, const CBLSSignature& sig)
932 {
933  auto quorum = SelectQuorumForSigning(llmqType, id, signedAtHeight);
934  if (!quorum) {
935  return false;
936  }
937 
938  uint256 signHash = CLLMQUtils::BuildSignHash(llmqType, quorum->qc.quorumHash, id, msgHash);
939  return sig.VerifyInsecure(quorum->qc.quorumPublicKey, signHash);
940 }
941 
942 } // namespace llmq
bool Exists(const K &key) const
Definition: dbwrapper.h:306
void PushMessage(const SourceId &sourceId, const MessageId &msgId, const uint256 &msgHash, const CBLSSignature &sig, const CBLSPublicKey &pubKey)
void Clear()
Definition: dbwrapper.h:68
CRecoveredSigsDb db
CSigSharesManager * quorumSigSharesManager
void CleanupOldVotes(int64_t maxAge)
UniValue quorum(const JSONRPCRequest &request)
Definition: rpcquorums.cpp:492
static uint256 BuildSignHash(Consensus::LLMQType llmqType, const uint256 &quorumHash, const uint256 &id, const uint256 &msgHash)
const uint256 & GetHash() const
Definition: bls.h:147
Batch of changes queued to be written to a CDBWrapper.
Definition: dbwrapper.h:49
void ProcessRecoveredSig(NodeId nodeId, const CRecoveredSig &recoveredSig, const CQuorumCPtr &quorum, CConnman &connman)
void ProcessMessage(CNode *pnode, const std::string &strCommand, CDataStream &vRecv, CConnman &connman)
void TruncateRecoveredSig(Consensus::LLMQType llmqType, const uint256 &id)
CCriticalSection cs
inv message data
Definition: protocol.h:429
static const unsigned char k2[32]
static bool IsQuorumActive(Consensus::LLMQType llmqType, const uint256 &quorumHash)
void CollectPendingRecoveredSigsToVerify(size_t maxUniqueSessions, std::unordered_map< NodeId, std::list< CRecoveredSig >> &retSigShares, std::unordered_map< std::pair< Consensus::LLMQType, uint256 >, CQuorumCPtr, StaticSaltedHasher > &retQuorums)
void Erase(const K &key)
Definition: dbwrapper.h:104
int Height() const
Return the maximal height in the chain.
Definition: chain.h:484
CCriticalSection cs_main
Definition: validation.cpp:213
CRecoveredSigsDb(CDBWrapper &_db)
bool HasRecoveredSig(Consensus::LLMQType llmqType, const uint256 &id, const uint256 &msgHash)
void ProcessMessageRecoveredSig(CNode *pfrom, const CRecoveredSig &recoveredSig, CConnman &connman)
std::vector< CQuorumCPtr > ScanQuorums(Consensus::LLMQType llmqType, size_t maxCount)
Definition: quorums.cpp:279
bool VerifyInsecure(const CBLSPublicKey &pubKey, const uint256 &hash) const
Definition: bls.cpp:335
bool GetRecoveredSigById(Consensus::LLMQType llmqType, const uint256 &id, CRecoveredSig &ret)
uint32_t be32toh(uint32_t big_endian_32bits)
Definition: endian.h:198
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:103
CBLSLazySignature sig
void erase(const Key &key)
bool ReadRecoveredSig(Consensus::LLMQType llmqType, const uint256 &id, CRecoveredSig &ret)
bool HasRecoveredSigForHash(const uint256 &hash)
bool ReadDataStream(const K &key, CDataStream &ssValue) const
Definition: dbwrapper.h:246
bool ProcessPendingRecoveredSigs(CConnman &connman)
bool IsNull() const
Definition: uint256.h:33
CDBIterator * NewIterator()
Definition: dbwrapper.h:351
CQuorumManager * quorumManager
Definition: quorums.cpp:30
FastRandomContext rnd
bool GetVoteForId(Consensus::LLMQType llmqType, const uint256 &id, uint256 &msgHashRet)
uint32_t htobe32(uint32_t host_32bits)
Definition: endian.h:184
void insert(const Key &key, const Value &v)
static const int LLMQS_PROTO_VERSION
introduction of LLMQs
Definition: version.h:51
void Misbehaving(NodeId pnode, int howmuch, const std::string &message)
Increase a node&#39;s misbehavior score.
bool fMasternodeMode
Definition: util.cpp:93
LLMQType
Definition: params.h:48
std::size_t size_t
Definition: bits.hpp:21
This class works as a stopwatch.
Definition: cxxtimer.hpp:38
bool push_back(const UniValue &val)
Definition: univalue.cpp:110
#define LogPrintf(...)
Definition: util.h:203
CActiveMasternodeInfo activeMasternodeInfo
size_type size() const
Definition: streams.h:194
bool get(const Key &key, Value &value)
std::unordered_map< NodeId, std::list< CRecoveredSig > > pendingRecoveredSigs
unordered_lru_cache< uint256, bool, StaticSaltedHasher, 30000 > hasSigForHashCache
std::string ToString() const
Definition: bls.h:210
#define LOCK(cs)
Definition: sync.h:178
unordered_lru_cache< std::pair< Consensus::LLMQType, uint256 >, bool, StaticSaltedHasher, 30000 > hasSigForIdCache
int type
Definition: protocol.h:455
void ForceReAnnouncement(const CQuorumCPtr &quorum, Consensus::LLMQType llmqType, const uint256 &id, const uint256 &msgHash)
const BLSObject & Get() const
Definition: bls.h:402
const char * QSIGREC
Definition: protocol.cpp:73
uint256 hash
Definition: protocol.h:456
static const unsigned char k1[32]
void Write(const K &key, const V &value)
Definition: dbwrapper.h:75
size_t SizeEstimate() const
Definition: dbwrapper.h:123
int64_t NodeId
Definition: net.h:109
Definition: net.h:136
bool GetRecoveredSigForGetData(const uint256 &hash, CRecoveredSig &ret)
std::string ToString() const
Definition: uint256.cpp:62
NodeId GetId() const
Definition: net.h:973
static std::pair< std::string, UniValue > Pair(const char *cKey, const char *cVal)
Definition: univalue.h:185
bool Read(const K &key, V &value) const
Definition: dbwrapper.h:273
bool GetRecoveredSigByHash(const uint256 &hash, CRecoveredSig &ret)
std::map< LLMQType, LLMQParams > llmqs
Definition: params.h:189
bool HasRecoveredSigForId(Consensus::LLMQType llmqType, const uint256 &id)
void WriteVoteForId(Consensus::LLMQType llmqType, const uint256 &id, const uint256 &msgHash)
std::set< SourceId > badSources
void ProcessPendingReconstructedRecoveredSigs()
std::unordered_map< uint256, std::pair< CRecoveredSig, CQuorumCPtr >, StaticSaltedHasher > pendingReconstructedRecoveredSigs
static void IterateNodesRandom(NodesContainer &nodeStates, Continue &&cont, Callback &&callback, FastRandomContext &rnd)
Definition: quorums_utils.h:46
#define LogPrint(category,...)
Definition: util.h:214
void CleanupOldRecoveredSigs(int64_t maxAge)
256-bit opaque blob.
Definition: uint256.h:123
ArgsManager gArgs
Definition: util.cpp:108
bool Write(const K &key, const V &value, bool fSync=false)
Definition: dbwrapper.h:298
void AsyncSign(const CQuorumCPtr &quorum, const uint256 &id, const uint256 &msgHash)
const uint256 & GetHash() const
void RemoveRecoveredSig(Consensus::LLMQType llmqType, const uint256 &id)
Consensus::LLMQType llmqType
The block chain is a tree shaped structure starting with the genesis block at the root...
Definition: chain.h:170
const CChainParams & Params()
Return the currently selected parameters.
UniValue ToJson() const
bool AlreadyHave(const CInv &inv)
int64_t GetTimeMillis()
Returns the system time (not mockable)
Definition: utiltime.cpp:56
std::shared_ptr< const CQuorum > CQuorumCPtr
Definition: quorums.h:73
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: util.cpp:808
int64_t GetAdjustedTime()
Definition: timedata.cpp:35
std::unique_ptr< CConnman > g_connman
Definition: init.cpp:97
bool HasRecoveredSigForSession(const uint256 &signHash)
std::atomic< int > nVersion
Definition: net.h:838
duration_t::rep count() const
Return the elapsed time.
Definition: cxxtimer.hpp:170
CQuorumCPtr GetQuorum(Consensus::LLMQType llmqType, const uint256 &quorumHash)
Definition: quorums.cpp:337
static const std::string TESTNET
void EraseObjectRequest(CNodeState *nodestate, const CInv &inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
CSigningManager * quorumSigningManager
A writer stream (for serialization) that computes a 256-bit hash.
Definition: hash.h:184
void stop()
Stop/pause the timer.
Definition: cxxtimer.hpp:152
CDBWrapper * llmqDb
bool WriteBatch(CDBBatch &batch, bool fSync=false)
Definition: dbwrapper.cpp:157
bool HasVotedOnId(Consensus::LLMQType llmqType, const uint256 &id)
std::atomic< bool > fSendRecSigs
Definition: net.h:945
Information about a peer.
Definition: net.h:800
const Consensus::Params & GetConsensus() const
Definition: chainparams.h:54
CChain & chainActive
The currently-connected chain of blocks (protected by cs_main).
Definition: validation.cpp:217
AssertLockHeld(g_cs_orphans)
CCriticalSection cs
static const int CLIENT_VERSION
dashd-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:38
CSigningManager(CDBWrapper &llmqDb, bool fMemory)
bool PreVerifyRecoveredSig(NodeId nodeId, const CRecoveredSig &recoveredSig, bool &retBan)
void WriteRecoveredSig(const CRecoveredSig &recSig)
unordered_lru_cache< uint256, bool, StaticSaltedHasher, 30000 > hasSigForSessionCache
Released under the MIT license