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 <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/fs_helpers.h>
19 #include <util/system.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  std::vector<CScript> &winners) {
975  if (!pprev) {
976  return false;
977  }
978 
979  // Don't select proofs that have not been known for long enough, i.e. at
980  // least since twice the dangling proof cleanup timeout before the last
981  // block time, so we're sure to not account for proofs more recent than the
982  // previous block or lacking node connected.
983  // The previous block time is capped to now for the unlikely event the
984  // previous block time is in the future.
985  auto registrationDelay = std::chrono::duration_cast<std::chrono::seconds>(
987  auto maxRegistrationDelay =
988  std::chrono::duration_cast<std::chrono::seconds>(
990  auto minRegistrationDelay =
991  std::chrono::duration_cast<std::chrono::seconds>(
993 
994  const int64_t refTime = std::min(pprev->GetBlockTime(), GetTime());
995 
996  const int64_t targetRegistrationTime = refTime - registrationDelay.count();
997  const int64_t maxRegistrationTime = refTime - minRegistrationDelay.count();
998  const int64_t minRegistrationTime = refTime - maxRegistrationDelay.count();
999 
1000  const BlockHash prevblockhash = pprev->GetBlockHash();
1001 
1002  std::vector<ProofRef> selectedProofs;
1003  ProofRef firstCompliantProof = ProofRef();
1004  while (selectedProofs.size() < peers.size()) {
1005  double bestRewardRank = std::numeric_limits<double>::max();
1006  ProofRef selectedProof = ProofRef();
1007  int64_t selectedProofRegistrationTime{0};
1008  uint256 bestRewardHash;
1009 
1010  for (const Peer &peer : peers) {
1011  if (!peer.proof) {
1012  // Should never happen, continue
1013  continue;
1014  }
1015 
1016  if (!peer.hasFinalized ||
1017  peer.registration_time.count() >= maxRegistrationTime) {
1018  continue;
1019  }
1020 
1021  if (std::find_if(selectedProofs.begin(), selectedProofs.end(),
1022  [&peer](const ProofRef &proof) {
1023  return peer.getProofId() == proof->getId();
1024  }) != selectedProofs.end()) {
1025  continue;
1026  }
1027 
1028  uint256 proofRewardHash;
1029  CHash256()
1030  .Write(prevblockhash)
1031  .Write(peer.getProofId())
1032  .Finalize(proofRewardHash);
1033 
1034  if (proofRewardHash == uint256::ZERO) {
1035  // This either the result of an incredibly unlikely lucky hash,
1036  // or a the hash is getting abused. In this case, skip the
1037  // proof.
1038  LogPrintf(
1039  "Staking reward hash has a suspicious value of zero for "
1040  "proof %s and blockhash %s, skipping\n",
1041  peer.getProofId().ToString(), prevblockhash.ToString());
1042  continue;
1043  }
1044 
1045  // To make sure the selection is properly weighted according to the
1046  // proof score, we normalize the proofRewardHash to a number between
1047  // 0 and 1, then take the logarithm and divide by the weight. Since
1048  // it is scale-independent, we can simplify by removing constants
1049  // and use base 2 logarithm.
1050  // Inspired by: https://stackoverflow.com/a/30226926.
1051  double proofRewardRank =
1052  (256.0 -
1053  std::log2(UintToArith256(proofRewardHash).getdouble())) /
1054  peer.getScore();
1055 
1056  // The best ranking is the lowest ranking value
1057  if (proofRewardRank < bestRewardRank) {
1058  bestRewardRank = proofRewardRank;
1059  selectedProof = peer.proof;
1060  selectedProofRegistrationTime = peer.registration_time.count();
1061  bestRewardHash = proofRewardHash;
1062  }
1063 
1064  // Select the lowest reward hash then proofid in the unlikely case
1065  // of a collision.
1066  if (proofRewardRank == bestRewardRank &&
1067  (proofRewardHash < bestRewardHash ||
1068  (proofRewardHash == bestRewardHash &&
1069  peer.getProofId() < selectedProof->getId()))) {
1070  selectedProof = peer.proof;
1071  selectedProofRegistrationTime = peer.registration_time.count();
1072  bestRewardHash = proofRewardHash;
1073  }
1074  }
1075 
1076  if (!selectedProof) {
1077  // No winner
1078  break;
1079  }
1080 
1081  if (!firstCompliantProof &&
1082  selectedProofRegistrationTime < targetRegistrationTime) {
1083  firstCompliantProof = selectedProof;
1084  }
1085 
1086  selectedProofs.push_back(selectedProof);
1087 
1088  if (selectedProofRegistrationTime < minRegistrationTime &&
1089  !isFlaky(selectedProof->getId())) {
1090  break;
1091  }
1092  }
1093 
1094  winners.clear();
1095 
1096  if (!firstCompliantProof) {
1097  return false;
1098  }
1099 
1100  winners.reserve(selectedProofs.size());
1101 
1102  // Find the winner
1103  for (const ProofRef &proof : selectedProofs) {
1104  if (proof->getId() == firstCompliantProof->getId()) {
1105  winners.push_back(proof->getPayoutScript());
1106  }
1107  }
1108  // Add the others (if any) after the winner
1109  for (const ProofRef &proof : selectedProofs) {
1110  if (proof->getId() != firstCompliantProof->getId()) {
1111  winners.push_back(proof->getPayoutScript());
1112  }
1113  }
1114 
1115  return true;
1116 }
1117 
1118 bool PeerManager::isFlaky(const ProofId &proofid) const {
1119  if (localProof && proofid == localProof->getId()) {
1120  return false;
1121  }
1122 
1123  // If we are missing connection to this proof, consider flaky
1124  if (forPeer(proofid,
1125  [](const Peer &peer) { return peer.node_count == 0; })) {
1126  return true;
1127  }
1128 
1129  auto &remoteProofsByNodeId = remoteProofs.get<by_nodeid>();
1130  auto &nview = nodes.get<next_request_time>();
1131 
1132  std::unordered_map<PeerId, std::unordered_set<ProofId, SaltedProofIdHasher>>
1133  missing_per_peer;
1134 
1135  // Construct a set of missing proof ids per peer
1136  double total_score{0};
1137  for (const Peer &peer : peers) {
1138  const PeerId peerid = peer.peerid;
1139 
1140  total_score += peer.getScore();
1141 
1142  auto nodes_range = nview.equal_range(peerid);
1143  for (auto &nit = nodes_range.first; nit != nodes_range.second; ++nit) {
1144  auto proofs_range = remoteProofsByNodeId.equal_range(nit->nodeid);
1145  for (auto &proofit = proofs_range.first;
1146  proofit != proofs_range.second; ++proofit) {
1147  if (!proofit->present) {
1148  missing_per_peer[peerid].insert(proofit->proofid);
1149  }
1150  }
1151  };
1152  }
1153 
1154  double missing_score{0};
1155 
1156  // Now compute a score for the missing proof
1157  for (const auto &[peerid, missingProofs] : missing_per_peer) {
1158  if (missingProofs.size() > 3) {
1159  // Ignore peers with too many missing proofs
1160  continue;
1161  }
1162 
1163  auto pit = peers.find(peerid);
1164  if (pit == peers.end()) {
1165  // Peer not found
1166  continue;
1167  }
1168 
1169  if (missingProofs.count(proofid) > 0) {
1170  missing_score += pit->getScore();
1171  }
1172  }
1173 
1174  return (missing_score / total_score) > 0.3;
1175 }
1176 
1177 std::optional<bool>
1179  auto &remoteProofsView = remoteProofs.get<by_proofid>();
1180  auto [begin, end] = remoteProofsView.equal_range(proofid);
1181 
1182  if (begin == end) {
1183  // No remote registered anything yet, we are on our own
1184  return std::nullopt;
1185  }
1186 
1187  double total_score{0};
1188  double present_score{0};
1189  double missing_score{0};
1190 
1191  for (auto it = begin; it != end; it++) {
1192  auto nit = nodes.find(it->nodeid);
1193  if (nit == nodes.end()) {
1194  // No such node
1195  continue;
1196  }
1197 
1198  const PeerId peerid = nit->peerid;
1199 
1200  auto pit = peers.find(peerid);
1201  if (pit == peers.end()) {
1202  // Peer not found
1203  continue;
1204  }
1205 
1206  uint32_t node_count = pit->node_count;
1207  if (localProof && pit->getProofId() == localProof->getId()) {
1208  // If that's our local proof, account for ourself
1209  ++node_count;
1210  }
1211 
1212  if (node_count == 0) {
1213  // should never happen
1214  continue;
1215  }
1216 
1217  const double score = double(pit->getScore()) / node_count;
1218 
1219  total_score += score;
1220  if (it->present) {
1221  present_score += score;
1222  } else {
1223  missing_score += score;
1224  }
1225  }
1226 
1227  if (localProof) {
1228  auto &peersByProofid = peers.get<by_proofid>();
1229 
1230  // Do we have a node connected for that proof ?
1231  bool present = false;
1232  auto pit = peersByProofid.find(proofid);
1233  if (pit != peersByProofid.end()) {
1234  present = pit->node_count > 0;
1235  }
1236 
1237  pit = peersByProofid.find(localProof->getId());
1238  if (pit != peersByProofid.end()) {
1239  // Also divide by node_count, we can have several nodes even for our
1240  // local proof.
1241  const double score =
1242  double(pit->getScore()) / (1 + pit->node_count);
1243 
1244  total_score += score;
1245  if (present) {
1246  present_score += score;
1247  } else {
1248  missing_score += score;
1249  }
1250  }
1251  }
1252 
1253  if (present_score / total_score > 0.55) {
1254  return std::make_optional(true);
1255  }
1256 
1257  if (missing_score / total_score > 0.55) {
1258  return std::make_optional(false);
1259  }
1260 
1261  return std::nullopt;
1262 }
1263 
1264 bool PeerManager::dumpPeersToFile(const fs::path &dumpPath) const {
1265  try {
1266  const fs::path dumpPathTmp = dumpPath + ".new";
1267  FILE *filestr = fsbridge::fopen(dumpPathTmp, "wb");
1268  if (!filestr) {
1269  return false;
1270  }
1271 
1272  CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
1273  file << PEERS_DUMP_VERSION;
1274  file << uint64_t(peers.size());
1275  for (const Peer &peer : peers) {
1276  file << peer.proof;
1277  file << peer.hasFinalized;
1278  file << int64_t(peer.registration_time.count());
1279  file << int64_t(peer.nextPossibleConflictTime.count());
1280  }
1281 
1282  if (!FileCommit(file.Get())) {
1283  throw std::runtime_error(strprintf("Failed to commit to file %s",
1284  PathToString(dumpPathTmp)));
1285  }
1286  file.fclose();
1287 
1288  if (!RenameOver(dumpPathTmp, dumpPath)) {
1289  throw std::runtime_error(strprintf("Rename failed from %s to %s",
1290  PathToString(dumpPathTmp),
1291  PathToString(dumpPath)));
1292  }
1293  } catch (const std::exception &e) {
1294  LogPrint(BCLog::AVALANCHE, "Failed to dump the avalanche peers: %s.\n",
1295  e.what());
1296  return false;
1297  }
1298 
1299  LogPrint(BCLog::AVALANCHE, "Successfully dumped %d peers to %s.\n",
1300  peers.size(), PathToString(dumpPath));
1301 
1302  return true;
1303 }
1304 
1306  const fs::path &dumpPath,
1307  std::unordered_set<ProofRef, SaltedProofHasher> &registeredProofs) {
1308  registeredProofs.clear();
1309 
1310  FILE *filestr = fsbridge::fopen(dumpPath, "rb");
1311  CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
1312  if (file.IsNull()) {
1314  "Failed to open avalanche peers file from disk.\n");
1315  return false;
1316  }
1317 
1318  try {
1319  uint64_t version;
1320  file >> version;
1321 
1322  if (version != PEERS_DUMP_VERSION) {
1324  "Unsupported avalanche peers file version.\n");
1325  return false;
1326  }
1327 
1328  uint64_t numPeers;
1329  file >> numPeers;
1330 
1331  auto &peersByProofId = peers.get<by_proofid>();
1332 
1333  for (uint64_t i = 0; i < numPeers; i++) {
1334  ProofRef proof;
1335  bool hasFinalized;
1336  int64_t registrationTime;
1337  int64_t nextPossibleConflictTime;
1338 
1339  file >> proof;
1340  file >> hasFinalized;
1341  file >> registrationTime;
1342  file >> nextPossibleConflictTime;
1343 
1344  if (registerProof(proof)) {
1345  auto it = peersByProofId.find(proof->getId());
1346  if (it == peersByProofId.end()) {
1347  // Should never happen
1348  continue;
1349  }
1350 
1351  // We don't modify any key so we don't need to rehash.
1352  // If the modify fails, it means we don't get the full benefit
1353  // from the file but we still added our peer to the set. The
1354  // non-overridden fields will be set the normal way.
1355  peersByProofId.modify(it, [&](Peer &p) {
1356  p.hasFinalized = hasFinalized;
1357  p.registration_time =
1358  std::chrono::seconds{registrationTime};
1360  std::chrono::seconds{nextPossibleConflictTime};
1361  });
1362 
1363  registeredProofs.insert(proof);
1364  }
1365  }
1366  } catch (const std::exception &e) {
1368  "Failed to read the avalanche peers file data on disk: %s.\n",
1369  e.what());
1370  return false;
1371  }
1372 
1373  return true;
1374 }
1375 
1376 } // namespace avalanche
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:546
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: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:87
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: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: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:46
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
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: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: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: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
ArgsManager gArgs
Definition: system.cpp:68
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())