Bitcoin ABC  0.26.3
P2P Digital Currency
processor.cpp
Go to the documentation of this file.
1 // Copyright (c) 2018-2019 The Bitcoin developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include <avalanche/processor.h>
6 
7 #include <avalanche/avalanche.h>
11 #include <avalanche/validation.h>
12 #include <avalanche/voterecord.h>
13 #include <chain.h>
14 #include <key_io.h> // For DecodeSecret
15 #include <net.h>
16 #include <netmessagemaker.h>
17 #include <reverse_iterator.h>
18 #include <scheduler.h>
19 #include <util/bitmanip.h>
20 #include <util/moneystr.h>
21 #include <util/translation.h>
22 #include <validation.h>
23 
24 #include <chrono>
25 #include <limits>
26 #include <tuple>
27 
31 static constexpr std::chrono::milliseconds AVALANCHE_TIME_STEP{10};
32 
33 // Unfortunately, the bitcoind codebase is full of global and we are kinda
34 // forced into it here.
35 std::unique_ptr<avalanche::Processor> g_avalanche;
36 
37 namespace avalanche {
38 static bool VerifyProof(const Amount &stakeUtxoDustThreshold,
39  const Proof &proof, bilingual_str &error) {
40  ProofValidationState proof_state;
41 
42  if (!proof.verify(stakeUtxoDustThreshold, proof_state)) {
43  switch (proof_state.GetResult()) {
45  error = _("The avalanche proof has no stake.");
46  return false;
48  error = _("The avalanche proof stake is too low.");
49  return false;
51  error = _("The avalanche proof has duplicated stake.");
52  return false;
54  error = _("The avalanche proof has invalid stake signatures.");
55  return false;
57  error = strprintf(
58  _("The avalanche proof has too many utxos (max: %u)."),
60  return false;
61  default:
62  error = _("The avalanche proof is invalid.");
63  return false;
64  }
65  }
66 
67  return true;
68 }
69 
70 static bool VerifyDelegation(const Delegation &dg,
71  const CPubKey &expectedPubKey,
73  DelegationState dg_state;
74 
75  CPubKey auth;
76  if (!dg.verify(dg_state, auth)) {
77  switch (dg_state.GetResult()) {
79  error = _("The avalanche delegation has invalid signatures.");
80  return false;
82  error = _(
83  "The avalanche delegation has too many delegation levels.");
84  return false;
85  default:
86  error = _("The avalanche delegation is invalid.");
87  return false;
88  }
89  }
90 
91  if (auth != expectedPubKey) {
92  error = _(
93  "The avalanche delegation does not match the expected public key.");
94  return false;
95  }
96 
97  return true;
98 }
99 
103 };
104 
108 
109 public:
111 
112  void updatedBlockTip() override {
113  auto registerProofs = [&]() {
115 
116  auto registeredProofs = m_processor->peerManager->updatedBlockTip();
117 
118  if (m_processor->peerData && m_processor->peerData->proof &&
119  m_processor->peerManager->registerProof(
120  m_processor->peerData->proof)) {
121  registeredProofs.insert(m_processor->peerData->proof);
122  }
123 
124  return registeredProofs;
125  };
126 
127  auto registeredProofs = registerProofs();
128  for (const auto &proof : registeredProofs) {
130  }
131  }
132 };
133 
135  CConnman *connmanIn, ChainstateManager &chainmanIn,
136  CScheduler &scheduler,
137  std::unique_ptr<PeerData> peerDataIn, CKey sessionKeyIn,
138  uint32_t minQuorumTotalScoreIn,
139  double minQuorumConnectedScoreRatioIn,
140  int64_t minAvaproofsNodeCountIn,
141  uint32_t staleVoteThresholdIn, uint32_t staleVoteFactorIn,
142  Amount stakeUtxoDustThreshold)
143  : avaconfig(std::move(avaconfigIn)), connman(connmanIn),
144  chainman(chainmanIn), round(0), peerManager(std::make_unique<PeerManager>(
145  stakeUtxoDustThreshold, chainman)),
146  peerData(std::move(peerDataIn)), sessionKey(std::move(sessionKeyIn)),
147  minQuorumScore(minQuorumTotalScoreIn),
148  minQuorumConnectedScoreRatio(minQuorumConnectedScoreRatioIn),
149  minAvaproofsNodeCount(minAvaproofsNodeCountIn),
150  staleVoteThreshold(staleVoteThresholdIn),
151  staleVoteFactor(staleVoteFactorIn) {
152  // Make sure we get notified of chain state changes.
154  chain.handleNotifications(std::make_shared<NotificationsHandler>(this));
155 
156  scheduler.scheduleEvery(
157  [this]() -> bool {
159  peerManager->cleanupDanglingProofs(getLocalProof()));
160  return true;
161  },
162  5min);
163 }
164 
167  stopEventLoop();
168 }
169 
170 std::unique_ptr<Processor>
172  CConnman *connman, ChainstateManager &chainman,
173  CScheduler &scheduler, bilingual_str &error) {
174  std::unique_ptr<PeerData> peerData;
175  CKey masterKey;
177 
178  Amount stakeUtxoDustThreshold = PROOF_DUST_THRESHOLD;
179  if (gArgs.IsArgSet("-avaproofstakeutxodustthreshold") &&
180  !ParseMoney(gArgs.GetArg("-avaproofstakeutxodustthreshold", ""),
181  stakeUtxoDustThreshold)) {
182  error = _("The avalanche stake utxo dust threshold amount is invalid.");
183  return nullptr;
184  }
185 
186  if (argsman.IsArgSet("-avasessionkey")) {
187  sessionKey = DecodeSecret(argsman.GetArg("-avasessionkey", ""));
188  if (!sessionKey.IsValid()) {
189  error = _("The avalanche session key is invalid.");
190  return nullptr;
191  }
192  } else {
193  // Pick a random key for the session.
194  sessionKey.MakeNewKey(true);
195  }
196 
197  if (argsman.IsArgSet("-avaproof")) {
198  if (!argsman.IsArgSet("-avamasterkey")) {
199  error = _(
200  "The avalanche master key is missing for the avalanche proof.");
201  return nullptr;
202  }
203 
204  masterKey = DecodeSecret(argsman.GetArg("-avamasterkey", ""));
205  if (!masterKey.IsValid()) {
206  error = _("The avalanche master key is invalid.");
207  return nullptr;
208  }
209 
210  auto proof = RCUPtr<Proof>::make();
211  if (!Proof::FromHex(*proof, argsman.GetArg("-avaproof", ""), error)) {
212  // error is set by FromHex
213  return nullptr;
214  }
215 
216  peerData = std::make_unique<PeerData>();
217  peerData->proof = std::move(proof);
218  if (!VerifyProof(stakeUtxoDustThreshold, *peerData->proof, error)) {
219  // error is set by VerifyProof
220  return nullptr;
221  }
222 
223  std::unique_ptr<DelegationBuilder> dgb;
224  const CPubKey &masterPubKey = masterKey.GetPubKey();
225 
226  if (argsman.IsArgSet("-avadelegation")) {
227  Delegation dg;
228  if (!Delegation::FromHex(dg, argsman.GetArg("-avadelegation", ""),
229  error)) {
230  // error is set by FromHex()
231  return nullptr;
232  }
233 
234  if (dg.getProofId() != peerData->proof->getId()) {
235  error = _("The delegation does not match the proof.");
236  return nullptr;
237  }
238 
239  if (masterPubKey != dg.getDelegatedPubkey()) {
240  error = _(
241  "The master key does not match the delegation public key.");
242  return nullptr;
243  }
244 
245  dgb = std::make_unique<DelegationBuilder>(dg);
246  } else {
247  if (masterPubKey != peerData->proof->getMaster()) {
248  error =
249  _("The master key does not match the proof public key.");
250  return nullptr;
251  }
252 
253  dgb = std::make_unique<DelegationBuilder>(*peerData->proof);
254  }
255 
256  // Generate the delegation to the session key.
257  const CPubKey sessionPubKey = sessionKey.GetPubKey();
258  if (sessionPubKey != masterPubKey) {
259  if (!dgb->addLevel(masterKey, sessionPubKey)) {
260  error = _("Failed to generate a delegation for this session.");
261  return nullptr;
262  }
263  }
264  peerData->delegation = dgb->build();
265 
266  if (!VerifyDelegation(peerData->delegation, sessionPubKey, error)) {
267  // error is set by VerifyDelegation
268  return nullptr;
269  }
270  }
271 
272  const auto queryTimeoutDuration =
273  std::chrono::milliseconds(argsman.GetIntArg(
274  "-avatimeout", AVALANCHE_DEFAULT_QUERY_TIMEOUT.count()));
275 
276  // Determine quorum parameters
278  if (argsman.IsArgSet("-avaminquorumstake") &&
279  !ParseMoney(argsman.GetArg("-avaminquorumstake", ""), minQuorumStake)) {
280  error = _("The avalanche min quorum stake amount is invalid.");
281  return nullptr;
282  }
283 
284  if (!MoneyRange(minQuorumStake)) {
285  error = _("The avalanche min quorum stake amount is out of range.");
286  return nullptr;
287  }
288 
289  double minQuorumConnectedStakeRatio =
291  if (argsman.IsArgSet("-avaminquorumconnectedstakeratio") &&
292  !ParseDouble(argsman.GetArg("-avaminquorumconnectedstakeratio", ""),
293  &minQuorumConnectedStakeRatio)) {
294  error = _("The avalanche min quorum connected stake ratio is invalid.");
295  return nullptr;
296  }
297 
298  if (minQuorumConnectedStakeRatio < 0 || minQuorumConnectedStakeRatio > 1) {
299  error = _(
300  "The avalanche min quorum connected stake ratio is out of range.");
301  return nullptr;
302  }
303 
304  int64_t minAvaproofsNodeCount =
305  argsman.GetIntArg("-avaminavaproofsnodecount",
307  if (minAvaproofsNodeCount < 0) {
308  error = _("The minimum number of node that sent avaproofs message "
309  "should be non-negative");
310  return nullptr;
311  }
312 
313  // Determine voting parameters
314  int64_t staleVoteThreshold = argsman.GetIntArg(
315  "-avastalevotethreshold", AVALANCHE_VOTE_STALE_THRESHOLD);
317  error = strprintf(_("The avalanche stale vote threshold must be "
318  "greater than or equal to %d"),
320  return nullptr;
321  }
322  if (staleVoteThreshold > std::numeric_limits<uint32_t>::max()) {
323  error = strprintf(_("The avalanche stale vote threshold must be less "
324  "than or equal to %d"),
325  std::numeric_limits<uint32_t>::max());
326  return nullptr;
327  }
328 
329  int64_t staleVoteFactor =
330  argsman.GetIntArg("-avastalevotefactor", AVALANCHE_VOTE_STALE_FACTOR);
331  if (staleVoteFactor <= 0) {
332  error = _("The avalanche stale vote factor must be greater than 0");
333  return nullptr;
334  }
335  if (staleVoteFactor > std::numeric_limits<uint32_t>::max()) {
336  error = strprintf(_("The avalanche stale vote factor must be less than "
337  "or equal to %d"),
338  std::numeric_limits<uint32_t>::max());
339  return nullptr;
340  }
341 
342  Config avaconfig(queryTimeoutDuration);
343 
344  // We can't use std::make_unique with a private constructor
345  return std::unique_ptr<Processor>(new Processor(
346  std::move(avaconfig), chain, connman, chainman, scheduler,
347  std::move(peerData), std::move(sessionKey),
348  Proof::amountToScore(minQuorumStake), minQuorumConnectedStakeRatio,
350  stakeUtxoDustThreshold));
351 }
352 
354  if (!pindex) {
355  // isWorthPolling expects this to be non-null, so bail early.
356  return false;
357  }
358 
359  bool isAccepted;
360  {
361  LOCK(cs_main);
362  if (!isWorthPolling(pindex)) {
363  // There is no point polling this block.
364  return false;
365  }
366 
368  }
369 
371  ->insert(std::make_pair(pindex, VoteRecord(isAccepted)))
372  .second;
373 }
374 
376  if (!proof) {
377  // isWorthPolling expects this to be non-null, so bail early.
378  return false;
379  }
380 
381  bool isAccepted;
382  {
384  if (!isWorthPolling(proof)) {
385  return false;
386  }
387 
388  isAccepted = peerManager->isBoundToPeer(proof->getId());
389  }
390 
392  ->insert(std::make_pair(proof, VoteRecord(isAccepted)))
393  .second;
394 }
395 
396 bool Processor::isAccepted(const CBlockIndex *pindex) const {
397  if (!pindex) {
398  // CBlockIndexWorkComparator expects this to be non-null, so bail early.
399  return false;
400  }
401 
402  auto r = blockVoteRecords.getReadView();
403  auto it = r->find(pindex);
404  if (it == r.end()) {
405  return false;
406  }
407 
408  return it->second.isAccepted();
409 }
410 
411 bool Processor::isAccepted(const ProofRef &proof) const {
412  auto r = proofVoteRecords.getReadView();
413  auto it = r->find(proof);
414  if (it == r.end()) {
415  return false;
416  }
417 
418  return it->second.isAccepted();
419 }
420 
421 int Processor::getConfidence(const CBlockIndex *pindex) const {
422  if (!pindex) {
423  // CBlockIndexWorkComparator expects this to be non-null, so bail early.
424  return -1;
425  }
426 
427  auto r = blockVoteRecords.getReadView();
428  auto it = r->find(pindex);
429  if (it == r.end()) {
430  return -1;
431  }
432 
433  return it->second.getConfidence();
434 }
435 
436 int Processor::getConfidence(const ProofRef &proof) const {
437  auto r = proofVoteRecords.getReadView();
438  auto it = r->find(proof);
439  if (it == r.end()) {
440  return -1;
441  }
442 
443  return it->second.getConfidence();
444 }
445 
446 namespace {
451  class TCPResponse {
452  Response response;
454 
455  public:
456  TCPResponse(Response responseIn, const CKey &key)
457  : response(std::move(responseIn)) {
458  CHashWriter hasher(SER_GETHASH, 0);
459  hasher << response;
460  const uint256 hash = hasher.GetHash();
461 
462  // Now let's sign!
463  if (!key.SignSchnorr(hash, sig)) {
464  sig.fill(0);
465  }
466  }
467 
468  // serialization support
469  SERIALIZE_METHODS(TCPResponse, obj) {
470  READWRITE(obj.response, obj.sig);
471  }
472  };
473 } // namespace
474 
477  pfrom, CNetMsgMaker(pfrom->GetCommonVersion())
479  TCPResponse(std::move(response), sessionKey)));
480 }
481 
483  std::vector<BlockUpdate> &blockUpdates,
484  std::vector<ProofUpdate> &proofUpdates,
485  int &banscore, std::string &error) {
486  {
487  // Save the time at which we can query again.
489 
490  // FIXME: This will override the time even when we received an old stale
491  // message. This should check that the message is indeed the most up to
492  // date one before updating the time.
493  peerManager->updateNextRequestTime(
494  nodeid, std::chrono::steady_clock::now() +
495  std::chrono::milliseconds(response.getCooldown()));
496  }
497 
498  std::vector<CInv> invs;
499 
500  {
501  // Check that the query exists.
502  auto w = queries.getWriteView();
503  auto it = w->find(std::make_tuple(nodeid, response.getRound()));
504  if (it == w.end()) {
505  banscore = 2;
506  error = "unexpected-ava-response";
507  return false;
508  }
509 
510  invs = std::move(it->invs);
511  w->erase(it);
512  }
513 
514  // Verify that the request and the vote are consistent.
515  const std::vector<Vote> &votes = response.GetVotes();
516  size_t size = invs.size();
517  if (votes.size() != size) {
518  banscore = 100;
519  error = "invalid-ava-response-size";
520  return false;
521  }
522 
523  for (size_t i = 0; i < size; i++) {
524  if (invs[i].hash != votes[i].GetHash()) {
525  banscore = 100;
526  error = "invalid-ava-response-content";
527  return false;
528  }
529  }
530 
531  std::map<CBlockIndex *, Vote> responseIndex;
532  std::map<ProofRef, Vote, ProofRefComparatorByAddress> responseProof;
533 
534  // At this stage we are certain that invs[i] matches votes[i], so we can use
535  // the inv type to retrieve what is being voted on.
536  for (size_t i = 0; i < size; i++) {
537  if (invs[i].IsMsgBlk()) {
538  CBlockIndex *pindex;
539  {
540  LOCK(cs_main);
541  pindex = chainman.m_blockman.LookupBlockIndex(
542  BlockHash(votes[i].GetHash()));
543  if (!pindex) {
544  // This should not happen, but just in case...
545  continue;
546  }
547 
548  if (!isWorthPolling(pindex)) {
549  // There is no point polling this block.
550  continue;
551  }
552  }
553 
554  responseIndex.insert(std::make_pair(pindex, votes[i]));
555  }
556 
557  if (invs[i].IsMsgProof()) {
558  const ProofId proofid(votes[i].GetHash());
559 
560  ProofRef proof;
561  {
563  proof = peerManager->getProof(proofid);
564  if (!proof) {
565  continue;
566  }
567 
568  if (!isWorthPolling(proof)) {
569  continue;
570  }
571  }
572 
573  responseProof.insert(std::make_pair(proof, votes[i]));
574  }
575  }
576 
577  // Thanks to C++14 generic lambdas, we can apply the same logic to various
578  // parameter types sharing the same interface.
579  auto registerVoteItems = [&](auto voteRecordsWriteView, auto &updates,
580  auto responseItems) {
581  // Register votes.
582  for (const auto &p : responseItems) {
583  auto item = p.first;
584  const Vote &v = p.second;
585 
586  auto it = voteRecordsWriteView->find(item);
587  if (it == voteRecordsWriteView.end()) {
588  // We are not voting on that item anymore.
589  continue;
590  }
591 
592  auto &vr = it->second;
593  if (!vr.registerVote(nodeid, v.GetError())) {
594  if (vr.isStale(staleVoteThreshold, staleVoteFactor)) {
595  updates.emplace_back(item, VoteStatus::Stale);
596 
597  // Just drop stale votes. If we see this item again, we'll
598  // do a new vote.
599  voteRecordsWriteView->erase(it);
600  }
601  // This vote did not provide any extra information, move on.
602  continue;
603  }
604 
605  if (!vr.hasFinalized()) {
606  // This item has not been finalized, so we have nothing more to
607  // do.
608  updates.emplace_back(item, vr.isAccepted()
611  continue;
612  }
613 
614  // We just finalized a vote. If it is valid, then let the caller
615  // know. Either way, remove the item from the map.
616  updates.emplace_back(item, vr.isAccepted() ? VoteStatus::Finalized
618  voteRecordsWriteView->erase(it);
619  }
620  };
621 
622  registerVoteItems(blockVoteRecords.getWriteView(), blockUpdates,
623  responseIndex);
624  registerVoteItems(proofVoteRecords.getWriteView(), proofUpdates,
625  responseProof);
626 
627  for (const auto &blockUpdate : blockUpdates) {
628  if (blockUpdate.getStatus() != VoteStatus::Finalized) {
629  continue;
630  }
631 
633  CBlockIndex *pindex = blockUpdate.getVoteItem();
634  if (finalizationTip &&
635  finalizationTip->GetAncestor(pindex->nHeight) == pindex) {
636  continue;
637  }
638 
639  finalizationTip = pindex;
640  }
641 
642  return true;
643 }
644 
646  return sessionKey.GetPubKey();
647 }
648 
649 bool Processor::sendHello(CNode *pfrom) const {
650  Delegation delegation;
651  if (peerData) {
652  delegation = peerData->delegation;
653  pfrom->AddKnownProof(delegation.getProofId());
654  }
655 
656  CHashWriter hasher(SER_GETHASH, 0);
657  hasher << delegation.getId();
658  hasher << pfrom->GetLocalNonce();
659  hasher << pfrom->nRemoteHostNonce;
660  hasher << pfrom->GetLocalExtraEntropy();
661  hasher << pfrom->nRemoteExtraEntropy;
662 
663  // Now let's sign!
664  SchnorrSig sig;
665  if (!sessionKey.SignSchnorr(hasher.GetHash(), sig)) {
666  return false;
667  }
668 
670  pfrom, CNetMsgMaker(pfrom->GetCommonVersion())
671  .Make(NetMsgType::AVAHELLO, Hello(delegation, sig)));
672 
673  return true;
674 }
675 
677  return peerData ? peerData->proof : ProofRef();
678 }
679 
681  return eventLoop.startEventLoop(
682  scheduler, [this]() { this->runEventLoop(); }, AVALANCHE_TIME_STEP);
683 }
684 
686  return eventLoop.stopEventLoop();
687 }
688 
691 
693  // Before IBD is complete there is no way to make sure a proof is valid
694  // or not, e.g. it can be spent in a block we don't know yet. In order
695  // to increase confidence that our proof set is similar to other nodes
696  // on the network, the messages received during IBD are not accounted.
697  return;
698  }
699 
701  if (peerManager->latchAvaproofsSent(nodeid)) {
703  }
704 }
705 
706 /*
707  * Returns a bool indicating whether we have a usable Avalanche quorum enabling
708  * us to take decisions based on polls.
709  */
712 
713  {
715  if (peerManager->getNodeCount() < 8) {
716  // There is no point polling if we know the vote cannot converge
717  return false;
718  }
719  }
720 
721  /*
722  * The following parameters can naturally go temporarly below the threshold
723  * under normal circumstances, like during a proof replacement with a lower
724  * stake amount, or the discovery of a new proofs for which we don't have a
725  * node yet.
726  * In order to prevent our node from starting and stopping the polls
727  * spuriously on such event, the quorum establishement is latched. The only
728  * parameters that should not latched is the minimum node count, as this
729  * would cause the poll to be inconclusive anyway and should not happen
730  * under normal circumstances.
731  */
732  if (quorumIsEstablished) {
733  return true;
734  }
735 
736  // Don't do Avalanche while node is IBD'ing
738  return false;
739  }
740 
742  return false;
743  }
744 
745  auto localProof = getLocalProof();
746 
747  // Get the registered proof score and registered score we have nodes for
748  uint32_t totalPeersScore;
749  uint32_t connectedPeersScore;
750  {
752  totalPeersScore = peerManager->getTotalPeersScore();
753  connectedPeersScore = peerManager->getConnectedPeersScore();
754 
755  // Consider that we are always connected to our proof, even if we are
756  // the single node using that proof.
757  if (localProof &&
758  peerManager->forPeer(localProof->getId(), [](const Peer &peer) {
759  return peer.node_count == 0;
760  })) {
761  connectedPeersScore += localProof->getScore();
762  }
763  }
764 
765  // Ensure enough is being staked overall
766  if (totalPeersScore < minQuorumScore) {
767  return false;
768  }
769 
770  // Ensure we have connected score for enough of the overall score
771  uint32_t minConnectedScore =
772  std::round(double(totalPeersScore) * minQuorumConnectedScoreRatio);
773  if (connectedPeersScore < minConnectedScore) {
774  return false;
775  }
776 
777  quorumIsEstablished = true;
778  return true;
779 }
780 
781 void Processor::FinalizeNode(const ::Config &config, const CNode &node) {
783 
784  WITH_LOCK(cs_peerManager, peerManager->removeNode(node.GetId()));
785 }
786 
788  // Don't poll if quorum hasn't been established yet
789  if (!isQuorumEstablished()) {
790  return;
791  }
792 
793  // First things first, check if we have requests that timed out and clear
794  // them.
796 
797  // Make sure there is at least one suitable node to query before gathering
798  // invs.
799  NodeId nodeid = WITH_LOCK(cs_peerManager, return peerManager->selectNode());
800  if (nodeid == NO_NODE) {
801  return;
802  }
803  std::vector<CInv> invs = getInvsForNextPoll();
804  if (invs.empty()) {
805  return;
806  }
807 
809 
810  do {
816  bool hasSent = connman->ForNode(
817  nodeid, [this, &invs](CNode *pnode) EXCLUSIVE_LOCKS_REQUIRED(
818  cs_peerManager) {
819  uint64_t current_round = round++;
820 
821  {
822  // Compute the time at which this requests times out.
823  auto timeout = std::chrono::steady_clock::now() +
825  // Register the query.
826  queries.getWriteView()->insert(
827  {pnode->GetId(), current_round, timeout, invs});
828  // Set the timeout.
829  peerManager->updateNextRequestTime(pnode->GetId(), timeout);
830  }
831 
832  pnode->invsPolled(invs.size());
833 
834  // Send the query to the node.
836  pnode, CNetMsgMaker(pnode->GetCommonVersion())
838  Poll(current_round, std::move(invs))));
839  return true;
840  });
841 
842  // Success!
843  if (hasSent) {
844  return;
845  }
846 
847  // This node is obsolete, delete it.
848  peerManager->removeNode(nodeid);
849 
850  // Get next suitable node to try again
851  nodeid = peerManager->selectNode();
852  } while (nodeid != NO_NODE);
853 }
854 
856  auto now = std::chrono::steady_clock::now();
857  std::map<CInv, uint8_t> timedout_items{};
858 
859  {
860  // Clear expired requests.
861  auto w = queries.getWriteView();
862  auto it = w->get<query_timeout>().begin();
863  while (it != w->get<query_timeout>().end() && it->timeout < now) {
864  for (const auto &i : it->invs) {
865  timedout_items[i]++;
866  }
867 
868  w->get<query_timeout>().erase(it++);
869  }
870  }
871 
872  if (timedout_items.empty()) {
873  return;
874  }
875 
876  auto clearInflightRequest = [&](auto &voteRecords, const auto &voteItem,
877  uint8_t count) {
878  if (!voteItem) {
879  return false;
880  }
881 
882  auto voteRecordsWriteView = voteRecords.getWriteView();
883  auto it = voteRecordsWriteView->find(voteItem);
884  if (it == voteRecordsWriteView.end()) {
885  return false;
886  }
887 
888  it->second.clearInflightRequest(count);
889 
890  return true;
891  };
892 
893  // In flight request accounting.
894  for (const auto &p : timedout_items) {
895  const CInv &inv = p.first;
896  if (inv.IsMsgBlk()) {
897  const CBlockIndex *pindex =
898  WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(
899  BlockHash(inv.hash)));
900 
901  if (!clearInflightRequest(blockVoteRecords, pindex, p.second)) {
902  continue;
903  }
904  }
905 
906  if (inv.IsMsgProof()) {
907  const ProofRef proof =
909  return peerManager->getProof(ProofId(inv.hash)));
910 
911  if (!clearInflightRequest(proofVoteRecords, proof, p.second)) {
912  continue;
913  }
914  }
915  }
916 }
917 
918 std::vector<CInv> Processor::getInvsForNextPoll(bool forPoll) {
919  std::vector<CInv> invs;
920 
921  // Use NO_THREAD_SAFETY_ANALYSIS to avoid false positive due to
922  // isWorthPolling requiring a different lock depending of the prototype.
923  auto removeItemsNotWorthPolling =
924  [&](auto &itemVoteRecords) NO_THREAD_SAFETY_ANALYSIS {
925  auto w = itemVoteRecords.getWriteView();
926  for (auto it = w->begin(); it != w->end();) {
927  if (!isWorthPolling(it->first)) {
928  it = w->erase(it);
929  } else {
930  ++it;
931  }
932  }
933  };
934 
935  auto extractVoteRecordsToInvs = [&](const auto &itemVoteRecordRange,
936  auto buildInvFromVoteItem) {
937  for (const auto &[item, voteRecord] : itemVoteRecordRange) {
938  if (invs.size() >= AVALANCHE_MAX_ELEMENT_POLL) {
939  // Make sure we do not produce more invs than specified by the
940  // protocol.
941  return true;
942  }
943 
944  const bool shouldPoll =
945  forPoll ? voteRecord.registerPoll() : voteRecord.shouldPoll();
946 
947  if (!shouldPoll) {
948  continue;
949  }
950 
951  invs.emplace_back(buildInvFromVoteItem(item));
952  }
953 
954  return invs.size() >= AVALANCHE_MAX_ELEMENT_POLL;
955  };
956 
957  // First remove all proofs that are not worth polling.
958  WITH_LOCK(cs_peerManager, removeItemsNotWorthPolling(proofVoteRecords));
959 
960  if (extractVoteRecordsToInvs(proofVoteRecords.getReadView(),
961  [](const ProofRef &proof) {
962  return CInv(MSG_AVA_PROOF, proof->getId());
963  })) {
964  // The inventory vector is full, we're done
965  return invs;
966  }
967 
968  // First remove all blocks that are not worth polling.
969  WITH_LOCK(cs_main, removeItemsNotWorthPolling(blockVoteRecords));
970 
971  auto r = blockVoteRecords.getReadView();
972  extractVoteRecordsToInvs(reverse_iterate(r), [](const CBlockIndex *pindex) {
973  return CInv(MSG_BLOCK, pindex->GetBlockHash());
974  });
975 
976  return invs;
977 }
978 
981 
982  if (pindex->nStatus.isInvalid()) {
983  // No point polling invalid blocks.
984  return false;
985  }
986 
988  return finalizationTip && finalizationTip->GetAncestor(
989  pindex->nHeight) == pindex)) {
990  // There is no point polling blocks that are ancestor of a block that
991  // has been accepted by the network.
992  return false;
993  }
994 
996  // There is no point polling finalized block.
997  return false;
998  }
999 
1000  return true;
1001 }
1002 
1003 bool Processor::isWorthPolling(const ProofRef &proof) const {
1005 
1006  const ProofId &proofid = proof->getId();
1007 
1008  // No point polling immature or discarded proofs
1009  return peerManager->isBoundToPeer(proofid) ||
1010  peerManager->isInConflictingPool(proofid);
1011 }
1012 
1013 } // namespace avalanche
bool MoneyRange(const Amount nValue)
Definition: amount.h:176
static constexpr double AVALANCHE_DEFAULT_MIN_QUORUM_CONNECTED_STAKE_RATIO
Default minimum percentage of stake-weighted peers we must have a node for to constitute a usable quo...
Definition: avalanche.h:53
static constexpr double AVALANCHE_DEFAULT_MIN_AVAPROOFS_NODE_COUNT
Default minimum number of nodes that sent us an avaproofs message before we can consider our quorum s...
Definition: avalanche.h:60
static constexpr Amount AVALANCHE_DEFAULT_MIN_QUORUM_STAKE
Default minimum cumulative stake of all known peers that constitutes a usable quorum.
Definition: avalanche.h:46
RecursiveMutex cs_main
Global state.
Definition: validation.cpp:94
bool IsArgSet(const std::string &strArg) const
Return true if the given argument has been manually set.
Definition: system.cpp:484
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Return integer argument or default value.
Definition: system.cpp:593
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: system.cpp:584
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: blockindex.h:23
BlockStatus nStatus
Verification status of this block. See enum BlockStatus.
Definition: blockindex.h:85
BlockHash GetBlockHash() const
Definition: blockindex.h:142
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: blockindex.h:36
bool Contains(const CBlockIndex *pindex) const
Efficiently check whether a block is present in this chain.
Definition: chain.h:172
bool IsInitialBlockDownload() const
Check whether we are doing an initial block download (synchronizing from disk or network)
bool IsBlockFinalized(const CBlockIndex *pindex) const EXCLUSIVE_LOCKS_REQUIRED(cs_main)
Checks if a block is finalized.
Definition: net.h:923
bool ForNode(NodeId id, std::function< bool(CNode *pnode)> func)
Definition: net.cpp:3536
void PushMessage(CNode *pnode, CSerializedNetMsg &&msg)
Definition: net.cpp:3491
A writer stream (for serialization) that computes a 256-bit hash.
Definition: hash.h:100
uint256 GetHash()
Compute the double-SHA256 hash of all data written to this object.
Definition: hash.h:123
Inv(ventory) message data.
Definition: protocol.h:516
bool IsMsgBlk() const
Definition: protocol.h:543
uint256 hash
Definition: protocol.h:519
bool IsMsgProof() const
Definition: protocol.h:539
An encapsulated secp256k1 private key.
Definition: key.h:28
bool IsValid() const
Check whether this private key is valid.
Definition: key.h:94
void MakeNewKey(bool fCompressed)
Generate a new private key using a cryptographic PRNG.
Definition: key.cpp:183
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:210
bool SignSchnorr(const uint256 &hash, SchnorrSig &sig, uint32_t test_case=0) const
Create a Schnorr signature.
Definition: key.cpp:288
CSerializedNetMsg Make(int nFlags, std::string msg_type, Args &&...args) const
Information about a peer.
Definition: net.h:455
NodeId GetId() const
Definition: net.h:793
uint64_t GetLocalNonce() const
Definition: net.h:795
int GetCommonVersion() const
Definition: net.h:819
uint64_t nRemoteHostNonce
Definition: net.h:499
uint64_t nRemoteExtraEntropy
Definition: net.h:501
uint64_t GetLocalExtraEntropy() const
Definition: net.h:796
void invsPolled(uint32_t count)
The node was polled for count invs.
Definition: net.cpp:3408
void AddKnownProof(const avalanche::ProofId &proofid)
Definition: net.h:849
An encapsulated public key.
Definition: pubkey.h:31
Simple class for background tasks that should be run periodically or once "after a while".
Definition: scheduler.h:36
void scheduleEvery(Predicate p, std::chrono::milliseconds delta)
Repeat p until it return false.
Definition: scheduler.cpp:114
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:1075
CChainState & ActiveChainstate() const
The most-work chain.
CChain & ActiveChain() const
Definition: validation.h:1210
static RCUPtr make(Args &&...args)
Construct a new object that is owned by the pointer.
Definition: rcu.h:112
ReadView getReadView() const
Definition: rwcollection.h:76
WriteView getWriteView()
Definition: rwcollection.h:82
Result GetResult() const
Definition: validation.h:123
ProofId getProofId() const
Definition: delegation.cpp:56
static bool FromHex(Delegation &dg, const std::string &dgHex, bilingual_str &errorOut)
Definition: delegation.cpp:16
bool verify(DelegationState &state, CPubKey &auth) const
Definition: delegation.cpp:73
const CPubKey & getDelegatedPubkey() const
Definition: delegation.cpp:60
const DelegationId & getId() const
Definition: delegation.h:60
void avaproofsSent(NodeId nodeid) LOCKS_EXCLUDED(cs_main)
Definition: processor.cpp:689
void sendResponse(CNode *pfrom, Response response) const
Definition: processor.cpp:475
const uint32_t staleVoteThreshold
Voting parameters.
Definition: processor.h:171
std::atomic< bool > quorumIsEstablished
Definition: processor.h:166
bool addProofToReconcile(const ProofRef &proof)
Definition: processor.cpp:375
RWCollection< QuerySet > queries
Definition: processor.h:151
static std::unique_ptr< Processor > MakeProcessor(const ArgsManager &argsman, interfaces::Chain &chain, CConnman *connman, ChainstateManager &chainman, CScheduler &scheduler, bilingual_str &error)
Definition: processor.cpp:171
bool sendHello(CNode *pfrom) const
Definition: processor.cpp:649
bool isWorthPolling(const CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
Definition: processor.cpp:979
bool isAccepted(const CBlockIndex *pindex) const
Definition: processor.cpp:396
void clearTimedoutRequests()
Definition: processor.cpp:855
bool startEventLoop(CScheduler &scheduler)
Definition: processor.cpp:680
int getConfidence(const CBlockIndex *pindex) const
Definition: processor.cpp:421
std::atomic< uint64_t > round
Keep track of peers and queries sent.
Definition: processor.h:115
EventLoop eventLoop
Event loop machinery.
Definition: processor.h:159
int64_t minAvaproofsNodeCount
Definition: processor.h:167
RWCollection< ProofVoteMap > proofVoteRecords
Proofs to run avalanche on.
Definition: processor.h:110
bool registerVotes(NodeId nodeid, const Response &response, std::vector< BlockUpdate > &blockUpdates, std::vector< ProofUpdate > &proofUpdates, int &banscore, std::string &error)
Definition: processor.cpp:482
std::unique_ptr< interfaces::Handler > chainNotificationsHandler
Definition: processor.h:175
uint32_t minQuorumScore
Quorum management.
Definition: processor.h:164
ProofRef getLocalProof() const
Definition: processor.cpp:676
const uint32_t staleVoteFactor
Definition: processor.h:172
std::unique_ptr< PeerData > peerData
Definition: processor.h:154
Processor(Config avaconfig, interfaces::Chain &chain, CConnman *connmanIn, ChainstateManager &chainman, CScheduler &scheduler, std::unique_ptr< PeerData > peerDataIn, CKey sessionKeyIn, uint32_t minQuorumTotalScoreIn, double minQuorumConnectedScoreRatioIn, int64_t minAvaproofsNodeCountIn, uint32_t staleVoteThresholdIn, uint32_t staleVoteFactorIn, Amount stakeUtxoDustThresholdIn)
Definition: processor.cpp:134
CConnman * connman
Definition: processor.h:99
CPubKey getSessionPubKey() const
Definition: processor.cpp:645
ChainstateManager & chainman
Definition: processor.h:100
std::atomic< int64_t > avaproofsNodeCounter
Definition: processor.h:168
bool isQuorumEstablished() LOCKS_EXCLUDED(cs_main)
Definition: processor.cpp:710
void FinalizeNode(const ::Config &config, const CNode &node) override LOCKS_EXCLUDED(cs_main)
Handle removal of a node.
Definition: processor.cpp:781
std::vector< CInv > getInvsForNextPoll(bool forPoll=true)
Definition: processor.cpp:918
RWCollection< BlockVoteMap > blockVoteRecords
Blocks to run avalanche on.
Definition: processor.h:105
bool addBlockToReconcile(const CBlockIndex *pindex)
Definition: processor.cpp:353
Mutex cs_peerManager
Keep track of the peers and associated infos.
Definition: processor.h:120
double minQuorumConnectedScoreRatio
Definition: processor.h:165
static bool FromHex(Proof &proof, const std::string &hexProof, bilingual_str &errorOut)
Definition: proof.cpp:50
bool verify(const Amount &stakeUtxoDustThreshold, ProofValidationState &state) const
Definition: proof.cpp:110
static uint32_t amountToScore(Amount amount)
Definition: proof.cpp:99
uint32_t GetError() const
Definition: protocol.h:27
Chain notifications.
Definition: chain.h:266
Interface giving clients (wallet processes, maybe other analysis tools in the future) ability to acce...
Definition: chain.h:121
virtual std::unique_ptr< Handler > handleNotifications(std::shared_ptr< Notifications > notifications)=0
Register handler for notifications.
256-bit opaque blob.
Definition: uint256.h:127
std::array< uint8_t, CPubKey::SCHNORR_SIZE > SchnorrSig
a Schnorr signature
Definition: key.h:25
CKey DecodeSecret(const std::string &str)
Definition: key_io.cpp:80
bool ParseMoney(const std::string &money_string, Amount &nRet)
Parse an amount denoted in full coins.
Definition: moneystr.cpp:37
const char * AVAHELLO
Contains a delegation and a signature.
Definition: protocol.cpp:50
const char * AVARESPONSE
Contains an avalanche::Response.
Definition: protocol.cpp:52
const char * AVAPOLL
Contains an avalanche::Poll.
Definition: protocol.cpp:51
static constexpr Amount PROOF_DUST_THRESHOLD
Minimum amount per utxo.
Definition: proof.h:40
static bool VerifyDelegation(const Delegation &dg, const CPubKey &expectedPubKey, bilingual_str &error)
Definition: processor.cpp:70
static bool VerifyProof(const Amount &stakeUtxoDustThreshold, const Proof &proof, bilingual_str &error)
Definition: processor.cpp:38
RCUPtr< const Proof > ProofRef
Definition: proof.h:185
static constexpr NodeId NO_NODE
Special NodeId that represent no node.
Definition: nodeid.h:15
int64_t NodeId
Definition: nodeid.h:10
Response response
Definition: processor.cpp:452
static constexpr std::chrono::milliseconds AVALANCHE_TIME_STEP
Run the avalanche event loop every 10ms.
Definition: processor.cpp:31
SchnorrSig sig
Definition: processor.cpp:453
std::unique_ptr< avalanche::Processor > g_avalanche
Global avalanche instance.
Definition: processor.cpp:35
static constexpr std::chrono::milliseconds AVALANCHE_DEFAULT_QUERY_TIMEOUT
How long before we consider that a query timed out.
Definition: processor.h:49
static constexpr size_t AVALANCHE_MAX_ELEMENT_POLL
Maximum item that can be polled at once.
Definition: processor.h:44
static constexpr int AVALANCHE_MAX_PROOF_STAKES
How many UTXOs can be used for a single proof.
Definition: proof.h:29
@ MSG_BLOCK
Definition: protocol.h:502
reverse_range< T > reverse_iterate(T &x)
#define SERIALIZE_METHODS(cls, obj)
Implement the Serialize and Unserialize methods by delegating to a single templated static method tha...
Definition: serialize.h:227
@ SER_GETHASH
Definition: serialize.h:168
#define READWRITE(...)
Definition: serialize.h:180
bool ParseDouble(const std::string &str, double *out)
Convert string to double with strict parse error feedback.
Definition: amount.h:19
A BlockHash is a unqiue identifier for a block.
Definition: blockhash.h:13
bool isInvalid() const
Definition: blockstatus.h:119
bool stopEventLoop()
Definition: eventloop.cpp:45
bool startEventLoop(CScheduler &scheduler, std::function< void()> runEventLoop, std::chrono::milliseconds delta)
Definition: eventloop.cpp:13
const std::chrono::milliseconds queryTimeoutDuration
Definition: config.h:13
Vote history.
Definition: voterecord.h:47
Bilingual messages:
Definition: translation.h:17
#define AssertLockNotHeld(cs)
Definition: sync.h:97
#define LOCK(cs)
Definition: sync.h:243
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:276
ArgsManager gArgs
Definition: system.cpp:77
bool error(const char *fmt, const Args &...args)
Definition: system.h:46
static int count
Definition: tests.c:31
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:56
#define NO_THREAD_SAFETY_ANALYSIS
Definition: threadsafety.h:58
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1201
bilingual_str _(const char *psz)
Translation function.
Definition: translation.h:55
AssertLockHeld(pool.cs)
static constexpr uint32_t AVALANCHE_VOTE_STALE_FACTOR
Scaling factor applied to confidence to determine staleness threshold.
Definition: voterecord.h:35
static constexpr uint32_t AVALANCHE_VOTE_STALE_MIN_THRESHOLD
Lowest configurable staleness threshold (finalization score + necessary votes to increase confidence ...
Definition: voterecord.h:28
static constexpr uint32_t AVALANCHE_VOTE_STALE_THRESHOLD
Number of votes before a record may be considered as stale.
Definition: voterecord.h:22