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