Bitcoin ABC  0.26.3
P2P Digital Currency
peermanager.cpp
Go to the documentation of this file.
1 // Copyright (c) 2020 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 
6 
7 #include <arith_uint256.h>
8 #include <avalanche/avalanche.h>
9 #include <avalanche/delegation.h>
10 #include <avalanche/validation.h>
11 #include <cashaddrenc.h>
12 #include <common/args.h>
13 #include <consensus/activation.h>
14 #include <logging.h>
15 #include <random.h>
16 #include <scheduler.h>
17 #include <uint256.h>
18 #include <util/fastrange.h>
19 #include <util/fs_helpers.h>
20 #include <util/time.h>
21 #include <validation.h> // For ChainstateManager
22 
23 #include <algorithm>
24 #include <cassert>
25 #include <cmath>
26 #include <limits>
27 
28 namespace avalanche {
29 static constexpr uint64_t PEERS_DUMP_VERSION{1};
30 
31 bool PeerManager::addNode(NodeId nodeid, const ProofId &proofid) {
32  auto &pview = peers.get<by_proofid>();
33  auto it = pview.find(proofid);
34  if (it == pview.end()) {
35  // If the node exists, it is actually updating its proof to an unknown
36  // one. In this case we need to remove it so it is not both active and
37  // pending at the same time.
38  removeNode(nodeid);
39  pendingNodes.emplace(proofid, nodeid);
40  return false;
41  }
42 
43  return addOrUpdateNode(peers.project<0>(it), nodeid);
44 }
45 
46 bool PeerManager::addOrUpdateNode(const PeerSet::iterator &it, NodeId nodeid) {
47  assert(it != peers.end());
48 
49  const PeerId peerid = it->peerid;
50 
51  auto nit = nodes.find(nodeid);
52  if (nit == nodes.end()) {
53  if (!nodes.emplace(nodeid, peerid).second) {
54  return false;
55  }
56  } else {
57  const PeerId oldpeerid = nit->peerid;
58  if (!nodes.modify(nit, [&](Node &n) { n.peerid = peerid; })) {
59  return false;
60  }
61 
62  // We actually have this node already, we need to update it.
63  bool success = removeNodeFromPeer(peers.find(oldpeerid));
64  assert(success);
65  }
66 
67  // Then increase the node counter, and create the slot if needed
68  bool success = addNodeToPeer(it);
69  assert(success);
70 
71  // If the added node was in the pending set, remove it
72  pendingNodes.get<by_nodeid>().erase(nodeid);
73 
74  // If the proof was in the dangling pool, remove it
75  const ProofId &proofid = it->getProofId();
76  if (danglingProofPool.getProof(proofid)) {
78  }
79 
80  // We know for sure there is at least 1 node. Note that this can fail if
81  // there is more than 1, in this case it's a no-op.
82  shareableProofs.insert(it->proof);
83 
84  return true;
85 }
86 
87 bool PeerManager::addNodeToPeer(const PeerSet::iterator &it) {
88  assert(it != peers.end());
89  return peers.modify(it, [&](Peer &p) {
90  if (p.node_count++ > 0) {
91  // We are done.
92  return;
93  }
94 
95  // We need to allocate this peer.
96  p.index = uint32_t(slots.size());
97  const uint32_t score = p.getScore();
98  const uint64_t start = slotCount;
99  slots.emplace_back(start, score, it->peerid);
100  slotCount = start + score;
101 
102  // Add to our allocated score when we allocate a new peer in the slots
103  connectedPeersScore += score;
104  });
105 }
106 
108  // Remove all the remote proofs from this node
109  auto &remoteProofsView = remoteProofs.get<by_nodeid>();
110  auto [begin, end] = remoteProofsView.equal_range(nodeid);
111  remoteProofsView.erase(begin, end);
112 
113  if (pendingNodes.get<by_nodeid>().erase(nodeid) > 0) {
114  // If this was a pending node, there is nothing else to do.
115  return true;
116  }
117 
118  auto it = nodes.find(nodeid);
119  if (it == nodes.end()) {
120  return false;
121  }
122 
123  const PeerId peerid = it->peerid;
124  nodes.erase(it);
125 
126  // Keep the track of the reference count.
127  bool success = removeNodeFromPeer(peers.find(peerid));
128  assert(success);
129 
130  return true;
131 }
132 
133 bool PeerManager::removeNodeFromPeer(const PeerSet::iterator &it,
134  uint32_t count) {
135  // It is possible for nodes to be dangling. If there was an inflight query
136  // when the peer gets removed, the node was not erased. In this case there
137  // is nothing to do.
138  if (it == peers.end()) {
139  return true;
140  }
141 
142  assert(count <= it->node_count);
143  if (count == 0) {
144  // This is a NOOP.
145  return false;
146  }
147 
148  const uint32_t new_count = it->node_count - count;
149  if (!peers.modify(it, [&](Peer &p) { p.node_count = new_count; })) {
150  return false;
151  }
152 
153  if (new_count > 0) {
154  // We are done.
155  return true;
156  }
157 
158  // There are no more nodes left, we need to clean up. Remove from the radix
159  // tree (unless it's our local proof), subtract allocated score and remove
160  // from slots.
161  if (!localProof || it->getProofId() != localProof->getId()) {
162  const auto removed = shareableProofs.remove(it->getProofId());
163  assert(removed);
164  }
165 
166  const size_t i = it->index;
167  assert(i < slots.size());
168  assert(connectedPeersScore >= slots[i].getScore());
169  connectedPeersScore -= slots[i].getScore();
170 
171  if (i + 1 == slots.size()) {
172  slots.pop_back();
173  slotCount = slots.empty() ? 0 : slots.back().getStop();
174  } else {
175  fragmentation += slots[i].getScore();
176  slots[i] = slots[i].withPeerId(NO_PEER);
177  }
178 
179  return true;
180 }
181 
183  SteadyMilliseconds timeout) {
184  auto it = nodes.find(nodeid);
185  if (it == nodes.end()) {
186  return false;
187  }
188 
189  return nodes.modify(it, [&](Node &n) { n.nextRequestTime = timeout; });
190 }
191 
193  auto it = nodes.find(nodeid);
194  if (it == nodes.end()) {
195  return false;
196  }
197 
198  return !it->avaproofsSent &&
199  nodes.modify(it, [&](Node &n) { n.avaproofsSent = true; });
200 }
201 
202 static bool isImmatureState(const ProofValidationState &state) {
204 }
205 
207  PeerId peerid, const std::chrono::seconds &nextTime) {
208  auto it = peers.find(peerid);
209  if (it == peers.end()) {
210  // No such peer
211  return false;
212  }
213 
214  // Make sure we don't move the time in the past.
215  peers.modify(it, [&](Peer &p) {
217  std::max(p.nextPossibleConflictTime, nextTime);
218  });
219 
220  return it->nextPossibleConflictTime == nextTime;
221 }
222 
224  auto it = peers.find(peerid);
225  if (it == peers.end()) {
226  // No such peer
227  return false;
228  }
229 
230  peers.modify(it, [&](Peer &p) { p.hasFinalized = true; });
231 
232  return true;
233 }
234 
235 template <typename ProofContainer>
236 void PeerManager::moveToConflictingPool(const ProofContainer &proofs) {
237  auto &peersView = peers.get<by_proofid>();
238  for (const ProofRef &proof : proofs) {
239  auto it = peersView.find(proof->getId());
240  if (it != peersView.end()) {
241  removePeer(it->peerid);
242  }
243 
245  }
246 }
247 
249  ProofRegistrationState &registrationState,
250  RegistrationMode mode) {
251  assert(proof);
252 
253  const ProofId &proofid = proof->getId();
254 
255  auto invalidate = [&](ProofRegistrationResult result,
256  const std::string &message) {
257  return registrationState.Invalid(
258  result, message, strprintf("proofid: %s", proofid.ToString()));
259  };
260 
261  if ((mode != RegistrationMode::FORCE_ACCEPT ||
262  !isInConflictingPool(proofid)) &&
263  exists(proofid)) {
264  // In default mode, we expect the proof to be unknown, i.e. in none of
265  // the pools.
266  // In forced accept mode, the proof can be in the conflicting pool.
268  "proof-already-registered");
269  }
270 
271  if (danglingProofPool.getProof(proofid) &&
272  pendingNodes.count(proofid) == 0) {
273  // Don't attempt to register a proof that we already evicted because it
274  // was dangling, but rather attempt to retrieve an associated node.
275  needMoreNodes = true;
276  return invalidate(ProofRegistrationResult::DANGLING, "dangling-proof");
277  }
278 
279  // Check the proof's validity.
280  ProofValidationState validationState;
281  if (!WITH_LOCK(cs_main, return proof->verify(stakeUtxoDustThreshold,
282  chainman, validationState))) {
283  if (isImmatureState(validationState)) {
287  // Adding this proof exceeds the immature pool limit, so evict
288  // the lowest scoring proof.
291  }
292 
293  return invalidate(ProofRegistrationResult::IMMATURE,
294  "immature-proof");
295  }
296 
297  if (validationState.GetResult() ==
299  return invalidate(ProofRegistrationResult::MISSING_UTXO,
300  "utxo-missing-or-spent");
301  }
302 
303  // Reject invalid proof.
304  return invalidate(ProofRegistrationResult::INVALID, "invalid-proof");
305  }
306 
307  auto now = GetTime<std::chrono::seconds>();
308  auto nextCooldownTimePoint =
309  now + std::chrono::seconds(gArgs.GetIntArg(
310  "-avalancheconflictingproofcooldown",
312 
313  ProofPool::ConflictingProofSet conflictingProofs;
314  switch (validProofPool.addProofIfNoConflict(proof, conflictingProofs)) {
315  case ProofPool::AddProofStatus::REJECTED: {
316  if (mode != RegistrationMode::FORCE_ACCEPT) {
317  auto bestPossibleConflictTime = std::chrono::seconds(0);
318  auto &pview = peers.get<by_proofid>();
319  for (auto &conflictingProof : conflictingProofs) {
320  auto it = pview.find(conflictingProof->getId());
321  assert(it != pview.end());
322 
323  // Search the most recent time over the peers
324  bestPossibleConflictTime = std::max(
325  bestPossibleConflictTime, it->nextPossibleConflictTime);
326 
328  nextCooldownTimePoint);
329  }
330 
331  if (bestPossibleConflictTime > now) {
332  // Cooldown not elapsed, reject the proof.
333  return invalidate(
335  "cooldown-not-elapsed");
336  }
337 
338  // Give the proof a chance to replace the conflicting ones.
339  if (validProofPool.addProofIfPreferred(proof)) {
340  // If we have overridden other proofs due to conflict,
341  // remove the peers and attempt to move them to the
342  // conflicting pool.
343  moveToConflictingPool(conflictingProofs);
344 
345  // Replacement is successful, continue to peer creation
346  break;
347  }
348 
349  // Not the preferred proof, or replacement is not enabled
351  ProofPool::AddProofStatus::REJECTED
353  "rejected-proof")
355  "conflicting-utxos");
356  }
357 
359 
360  // Move the conflicting proofs from the valid pool to the
361  // conflicting pool
362  moveToConflictingPool(conflictingProofs);
363 
364  auto status = validProofPool.addProofIfNoConflict(proof);
365  assert(status == ProofPool::AddProofStatus::SUCCEED);
366 
367  break;
368  }
369  case ProofPool::AddProofStatus::DUPLICATED:
370  // If the proof was already in the pool, don't duplicate the peer.
372  "proof-already-registered");
373  case ProofPool::AddProofStatus::SUCCEED:
374  break;
375 
376  // No default case, so the compiler can warn about missing cases
377  }
378 
379  // At this stage we are going to create a peer so the proof should never
380  // exist in the conflicting pool, but use belt and suspenders.
382 
383  // New peer means new peerid!
384  const PeerId peerid = nextPeerId++;
385 
386  // We have no peer for this proof, time to create it.
387  auto inserted = peers.emplace(peerid, proof, nextCooldownTimePoint);
388  assert(inserted.second);
389 
390  if (localProof && proof->getId() == localProof->getId()) {
391  // Add it to the shareable proofs even if there is no node, we are the
392  // node. Otherwise it will be inserted after a node is attached to the
393  // proof.
394  shareableProofs.insert(proof);
395  }
396 
397  // Add to our registered score when adding to the peer list
398  totalPeersScore += proof->getScore();
399 
400  // If there are nodes waiting for this proof, add them
401  auto &pendingNodesView = pendingNodes.get<by_proofid>();
402  auto range = pendingNodesView.equal_range(proofid);
403 
404  // We want to update the nodes then remove them from the pending set. That
405  // will invalidate the range iterators, so we need to save the node ids
406  // first before we can loop over them.
407  std::vector<NodeId> nodeids;
408  nodeids.reserve(std::distance(range.first, range.second));
409  std::transform(range.first, range.second, std::back_inserter(nodeids),
410  [](const PendingNode &n) { return n.nodeid; });
411 
412  for (const NodeId &nodeid : nodeids) {
413  addOrUpdateNode(inserted.first, nodeid);
414  }
415 
416  return true;
417 }
418 
419 bool PeerManager::rejectProof(const ProofId &proofid, RejectionMode mode) {
420  if (isDangling(proofid) && mode == RejectionMode::INVALIDATE) {
422  return true;
423  }
424 
425  if (!exists(proofid)) {
426  return false;
427  }
428 
429  if (immatureProofPool.removeProof(proofid)) {
430  return true;
431  }
432 
433  if (mode == RejectionMode::DEFAULT &&
434  conflictingProofPool.getProof(proofid)) {
435  // In default mode we keep the proof in the conflicting pool
436  return true;
437  }
438 
439  if (mode == RejectionMode::INVALIDATE &&
441  // In invalidate mode we remove the proof completely
442  return true;
443  }
444 
445  auto &pview = peers.get<by_proofid>();
446  auto it = pview.find(proofid);
447  assert(it != pview.end());
448 
449  const ProofRef proof = it->proof;
450 
451  if (!removePeer(it->peerid)) {
452  return false;
453  }
454 
455  // If there was conflicting proofs, attempt to pull them back
456  for (const SignedStake &ss : proof->getStakes()) {
457  const ProofRef conflictingProof =
458  conflictingProofPool.getProof(ss.getStake().getUTXO());
459  if (!conflictingProof) {
460  continue;
461  }
462 
463  conflictingProofPool.removeProof(conflictingProof->getId());
464  registerProof(conflictingProof);
465  }
466 
467  if (mode == RejectionMode::DEFAULT) {
469  }
470 
471  return true;
472 }
473 
475  std::unordered_set<ProofRef, SaltedProofHasher> &registeredProofs) {
476  registeredProofs.clear();
477  const auto now = GetTime<std::chrono::seconds>();
478 
479  std::vector<ProofRef> newlyDanglingProofs;
480  for (const Peer &peer : peers) {
481  // If the peer is not our local proof, has been registered for some
482  // time and has no node attached, discard it.
483  if ((!localProof || peer.getProofId() != localProof->getId()) &&
484  peer.node_count == 0 &&
485  (peer.registration_time + Peer::DANGLING_TIMEOUT) <= now) {
486  // Check the remotes status to determine if we should set the proof
487  // as dangling. This prevents from dropping a proof on our own due
488  // to a network issue. If the remote presence status is inconclusive
489  // we assume our own position (missing = false).
490  if (!getRemotePresenceStatus(peer.getProofId()).value_or(false)) {
491  newlyDanglingProofs.push_back(peer.proof);
492  }
493  }
494  }
495 
496  // Similarly, check if we have dangling proofs that could be pulled back
497  // because the network says so.
498  std::vector<ProofRef> previouslyDanglingProofs;
499  danglingProofPool.forEachProof([&](const ProofRef &proof) {
500  if (getRemotePresenceStatus(proof->getId()).value_or(false)) {
501  previouslyDanglingProofs.push_back(proof);
502  }
503  });
504  for (const ProofRef &proof : previouslyDanglingProofs) {
505  danglingProofPool.removeProof(proof->getId());
506  if (registerProof(proof)) {
507  registeredProofs.insert(proof);
508  }
509  }
510 
511  for (const ProofRef &proof : newlyDanglingProofs) {
512  rejectProof(proof->getId(), RejectionMode::INVALIDATE);
514  // If the proof is added, it means there is no better conflicting
515  // dangling proof and this is not a duplicated, so it's worth
516  // printing a message to the log.
518  "Proof dangling for too long (no connected node): %s\n",
519  proof->getId().GetHex());
520  }
521  }
522 
523  // If we have dangling proof, this is a good indicator that we need to
524  // request more nodes from our peers.
525  needMoreNodes = !newlyDanglingProofs.empty();
526 }
527 
529  for (int retry = 0; retry < SELECT_NODE_MAX_RETRY; retry++) {
530  const PeerId p = selectPeer();
531 
532  // If we cannot find a peer, it may be due to the fact that it is
533  // unlikely due to high fragmentation, so compact and retry.
534  if (p == NO_PEER) {
535  compact();
536  continue;
537  }
538 
539  // See if that peer has an available node.
540  auto &nview = nodes.get<next_request_time>();
541  auto it = nview.lower_bound(boost::make_tuple(p, SteadyMilliseconds()));
542  if (it != nview.end() && it->peerid == p &&
543  it->nextRequestTime <= Now<SteadyMilliseconds>()) {
544  return it->nodeid;
545  }
546  }
547 
548  // We failed to find a node to query, flag this so we can request more
549  needMoreNodes = true;
550 
551  return NO_NODE;
552 }
553 
554 std::unordered_set<ProofRef, SaltedProofHasher> PeerManager::updatedBlockTip() {
555  std::vector<ProofId> invalidProofIds;
556  std::vector<ProofRef> newImmatures;
557 
558  {
559  LOCK(cs_main);
560 
561  for (const auto &p : peers) {
562  ProofValidationState state;
563  if (!p.proof->verify(stakeUtxoDustThreshold, chainman, state)) {
564  if (isImmatureState(state)) {
565  newImmatures.push_back(p.proof);
566  }
567  invalidProofIds.push_back(p.getProofId());
568 
570  "Invalidating proof %s: verification failed (%s)\n",
571  p.proof->getId().GetHex(), state.ToString());
572  }
573  }
574  }
575 
576  // Remove the invalid proofs before the immature rescan. This makes it
577  // possible to pull back proofs with utxos that conflicted with these
578  // invalid proofs.
579  for (const ProofId &invalidProofId : invalidProofIds) {
580  rejectProof(invalidProofId, RejectionMode::INVALIDATE);
581  }
582 
583  auto registeredProofs = immatureProofPool.rescan(*this);
584 
585  for (auto &p : newImmatures) {
587  }
588 
589  return registeredProofs;
590 }
591 
592 ProofRef PeerManager::getProof(const ProofId &proofid) const {
593  ProofRef proof;
594 
595  forPeer(proofid, [&](const Peer &p) {
596  proof = p.proof;
597  return true;
598  });
599 
600  if (!proof) {
601  proof = conflictingProofPool.getProof(proofid);
602  }
603 
604  if (!proof) {
605  proof = immatureProofPool.getProof(proofid);
606  }
607 
608  return proof;
609 }
610 
611 bool PeerManager::isBoundToPeer(const ProofId &proofid) const {
612  auto &pview = peers.get<by_proofid>();
613  return pview.find(proofid) != pview.end();
614 }
615 
616 bool PeerManager::isImmature(const ProofId &proofid) const {
617  return immatureProofPool.getProof(proofid) != nullptr;
618 }
619 
620 bool PeerManager::isInConflictingPool(const ProofId &proofid) const {
621  return conflictingProofPool.getProof(proofid) != nullptr;
622 }
623 
624 bool PeerManager::isDangling(const ProofId &proofid) const {
625  return danglingProofPool.getProof(proofid) != nullptr;
626 }
627 
628 void PeerManager::setInvalid(const ProofId &proofid) {
629  invalidProofs.insert(proofid);
630 }
631 
632 bool PeerManager::isInvalid(const ProofId &proofid) const {
633  return invalidProofs.contains(proofid);
634 }
635 
638 }
639 
640 bool PeerManager::saveRemoteProof(const ProofId &proofid, const NodeId nodeid,
641  const bool present) {
642  // Get how many proofs this node has announced
643  auto &remoteProofsByLastUpdate = remoteProofs.get<by_lastUpdate>();
644  auto [begin, end] = remoteProofsByLastUpdate.equal_range(nodeid);
645 
646  // Limit the number of proofs a single node can save:
647  // - At least MAX_REMOTE_PROOFS
648  // - Up to 2x as much as we have
649  // The MAX_REMOTE_PROOFS minimum is there to ensure we don't overlimit at
650  // startup when we don't have proofs yet.
651  while (size_t(std::distance(begin, end)) >=
652  std::max(MAX_REMOTE_PROOFS, 2 * peers.size())) {
653  // Remove the proof with the oldest update time
654  begin = remoteProofsByLastUpdate.erase(begin);
655  }
656 
657  auto it = remoteProofs.find(boost::make_tuple(proofid, nodeid));
658  if (it != remoteProofs.end()) {
659  remoteProofs.erase(it);
660  }
661 
662  return remoteProofs
663  .emplace(RemoteProof{proofid, nodeid, GetTime<std::chrono::seconds>(),
664  present})
665  .second;
666 }
667 
668 std::vector<RemoteProof>
670  std::vector<RemoteProof> nodeRemoteProofs;
671 
672  auto &remoteProofsByLastUpdate = remoteProofs.get<by_lastUpdate>();
673  auto [begin, end] = remoteProofsByLastUpdate.equal_range(nodeid);
674 
675  for (auto &it = begin; it != end; it++) {
676  nodeRemoteProofs.emplace_back(*it);
677  }
678 
679  return nodeRemoteProofs;
680 }
681 
682 bool PeerManager::removePeer(const PeerId peerid) {
683  auto it = peers.find(peerid);
684  if (it == peers.end()) {
685  return false;
686  }
687 
688  // Remove all nodes from this peer.
689  removeNodeFromPeer(it, it->node_count);
690 
691  auto &nview = nodes.get<next_request_time>();
692 
693  // Add the nodes to the pending set
694  auto range = nview.equal_range(peerid);
695  for (auto &nit = range.first; nit != range.second; ++nit) {
696  pendingNodes.emplace(it->getProofId(), nit->nodeid);
697  };
698 
699  // Remove nodes associated with this peer, unless their timeout is still
700  // active. This ensure that we don't overquery them in case they are
701  // subsequently added to another peer.
702  nview.erase(
703  nview.lower_bound(boost::make_tuple(peerid, SteadyMilliseconds())),
704  nview.upper_bound(
705  boost::make_tuple(peerid, Now<SteadyMilliseconds>())));
706 
707  // Release UTXOs attached to this proof.
708  validProofPool.removeProof(it->getProofId());
709 
710  // If there were nodes attached, remove from the radix tree as well
711  auto removed = shareableProofs.remove(Uint256RadixKey(it->getProofId()));
712 
713  m_unbroadcast_proofids.erase(it->getProofId());
714 
715  // Remove the peer from the PeerSet and remove its score from the registered
716  // score total.
717  assert(totalPeersScore >= it->getScore());
718  totalPeersScore -= it->getScore();
719  peers.erase(it);
720  return true;
721 }
722 
724  if (slots.empty() || slotCount == 0) {
725  return NO_PEER;
726  }
727 
728  const uint64_t max = slotCount;
729  for (int retry = 0; retry < SELECT_PEER_MAX_RETRY; retry++) {
730  size_t i = selectPeerImpl(slots, GetRand(max), max);
731  if (i != NO_PEER) {
732  return i;
733  }
734  }
735 
736  return NO_PEER;
737 }
738 
740  // There is nothing to compact.
741  if (fragmentation == 0) {
742  return 0;
743  }
744 
745  std::vector<Slot> newslots;
746  newslots.reserve(peers.size());
747 
748  uint64_t prevStop = 0;
749  uint32_t i = 0;
750  for (auto it = peers.begin(); it != peers.end(); it++) {
751  if (it->node_count == 0) {
752  continue;
753  }
754 
755  newslots.emplace_back(prevStop, it->getScore(), it->peerid);
756  prevStop = slots[i].getStop();
757  if (!peers.modify(it, [&](Peer &p) { p.index = i++; })) {
758  return 0;
759  }
760  }
761 
762  slots = std::move(newslots);
763 
764  const uint64_t saved = slotCount - prevStop;
765  slotCount = prevStop;
766  fragmentation = 0;
767 
768  return saved;
769 }
770 
771 bool PeerManager::verify() const {
772  uint64_t prevStop = 0;
773  uint32_t scoreFromSlots = 0;
774  for (size_t i = 0; i < slots.size(); i++) {
775  const Slot &s = slots[i];
776 
777  // Slots must be in correct order.
778  if (s.getStart() < prevStop) {
779  return false;
780  }
781 
782  prevStop = s.getStop();
783 
784  // If this is a dead slot, then nothing more needs to be checked.
785  if (s.getPeerId() == NO_PEER) {
786  continue;
787  }
788 
789  // We have a live slot, verify index.
790  auto it = peers.find(s.getPeerId());
791  if (it == peers.end() || it->index != i) {
792  return false;
793  }
794 
795  // Accumulate score across slots
796  scoreFromSlots += slots[i].getScore();
797  }
798 
799  // Score across slots must be the same as our allocated score
800  if (scoreFromSlots != connectedPeersScore) {
801  return false;
802  }
803 
804  uint32_t scoreFromAllPeers = 0;
805  uint32_t scoreFromPeersWithNodes = 0;
806 
807  std::unordered_set<COutPoint, SaltedOutpointHasher> peersUtxos;
808  for (const auto &p : peers) {
809  // Accumulate the score across peers to compare with total known score
810  scoreFromAllPeers += p.getScore();
811 
812  // A peer should have a proof attached
813  if (!p.proof) {
814  return false;
815  }
816 
817  // Check proof pool consistency
818  for (const auto &ss : p.proof->getStakes()) {
819  const COutPoint &outpoint = ss.getStake().getUTXO();
820  auto proof = validProofPool.getProof(outpoint);
821 
822  if (!proof) {
823  // Missing utxo
824  return false;
825  }
826  if (proof != p.proof) {
827  // Wrong proof
828  return false;
829  }
830 
831  if (!peersUtxos.emplace(outpoint).second) {
832  // Duplicated utxo
833  return false;
834  }
835  }
836 
837  // Count node attached to this peer.
838  const auto count_nodes = [&]() {
839  size_t count = 0;
840  auto &nview = nodes.get<next_request_time>();
841  auto begin = nview.lower_bound(
842  boost::make_tuple(p.peerid, SteadyMilliseconds()));
843  auto end = nview.upper_bound(
844  boost::make_tuple(p.peerid + 1, SteadyMilliseconds()));
845 
846  for (auto it = begin; it != end; ++it) {
847  count++;
848  }
849 
850  return count;
851  };
852 
853  if (p.node_count != count_nodes()) {
854  return false;
855  }
856 
857  // If there are no nodes attached to this peer, then we are done.
858  if (p.node_count == 0) {
859  continue;
860  }
861 
862  scoreFromPeersWithNodes += p.getScore();
863  // The index must point to a slot refering to this peer.
864  if (p.index >= slots.size() || slots[p.index].getPeerId() != p.peerid) {
865  return false;
866  }
867 
868  // If the score do not match, same thing.
869  if (slots[p.index].getScore() != p.getScore()) {
870  return false;
871  }
872 
873  // Check the proof is in the radix tree only if there are nodes attached
874  if (((localProof && p.getProofId() == localProof->getId()) ||
875  p.node_count > 0) &&
876  shareableProofs.get(p.getProofId()) == nullptr) {
877  return false;
878  }
879  if (p.node_count == 0 &&
880  shareableProofs.get(p.getProofId()) != nullptr) {
881  return false;
882  }
883  }
884 
885  // Check our accumulated scores against our registred and allocated scores
886  if (scoreFromAllPeers != totalPeersScore) {
887  return false;
888  }
889  if (scoreFromPeersWithNodes != connectedPeersScore) {
890  return false;
891  }
892 
893  // We checked the utxo consistency for all our peers utxos already, so if
894  // the pool size differs from the expected one there are dangling utxos.
895  if (validProofPool.size() != peersUtxos.size()) {
896  return false;
897  }
898 
899  // Check there is no dangling proof in the radix tree
901  return isBoundToPeer(pLeaf->getId());
902  });
903 }
904 
905 PeerId selectPeerImpl(const std::vector<Slot> &slots, const uint64_t slot,
906  const uint64_t max) {
907  assert(slot <= max);
908 
909  size_t begin = 0, end = slots.size();
910  uint64_t bottom = 0, top = max;
911 
912  // Try to find the slot using dichotomic search.
913  while ((end - begin) > 8) {
914  // The slot we picked in not allocated.
915  if (slot < bottom || slot >= top) {
916  return NO_PEER;
917  }
918 
919  // Guesstimate the position of the slot.
920  size_t i = begin + ((slot - bottom) * (end - begin) / (top - bottom));
921  assert(begin <= i && i < end);
922 
923  // We have a match.
924  if (slots[i].contains(slot)) {
925  return slots[i].getPeerId();
926  }
927 
928  // We undershooted.
929  if (slots[i].precedes(slot)) {
930  begin = i + 1;
931  if (begin >= end) {
932  return NO_PEER;
933  }
934 
935  bottom = slots[begin].getStart();
936  continue;
937  }
938 
939  // We overshooted.
940  if (slots[i].follows(slot)) {
941  end = i;
942  top = slots[end].getStart();
943  continue;
944  }
945 
946  // We have an unalocated slot.
947  return NO_PEER;
948  }
949 
950  // Enough of that nonsense, let fallback to linear search.
951  for (size_t i = begin; i < end; i++) {
952  // We have a match.
953  if (slots[i].contains(slot)) {
954  return slots[i].getPeerId();
955  }
956  }
957 
958  // We failed to find a slot, retry.
959  return NO_PEER;
960 }
961 
963  // The proof should be bound to a peer
964  if (isBoundToPeer(proofid)) {
965  m_unbroadcast_proofids.insert(proofid);
966  }
967 }
968 
970  m_unbroadcast_proofids.erase(proofid);
971 }
972 
974  const CBlockIndex *pprev,
975  std::vector<std::pair<ProofId, CScript>> &winners) {
976  if (!pprev) {
977  return false;
978  }
979 
980  // Don't select proofs that have not been known for long enough, i.e. at
981  // least since twice the dangling proof cleanup timeout before the last
982  // block time, so we're sure to not account for proofs more recent than the
983  // previous block or lacking node connected.
984  // The previous block time is capped to now for the unlikely event the
985  // previous block time is in the future.
986  auto registrationDelay = std::chrono::duration_cast<std::chrono::seconds>(
988  auto maxRegistrationDelay =
989  std::chrono::duration_cast<std::chrono::seconds>(
991  auto minRegistrationDelay =
992  std::chrono::duration_cast<std::chrono::seconds>(
994 
995  const int64_t refTime = std::min(pprev->GetBlockTime(), GetTime());
996 
997  const int64_t targetRegistrationTime = refTime - registrationDelay.count();
998  const int64_t maxRegistrationTime = refTime - minRegistrationDelay.count();
999  const int64_t minRegistrationTime = refTime - maxRegistrationDelay.count();
1000 
1001  const BlockHash prevblockhash = pprev->GetBlockHash();
1002 
1003  std::vector<ProofRef> selectedProofs;
1004  ProofRef firstCompliantProof = ProofRef();
1005  while (selectedProofs.size() < peers.size()) {
1006  double bestRewardRank = std::numeric_limits<double>::max();
1007  ProofRef selectedProof = ProofRef();
1008  int64_t selectedProofRegistrationTime{0};
1009  uint256 bestRewardHash;
1010 
1011  for (const Peer &peer : peers) {
1012  if (!peer.proof) {
1013  // Should never happen, continue
1014  continue;
1015  }
1016 
1017  if (!peer.hasFinalized ||
1018  peer.registration_time.count() >= maxRegistrationTime) {
1019  continue;
1020  }
1021 
1022  if (std::find_if(selectedProofs.begin(), selectedProofs.end(),
1023  [&peer](const ProofRef &proof) {
1024  return peer.getProofId() == proof->getId();
1025  }) != selectedProofs.end()) {
1026  continue;
1027  }
1028 
1029  uint256 proofRewardHash;
1030  CHash256()
1031  .Write(prevblockhash)
1032  .Write(peer.getProofId())
1033  .Finalize(proofRewardHash);
1034 
1035  if (proofRewardHash == uint256::ZERO) {
1036  // This either the result of an incredibly unlikely lucky hash,
1037  // or a the hash is getting abused. In this case, skip the
1038  // proof.
1039  LogPrintf(
1040  "Staking reward hash has a suspicious value of zero for "
1041  "proof %s and blockhash %s, skipping\n",
1042  peer.getProofId().ToString(), prevblockhash.ToString());
1043  continue;
1044  }
1045 
1046  // To make sure the selection is properly weighted according to the
1047  // proof score, we normalize the proofRewardHash to a number between
1048  // 0 and 1, then take the logarithm and divide by the weight. Since
1049  // it is scale-independent, we can simplify by removing constants
1050  // and use base 2 logarithm.
1051  // Inspired by: https://stackoverflow.com/a/30226926.
1052  double proofRewardRank =
1053  (256.0 -
1054  std::log2(UintToArith256(proofRewardHash).getdouble())) /
1055  peer.getScore();
1056 
1057  // The best ranking is the lowest ranking value
1058  if (proofRewardRank < bestRewardRank) {
1059  bestRewardRank = proofRewardRank;
1060  selectedProof = peer.proof;
1061  selectedProofRegistrationTime = peer.registration_time.count();
1062  bestRewardHash = proofRewardHash;
1063  }
1064 
1065  // Select the lowest reward hash then proofid in the unlikely case
1066  // of a collision.
1067  if (proofRewardRank == bestRewardRank &&
1068  (proofRewardHash < bestRewardHash ||
1069  (proofRewardHash == bestRewardHash &&
1070  peer.getProofId() < selectedProof->getId()))) {
1071  selectedProof = peer.proof;
1072  selectedProofRegistrationTime = peer.registration_time.count();
1073  bestRewardHash = proofRewardHash;
1074  }
1075  }
1076 
1077  if (!selectedProof) {
1078  // No winner
1079  break;
1080  }
1081 
1082  if (!firstCompliantProof &&
1083  selectedProofRegistrationTime < targetRegistrationTime) {
1084  firstCompliantProof = selectedProof;
1085  }
1086 
1087  selectedProofs.push_back(selectedProof);
1088 
1089  if (selectedProofRegistrationTime < minRegistrationTime &&
1090  !isFlaky(selectedProof->getId())) {
1091  break;
1092  }
1093  }
1094 
1095  winners.clear();
1096 
1097  if (!firstCompliantProof) {
1098  return false;
1099  }
1100 
1101  winners.reserve(selectedProofs.size());
1102 
1103  // Find the winner
1104  for (const ProofRef &proof : selectedProofs) {
1105  if (proof->getId() == firstCompliantProof->getId()) {
1106  winners.push_back({proof->getId(), proof->getPayoutScript()});
1107  }
1108  }
1109  // Add the others (if any) after the winner
1110  for (const ProofRef &proof : selectedProofs) {
1111  if (proof->getId() != firstCompliantProof->getId()) {
1112  winners.push_back({proof->getId(), proof->getPayoutScript()});
1113  }
1114  }
1115 
1116  return true;
1117 }
1118 
1119 bool PeerManager::setFlaky(const ProofId &proofid) {
1120  return manualFlakyProofids.insert(proofid).second;
1121 }
1122 
1123 bool PeerManager::unsetFlaky(const ProofId &proofid) {
1124  return manualFlakyProofids.erase(proofid) > 0;
1125 }
1126 
1127 bool PeerManager::isFlaky(const ProofId &proofid) const {
1128  if (localProof && proofid == localProof->getId()) {
1129  return false;
1130  }
1131 
1132  if (manualFlakyProofids.count(proofid) > 0) {
1133  return true;
1134  }
1135 
1136  // If we are missing connection to this proof, consider flaky
1137  if (forPeer(proofid,
1138  [](const Peer &peer) { return peer.node_count == 0; })) {
1139  return true;
1140  }
1141 
1142  auto &remoteProofsByNodeId = remoteProofs.get<by_nodeid>();
1143  auto &nview = nodes.get<next_request_time>();
1144 
1145  std::unordered_map<PeerId, std::unordered_set<ProofId, SaltedProofIdHasher>>
1146  missing_per_peer;
1147 
1148  // Construct a set of missing proof ids per peer
1149  double total_score{0};
1150  for (const Peer &peer : peers) {
1151  const PeerId peerid = peer.peerid;
1152 
1153  total_score += peer.getScore();
1154 
1155  auto nodes_range = nview.equal_range(peerid);
1156  for (auto &nit = nodes_range.first; nit != nodes_range.second; ++nit) {
1157  auto proofs_range = remoteProofsByNodeId.equal_range(nit->nodeid);
1158  for (auto &proofit = proofs_range.first;
1159  proofit != proofs_range.second; ++proofit) {
1160  if (!proofit->present) {
1161  missing_per_peer[peerid].insert(proofit->proofid);
1162  }
1163  }
1164  };
1165  }
1166 
1167  double missing_score{0};
1168 
1169  // Now compute a score for the missing proof
1170  for (const auto &[peerid, missingProofs] : missing_per_peer) {
1171  if (missingProofs.size() > 3) {
1172  // Ignore peers with too many missing proofs
1173  continue;
1174  }
1175 
1176  auto pit = peers.find(peerid);
1177  if (pit == peers.end()) {
1178  // Peer not found
1179  continue;
1180  }
1181 
1182  if (missingProofs.count(proofid) > 0) {
1183  missing_score += pit->getScore();
1184  }
1185  }
1186 
1187  return (missing_score / total_score) > 0.3;
1188 }
1189 
1190 std::optional<bool>
1192  auto &remoteProofsView = remoteProofs.get<by_proofid>();
1193  auto [begin, end] = remoteProofsView.equal_range(proofid);
1194 
1195  if (begin == end) {
1196  // No remote registered anything yet, we are on our own
1197  return std::nullopt;
1198  }
1199 
1200  double total_score{0};
1201  double present_score{0};
1202  double missing_score{0};
1203 
1204  for (auto it = begin; it != end; it++) {
1205  auto nit = nodes.find(it->nodeid);
1206  if (nit == nodes.end()) {
1207  // No such node
1208  continue;
1209  }
1210 
1211  const PeerId peerid = nit->peerid;
1212 
1213  auto pit = peers.find(peerid);
1214  if (pit == peers.end()) {
1215  // Peer not found
1216  continue;
1217  }
1218 
1219  uint32_t node_count = pit->node_count;
1220  if (localProof && pit->getProofId() == localProof->getId()) {
1221  // If that's our local proof, account for ourself
1222  ++node_count;
1223  }
1224 
1225  if (node_count == 0) {
1226  // should never happen
1227  continue;
1228  }
1229 
1230  const double score = double(pit->getScore()) / node_count;
1231 
1232  total_score += score;
1233  if (it->present) {
1234  present_score += score;
1235  } else {
1236  missing_score += score;
1237  }
1238  }
1239 
1240  if (localProof) {
1241  auto &peersByProofid = peers.get<by_proofid>();
1242 
1243  // Do we have a node connected for that proof ?
1244  bool present = false;
1245  auto pit = peersByProofid.find(proofid);
1246  if (pit != peersByProofid.end()) {
1247  present = pit->node_count > 0;
1248  }
1249 
1250  pit = peersByProofid.find(localProof->getId());
1251  if (pit != peersByProofid.end()) {
1252  // Also divide by node_count, we can have several nodes even for our
1253  // local proof.
1254  const double score =
1255  double(pit->getScore()) / (1 + pit->node_count);
1256 
1257  total_score += score;
1258  if (present) {
1259  present_score += score;
1260  } else {
1261  missing_score += score;
1262  }
1263  }
1264  }
1265 
1266  if (present_score / total_score > 0.55) {
1267  return std::make_optional(true);
1268  }
1269 
1270  if (missing_score / total_score > 0.55) {
1271  return std::make_optional(false);
1272  }
1273 
1274  return std::nullopt;
1275 }
1276 
1277 bool PeerManager::dumpPeersToFile(const fs::path &dumpPath) const {
1278  try {
1279  const fs::path dumpPathTmp = dumpPath + ".new";
1280  FILE *filestr = fsbridge::fopen(dumpPathTmp, "wb");
1281  if (!filestr) {
1282  return false;
1283  }
1284 
1285  CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
1286  file << PEERS_DUMP_VERSION;
1287  file << uint64_t(peers.size());
1288  for (const Peer &peer : peers) {
1289  file << peer.proof;
1290  file << peer.hasFinalized;
1291  file << int64_t(peer.registration_time.count());
1292  file << int64_t(peer.nextPossibleConflictTime.count());
1293  }
1294 
1295  if (!FileCommit(file.Get())) {
1296  throw std::runtime_error(strprintf("Failed to commit to file %s",
1297  PathToString(dumpPathTmp)));
1298  }
1299  file.fclose();
1300 
1301  if (!RenameOver(dumpPathTmp, dumpPath)) {
1302  throw std::runtime_error(strprintf("Rename failed from %s to %s",
1303  PathToString(dumpPathTmp),
1304  PathToString(dumpPath)));
1305  }
1306  } catch (const std::exception &e) {
1307  LogPrint(BCLog::AVALANCHE, "Failed to dump the avalanche peers: %s.\n",
1308  e.what());
1309  return false;
1310  }
1311 
1312  LogPrint(BCLog::AVALANCHE, "Successfully dumped %d peers to %s.\n",
1313  peers.size(), PathToString(dumpPath));
1314 
1315  return true;
1316 }
1317 
1319  const fs::path &dumpPath,
1320  std::unordered_set<ProofRef, SaltedProofHasher> &registeredProofs) {
1321  registeredProofs.clear();
1322 
1323  FILE *filestr = fsbridge::fopen(dumpPath, "rb");
1324  CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
1325  if (file.IsNull()) {
1327  "Failed to open avalanche peers file from disk.\n");
1328  return false;
1329  }
1330 
1331  try {
1332  uint64_t version;
1333  file >> version;
1334 
1335  if (version != PEERS_DUMP_VERSION) {
1337  "Unsupported avalanche peers file version.\n");
1338  return false;
1339  }
1340 
1341  uint64_t numPeers;
1342  file >> numPeers;
1343 
1344  auto &peersByProofId = peers.get<by_proofid>();
1345 
1346  for (uint64_t i = 0; i < numPeers; i++) {
1347  ProofRef proof;
1348  bool hasFinalized;
1349  int64_t registrationTime;
1350  int64_t nextPossibleConflictTime;
1351 
1352  file >> proof;
1353  file >> hasFinalized;
1354  file >> registrationTime;
1355  file >> nextPossibleConflictTime;
1356 
1357  if (registerProof(proof)) {
1358  auto it = peersByProofId.find(proof->getId());
1359  if (it == peersByProofId.end()) {
1360  // Should never happen
1361  continue;
1362  }
1363 
1364  // We don't modify any key so we don't need to rehash.
1365  // If the modify fails, it means we don't get the full benefit
1366  // from the file but we still added our peer to the set. The
1367  // non-overridden fields will be set the normal way.
1368  peersByProofId.modify(it, [&](Peer &p) {
1369  p.hasFinalized = hasFinalized;
1370  p.registration_time =
1371  std::chrono::seconds{registrationTime};
1373  std::chrono::seconds{nextPossibleConflictTime};
1374  });
1375 
1376  registeredProofs.insert(proof);
1377  }
1378  }
1379  } catch (const std::exception &e) {
1381  "Failed to read the avalanche peers file data on disk: %s.\n",
1382  e.what());
1383  return false;
1384  }
1385 
1386  return true;
1387 }
1388 
1389 } // namespace avalanche
ArgsManager gArgs
Definition: args.cpp:38
arith_uint256 UintToArith256(const uint256 &a)
static constexpr PeerId NO_PEER
Definition: node.h:16
uint32_t PeerId
Definition: node.h:15
static constexpr size_t AVALANCHE_DEFAULT_CONFLICTING_PROOF_COOLDOWN
Conflicting proofs cooldown time default value in seconds.
Definition: avalanche.h:28
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Return integer argument or default value.
Definition: args.cpp:526
bool IsNull() const
Return true if the wrapped FILE* is nullptr, false otherwise.
Definition: streams.h:570
FILE * Get() const
Get wrapped FILE* without transfer of ownership.
Definition: streams.h:567
int fclose()
Definition: streams.h:541
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: blockindex.h:25
int64_t GetBlockTime() const
Definition: blockindex.h:180
BlockHash GetBlockHash() const
Definition: blockindex.h:146
A hasher class for Bitcoin's 256-bit hash (double SHA-256).
Definition: hash.h:22
void Finalize(Span< uint8_t > output)
Definition: hash.h:29
CHash256 & Write(Span< const uint8_t > input)
Definition: hash.h:36
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:20
void insert(Span< const uint8_t > vKey)
Definition: bloom.cpp:215
bool contains(Span< const uint8_t > vKey) const
Definition: bloom.cpp:249
bool Invalid(Result result, const std::string &reject_reason="", const std::string &debug_message="")
Definition: validation.h:99
Result GetResult() const
Definition: validation.h:120
std::string ToString() const
Definition: validation.h:123
uint32_t connectedPeersScore
Definition: peermanager.h:239
std::vector< RemoteProof > getRemoteProofs(const NodeId nodeid) const
bool removeNode(NodeId nodeid)
bool setFinalized(PeerId peerid)
Latch on that this peer has a finalized proof.
bool dumpPeersToFile(const fs::path &dumpPath) const
RemoteProofSet remoteProofs
Remember which node sent which proof so we have an image of the proof set of our peers.
Definition: peermanager.h:281
bool isDangling(const ProofId &proofid) const
bool updateNextRequestTime(NodeId nodeid, SteadyMilliseconds timeout)
bool unsetFlaky(const ProofId &proofid)
std::optional< bool > getRemotePresenceStatus(const ProofId &proofid) const
Get the presence remote status of a proof.
bool addNodeToPeer(const PeerSet::iterator &it)
Definition: peermanager.cpp:87
bool exists(const ProofId &proofid) const
Definition: peermanager.h:402
PendingNodeSet pendingNodes
Definition: peermanager.h:225
bool verify() const
Perform consistency check on internal data structures.
bool forPeer(const ProofId &proofid, Callable &&func) const
Definition: peermanager.h:410
bool latchAvaproofsSent(NodeId nodeid)
Flag that a node did send its compact proofs.
bool addNode(NodeId nodeid, const ProofId &proofid)
Node API.
Definition: peermanager.cpp:31
static constexpr int SELECT_PEER_MAX_RETRY
Definition: peermanager.h:227
ProofIdSet m_unbroadcast_proofids
Track proof ids to broadcast.
Definition: peermanager.h:233
bool loadPeersFromFile(const fs::path &dumpPath, std::unordered_set< ProofRef, SaltedProofHasher > &registeredProofs)
RejectionMode
Rejection mode.
Definition: peermanager.h:394
void addUnbroadcastProof(const ProofId &proofid)
Proof broadcast API.
std::unordered_set< ProofRef, SaltedProofHasher > updatedBlockTip()
Update the peer set when a new block is connected.
void removeUnbroadcastProof(const ProofId &proofid)
bool isBoundToPeer(const ProofId &proofid) const
ProofRadixTree shareableProofs
Definition: peermanager.h:191
bool saveRemoteProof(const ProofId &proofid, const NodeId nodeid, const bool present)
CRollingBloomFilter invalidProofs
Filter for proofs that are consensus-invalid or were recently invalidated by avalanche (finalized rej...
Definition: peermanager.h:295
uint64_t compact()
Trigger maintenance of internal data structures.
std::vector< Slot > slots
Definition: peermanager.h:163
uint32_t totalPeersScore
Quorum management.
Definition: peermanager.h:238
ProofPool danglingProofPool
Definition: peermanager.h:188
void setInvalid(const ProofId &proofid)
bool isFlaky(const ProofId &proofid) const
ChainstateManager & chainman
Definition: peermanager.h:243
bool isInvalid(const ProofId &proofid) const
std::unordered_set< ProofId, SaltedProofIdHasher > manualFlakyProofids
Definition: peermanager.h:297
bool removePeer(const PeerId peerid)
Remove an existing peer.
bool isImmature(const ProofId &proofid) const
bool addOrUpdateNode(const PeerSet::iterator &it, NodeId nodeid)
Definition: peermanager.cpp:46
bool rejectProof(const ProofId &proofid, RejectionMode mode=RejectionMode::DEFAULT)
ProofPool immatureProofPool
Definition: peermanager.h:187
RegistrationMode
Registration mode.
Definition: peermanager.h:371
ProofPool conflictingProofPool
Definition: peermanager.h:186
static constexpr size_t MAX_REMOTE_PROOFS
Definition: peermanager.h:300
bool selectStakingRewardWinner(const CBlockIndex *pprev, std::vector< std::pair< ProofId, CScript >> &winners)
Deterministically select a list of payout scripts based on the proof set and the previous block hash.
bool setFlaky(const ProofId &proofid)
std::atomic< bool > needMoreNodes
Flag indicating that we failed to select a node and need to expand our node set.
Definition: peermanager.h:211
PeerId selectPeer() const
Randomly select a peer to poll.
bool isInConflictingPool(const ProofId &proofid) const
static constexpr int SELECT_NODE_MAX_RETRY
Definition: peermanager.h:228
void cleanupDanglingProofs(std::unordered_set< ProofRef, SaltedProofHasher > &registeredProofs)
ProofRef getProof(const ProofId &proofid) const
bool registerProof(const ProofRef &proof, ProofRegistrationState &registrationState, RegistrationMode mode=RegistrationMode::DEFAULT)
bool removeNodeFromPeer(const PeerSet::iterator &it, uint32_t count=1)
bool updateNextPossibleConflictTime(PeerId peerid, const std::chrono::seconds &nextTime)
Proof and Peer related API.
void moveToConflictingPool(const ProofContainer &proofs)
AddProofStatus addProofIfPreferred(const ProofRef &proof, ConflictingProofSet &conflictingProofs)
Attempt to add a proof to the pool.
Definition: proofpool.cpp:54
size_t size() const
Definition: proofpool.h:135
AddProofStatus addProofIfNoConflict(const ProofRef &proof, ConflictingProofSet &conflictingProofs)
Attempt to add a proof to the pool, and fail if there is a conflict on any UTXO.
Definition: proofpool.cpp:13
size_t countProofs() const
Definition: proofpool.cpp:129
bool removeProof(ProofId proofid)
Definition: proofpool.cpp:79
void forEachProof(Callable &&func) const
Definition: proofpool.h:118
ProofRef getProof(const ProofId &proofid) const
Definition: proofpool.cpp:112
std::set< ProofRef, ConflictingProofComparator > ConflictingProofSet
Definition: proofpool.h:88
ProofRef getLowestScoreProof() const
Definition: proofpool.cpp:123
std::unordered_set< ProofRef, SaltedProofHasher > rescan(PeerManager &peerManager)
Definition: proofpool.cpp:86
std::string ToString() const
Definition: uint256.h:80
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:30
256-bit opaque blob.
Definition: uint256.h:129
static const uint256 ZERO
Definition: uint256.h:134
static constexpr int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:38
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:7
bool RenameOver(fs::path src, fs::path dest)
Definition: fs_helpers.cpp:272
bool FileCommit(FILE *file)
Ensure file contents are fully committed to disk, using a platform-specific feature analogous to fsyn...
Definition: fs_helpers.cpp:125
#define LogPrint(category,...)
Definition: logging.h:211
#define LogPrintf(...)
Definition: logging.h:207
@ AVALANCHE
Definition: logging.h:62
ProofRegistrationResult
Definition: peermanager.h:145
static constexpr uint32_t AVALANCHE_MAX_IMMATURE_PROOFS
Maximum number of immature proofs the peer manager will accept from the network.
Definition: peermanager.h:44
static bool isImmatureState(const ProofValidationState &state)
static constexpr uint64_t PEERS_DUMP_VERSION
Definition: peermanager.cpp:29
PeerId selectPeerImpl(const std::vector< Slot > &slots, const uint64_t slot, const uint64_t max)
Internal methods that are exposed for testing purposes.
RCUPtr< const Proof > ProofRef
Definition: proof.h:185
static std::string PathToString(const path &path)
Convert path object to byte string.
Definition: fs.h:142
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:30
static constexpr NodeId NO_NODE
Special NodeId that represent no node.
Definition: nodeid.h:15
int64_t NodeId
Definition: nodeid.h:10
T GetRand(T nMax=std::numeric_limits< T >::max()) noexcept
Generate a uniform random integer of type T in the range [0..nMax) nMax defaults to std::numeric_limi...
Definition: random.h:85
@ SER_DISK
Definition: serialize.h:153
A BlockHash is a unqiue identifier for a block.
Definition: blockhash.h:13
RCUPtr< T > get(const KeyType &key)
Get the value corresponding to a key.
Definition: radix.h:118
RCUPtr< T > remove(const KeyType &key)
Remove an element from the tree.
Definition: radix.h:181
bool forEachLeaf(Callable &&func) const
Definition: radix.h:144
bool insert(const RCUPtr< T > &value)
Insert a value into the tree.
Definition: radix.h:112
Facility for using an uint256 as a radix tree key.
SteadyMilliseconds nextRequestTime
Definition: node.h:23
bool avaproofsSent
Definition: node.h:24
std::chrono::seconds registration_time
Definition: peermanager.h:93
std::chrono::seconds nextPossibleConflictTime
Definition: peermanager.h:94
uint32_t node_count
Definition: peermanager.h:87
static constexpr auto DANGLING_TIMEOUT
Consider dropping the peer if no node is attached after this timeout expired.
Definition: peermanager.h:102
uint32_t index
Definition: peermanager.h:86
uint32_t getScore() const
Definition: peermanager.h:111
ProofRef proof
Definition: peermanager.h:89
uint64_t getStop() const
Definition: peermanager.h:73
uint64_t getStart() const
Definition: peermanager.h:72
PeerId getPeerId() const
Definition: peermanager.h:75
#define LOCK(cs)
Definition: sync.h:306
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:357
static int count
Definition: tests.c:31
int64_t GetTime()
Definition: time.cpp:109
std::chrono::time_point< std::chrono::steady_clock, std::chrono::milliseconds > SteadyMilliseconds
Definition: time.h:31
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1202
assert(!tx.IsCoinBase())