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