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