Bitcoin ABC 0.34.2
P2P Digital Currency
processor.cpp
Go to the documentation of this file.
1// Copyright (c) 2018-2019 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
13#include <chain.h>
14#include <common/args.h>
16#include <key_io.h> // For DecodeSecret
17#include <net.h>
18#include <netbase.h>
19#include <netmessagemaker.h>
21#include <scheduler.h>
22#include <util/bitmanip.h>
23#include <util/moneystr.h>
24#include <util/time.h>
25#include <util/translation.h>
26#include <validation.h>
27
28#include <algorithm>
29#include <chrono>
30#include <limits>
31#include <tuple>
32
36static constexpr std::chrono::milliseconds AVALANCHE_TIME_STEP{10};
37
38static const std::string AVAPEERS_FILE_NAME{"avapeers.dat"};
39
40namespace avalanche {
41static uint256 GetVoteItemId(const AnyVoteItem &item) {
42 return std::visit(variant::overloaded{
43 [](const ProofRef &proof) {
44 uint256 id = proof->getId();
45 return id;
46 },
47 [](const CBlockIndex *pindex) {
48 uint256 hash = pindex->GetBlockHash();
49 return hash;
50 },
51 [](const StakeContenderId &contenderId) {
52 return uint256(contenderId);
53 },
54 [](const CTransactionRef &tx) {
55 uint256 id = tx->GetId();
56 return id;
57 },
58 },
59 item);
60}
61
62static bool VerifyProof(const Amount &stakeUtxoDustThreshold,
63 const Proof &proof, bilingual_str &error) {
64 ProofValidationState proof_state;
65
66 if (!proof.verify(stakeUtxoDustThreshold, proof_state)) {
67 switch (proof_state.GetResult()) {
69 error = _("The avalanche proof has no stake.");
70 return false;
72 error = _("The avalanche proof stake is too low.");
73 return false;
75 error = _("The avalanche proof has duplicated stake.");
76 return false;
78 error = _("The avalanche proof has invalid stake signatures.");
79 return false;
81 error = strprintf(
82 _("The avalanche proof has too many utxos (max: %u)."),
84 return false;
85 default:
86 error = _("The avalanche proof is invalid.");
87 return false;
88 }
89 }
90
91 return true;
92}
93
94static bool VerifyDelegation(const Delegation &dg,
95 const CPubKey &expectedPubKey,
96 bilingual_str &error) {
97 DelegationState dg_state;
98
99 CPubKey auth;
100 if (!dg.verify(dg_state, auth)) {
101 switch (dg_state.GetResult()) {
103 error = _("The avalanche delegation has invalid signatures.");
104 return false;
106 error = _(
107 "The avalanche delegation has too many delegation levels.");
108 return false;
109 default:
110 error = _("The avalanche delegation is invalid.");
111 return false;
112 }
113 }
114
115 if (auth != expectedPubKey) {
116 error = _(
117 "The avalanche delegation does not match the expected public key.");
118 return false;
119 }
120
121 return true;
122}
123
127
130};
131
135
136public:
138
140
142 uint64_t mempool_sequence) override {
144 }
145};
146
148 CConnman *connmanIn, ChainstateManager &chainmanIn,
149 CTxMemPool *mempoolIn, CScheduler &scheduler,
150 std::unique_ptr<PeerData> peerDataIn, CKey sessionKeyIn,
151 uint32_t minQuorumTotalScoreIn,
152 double minQuorumConnectedScoreRatioIn,
153 int64_t minAvaproofsNodeCountIn,
154 uint32_t staleVoteThresholdIn, uint32_t staleVoteFactorIn,
155 Amount stakeUtxoDustThreshold, bool preConsensus,
156 bool stakingPreConsensus, size_t maxElementPoll)
157 : avaconfig(std::move(avaconfigIn)), connman(connmanIn),
158 chainman(chainmanIn), mempool(mempoolIn), round(0),
159 peerManager(std::make_unique<PeerManager>(
160 stakeUtxoDustThreshold, chainman, stakingPreConsensus,
161 peerDataIn ? peerDataIn->proof : ProofRef())),
162 peerData(std::move(peerDataIn)), sessionKey(std::move(sessionKeyIn)),
163 minQuorumScore(minQuorumTotalScoreIn),
164 minQuorumConnectedScoreRatio(minQuorumConnectedScoreRatioIn),
165 minAvaproofsNodeCount(minAvaproofsNodeCountIn),
166 staleVoteThreshold(staleVoteThresholdIn),
167 staleVoteFactor(staleVoteFactorIn), m_preConsensus(preConsensus),
168 m_stakingPreConsensus(stakingPreConsensus),
169 m_maxElementPoll(maxElementPoll) {
170 // Make sure we get notified of chain state changes.
172 chain.handleNotifications(std::make_shared<NotificationsHandler>(this));
173
174 scheduler.scheduleEvery(
175 [this]() -> bool {
176 std::unordered_set<ProofRef, SaltedProofHasher> registeredProofs;
178 peerManager->cleanupDanglingProofs(registeredProofs));
179 for (const auto &proof : registeredProofs) {
181 "Promoting previously dangling proof %s\n",
182 proof->getId().ToString());
183 reconcileOrFinalize(proof);
184 }
185 return true;
186 },
187 5min);
188
189 if (!gArgs.GetBoolArg("-persistavapeers", DEFAULT_PERSIST_AVAPEERS)) {
190 return;
191 }
192
193 std::unordered_set<ProofRef, SaltedProofHasher> registeredProofs;
194
195 // Attempt to load the peer file if it exists.
196 const fs::path dumpPath = gArgs.GetDataDirNet() / AVAPEERS_FILE_NAME;
197 WITH_LOCK(cs_peerManager, return peerManager->loadPeersFromFile(
198 dumpPath, registeredProofs));
199
200 // We just loaded the previous finalization status, but make sure to trigger
201 // another round of vote for these proofs to avoid issue if the network
202 // status changed since the peers file was dumped.
203 for (const auto &proof : registeredProofs) {
204 addToReconcile(proof);
205 }
206
207 LogDebug(BCLog::AVALANCHE, "Loaded %d peers from the %s file\n",
208 registeredProofs.size(), PathToString(dumpPath));
209}
210
212 chainNotificationsHandler->disconnect();
215
216 if (!gArgs.GetBoolArg("-persistavapeers", DEFAULT_PERSIST_AVAPEERS)) {
217 return;
218 }
219
221 // Discard the status output: if it fails we want to continue normally.
222 peerManager->dumpPeersToFile(gArgs.GetDataDirNet() / AVAPEERS_FILE_NAME);
223}
224
225std::unique_ptr<Processor>
227 CConnman *connman, ChainstateManager &chainman,
228 CTxMemPool *mempool, CScheduler &scheduler,
229 bilingual_str &error) {
230 std::unique_ptr<PeerData> peerData;
231 CKey masterKey;
233
234 Amount stakeUtxoDustThreshold = PROOF_DUST_THRESHOLD;
235 if (argsman.IsArgSet("-avaproofstakeutxodustthreshold") &&
236 !ParseMoney(argsman.GetArg("-avaproofstakeutxodustthreshold", ""),
237 stakeUtxoDustThreshold)) {
238 error = _("The avalanche stake utxo dust threshold amount is invalid.");
239 return nullptr;
240 }
241
242 if (argsman.IsArgSet("-avasessionkey")) {
243 sessionKey = DecodeSecret(argsman.GetArg("-avasessionkey", ""));
244 if (!sessionKey.IsValid()) {
245 error = _("The avalanche session key is invalid.");
246 return nullptr;
247 }
248 } else {
249 // Pick a random key for the session.
251 }
252
253 if (argsman.IsArgSet("-avaproof")) {
254 if (!argsman.IsArgSet("-avamasterkey")) {
255 error = _(
256 "The avalanche master key is missing for the avalanche proof.");
257 return nullptr;
258 }
259
260 masterKey = DecodeSecret(argsman.GetArg("-avamasterkey", ""));
261 if (!masterKey.IsValid()) {
262 error = _("The avalanche master key is invalid.");
263 return nullptr;
264 }
265
266 auto proof = RCUPtr<Proof>::make();
267 if (!Proof::FromHex(*proof, argsman.GetArg("-avaproof", ""), error)) {
268 // error is set by FromHex
269 return nullptr;
270 }
271
272 peerData = std::make_unique<PeerData>();
273 peerData->proof = proof;
274 if (!VerifyProof(stakeUtxoDustThreshold, *peerData->proof, error)) {
275 // error is set by VerifyProof
276 return nullptr;
277 }
278
279 std::unique_ptr<DelegationBuilder> dgb;
280 const CPubKey &masterPubKey = masterKey.GetPubKey();
281
282 if (argsman.IsArgSet("-avadelegation")) {
283 Delegation dg;
284 if (!Delegation::FromHex(dg, argsman.GetArg("-avadelegation", ""),
285 error)) {
286 // error is set by FromHex()
287 return nullptr;
288 }
289
290 if (dg.getProofId() != peerData->proof->getId()) {
291 error = _("The delegation does not match the proof.");
292 return nullptr;
293 }
294
295 if (masterPubKey != dg.getDelegatedPubkey()) {
296 error = _(
297 "The master key does not match the delegation public key.");
298 return nullptr;
299 }
300
301 dgb = std::make_unique<DelegationBuilder>(dg);
302 } else {
303 if (masterPubKey != peerData->proof->getMaster()) {
304 error =
305 _("The master key does not match the proof public key.");
306 return nullptr;
307 }
308
309 dgb = std::make_unique<DelegationBuilder>(*peerData->proof);
310 }
311
312 // Generate the delegation to the session key.
313 const CPubKey sessionPubKey = sessionKey.GetPubKey();
314 if (sessionPubKey != masterPubKey) {
315 if (!dgb->addLevel(masterKey, sessionPubKey)) {
316 error = _("Failed to generate a delegation for this session.");
317 return nullptr;
318 }
319 }
320 peerData->delegation = dgb->build();
321
322 if (!VerifyDelegation(peerData->delegation, sessionPubKey, error)) {
323 // error is set by VerifyDelegation
324 return nullptr;
325 }
326 }
327
328 const auto queryTimeoutDuration =
329 std::chrono::milliseconds(argsman.GetIntArg(
330 "-avatimeout", AVALANCHE_DEFAULT_QUERY_TIMEOUT.count()));
331
332 // Determine quorum parameters
334 if (argsman.IsArgSet("-avaminquorumstake") &&
335 !ParseMoney(argsman.GetArg("-avaminquorumstake", ""), minQuorumStake)) {
336 error = _("The avalanche min quorum stake amount is invalid.");
337 return nullptr;
338 }
339
340 if (!MoneyRange(minQuorumStake)) {
341 error = _("The avalanche min quorum stake amount is out of range.");
342 return nullptr;
343 }
344
345 double minQuorumConnectedStakeRatio =
347 if (argsman.IsArgSet("-avaminquorumconnectedstakeratio")) {
348 // Parse the parameter with a precision of 0.000001.
349 int64_t megaMinRatio;
350 if (!ParseFixedPoint(
351 argsman.GetArg("-avaminquorumconnectedstakeratio", ""), 6,
352 &megaMinRatio)) {
353 error =
354 _("The avalanche min quorum connected stake ratio is invalid.");
355 return nullptr;
356 }
357 minQuorumConnectedStakeRatio = double(megaMinRatio) / 1000000;
358 }
359
360 if (minQuorumConnectedStakeRatio < 0 || minQuorumConnectedStakeRatio > 1) {
361 error = _(
362 "The avalanche min quorum connected stake ratio is out of range.");
363 return nullptr;
364 }
365
366 int64_t minAvaproofsNodeCount =
367 argsman.GetIntArg("-avaminavaproofsnodecount",
369 if (minAvaproofsNodeCount < 0) {
370 error = _("The minimum number of node that sent avaproofs message "
371 "should be non-negative");
372 return nullptr;
373 }
374
375 // Determine voting parameters
376 int64_t staleVoteThreshold = argsman.GetIntArg(
377 "-avastalevotethreshold", AVALANCHE_VOTE_STALE_THRESHOLD);
379 error = strprintf(_("The avalanche stale vote threshold must be "
380 "greater than or equal to %d"),
382 return nullptr;
383 }
384 if (staleVoteThreshold > std::numeric_limits<uint32_t>::max()) {
385 error = strprintf(_("The avalanche stale vote threshold must be less "
386 "than or equal to %d"),
387 std::numeric_limits<uint32_t>::max());
388 return nullptr;
389 }
390
391 int64_t staleVoteFactor =
392 argsman.GetIntArg("-avastalevotefactor", AVALANCHE_VOTE_STALE_FACTOR);
393 if (staleVoteFactor <= 0) {
394 error = _("The avalanche stale vote factor must be greater than 0");
395 return nullptr;
396 }
397 if (staleVoteFactor > std::numeric_limits<uint32_t>::max()) {
398 error = strprintf(_("The avalanche stale vote factor must be less than "
399 "or equal to %d"),
400 std::numeric_limits<uint32_t>::max());
401 return nullptr;
402 }
403
404 Config avaconfig(queryTimeoutDuration);
405
406 int64_t maxElementPoll = argsman.GetIntArg(
407 "-avamaxelementpoll", DEFAULT_AVALANCHE_MAX_ELEMENT_POLL);
408 if (maxElementPoll < int64_t{AVALANCHE_MAX_ELEMENT_POLL_LEGACY} ||
409 maxElementPoll > std::numeric_limits<uint32_t>::max()) {
410 error = strprintf(
411 _("The -avamaxelementpoll value must be between %d and %d"),
413 std::numeric_limits<uint32_t>::max());
414 return nullptr;
415 }
416
417 // We can't use std::make_unique with a private constructor
418 return std::unique_ptr<Processor>(new Processor(
419 std::move(avaconfig), chain, connman, chainman, mempool, scheduler,
420 std::move(peerData), std::move(sessionKey),
421 Proof::amountToScore(minQuorumStake), minQuorumConnectedStakeRatio,
423 stakeUtxoDustThreshold,
424 argsman.GetBoolArg("-avalanchepreconsensus",
426 argsman.GetBoolArg("-avalanchestakingpreconsensus",
428 // This is safe because we ensure size_t is >= 32 bits in assumptions.h
429 static_cast<size_t>(maxElementPoll)));
430}
431
432static bool isNull(const AnyVoteItem &item) {
433 return item.valueless_by_exception() ||
434 std::visit(variant::overloaded{
435 [](const StakeContenderId &contenderId) {
436 return contenderId == uint256::ZERO;
437 },
438 [](const auto &item) { return item == nullptr; },
439 },
440 item);
441};
442
444 if (isNull(item)) {
445 return false;
446 }
447
448 if (!isWorthPolling(item)) {
449 return false;
450 }
451
452 // getLocalAcceptance() takes the voteRecords read lock, so we can't inline
453 // the calls or we get a deadlock.
454 const bool accepted = getLocalAcceptance(item);
455
457 ->insert(std::make_pair(item, VoteRecord(accepted)))
458 .second;
459}
460
462 if (!proof) {
463 return false;
464 }
465
466 if (isRecentlyFinalized(proof->getId())) {
467 PeerId peerid;
469 if (peerManager->forPeer(proof->getId(), [&](const Peer &peer) {
470 peerid = peer.peerid;
471 return true;
472 })) {
473 return peerManager->setFinalized(peerid);
474 }
475 }
476
477 return addToReconcile(proof);
478}
479
480bool Processor::isAccepted(const AnyVoteItem &item) const {
481 if (isNull(item)) {
482 return false;
483 }
484
485 auto r = voteRecords.getReadView();
486 auto it = r->find(item);
487 if (it == r.end()) {
488 return false;
489 }
490
491 return it->second.isAccepted();
492}
493
494int Processor::getConfidence(const AnyVoteItem &item) const {
495 if (isNull(item)) {
496 return -1;
497 }
498
499 auto r = voteRecords.getReadView();
500 auto it = r->find(item);
501 if (it == r.end()) {
502 return -1;
503 }
504
505 return it->second.getConfidence();
506}
507
508bool Processor::isPolled(const AnyVoteItem &item) const {
509 if (isNull(item)) {
510 return false;
511 }
512
513 auto r = voteRecords.getReadView();
514 auto it = r->find(item);
515 return it != r.end();
516}
517
518bool Processor::isRecentlyFinalized(const uint256 &itemId) const {
519 return WITH_LOCK(cs_finalizedItems, return finalizedItems.contains(itemId));
520}
521
523 WITH_LOCK(cs_finalizedItems, finalizedItems.insert(itemId));
524}
525
528 finalizedItems.reset();
529}
530
531namespace {
536 class TCPResponse {
537 Response response;
539
540 public:
541 TCPResponse(Response responseIn, const CKey &key)
542 : response(std::move(responseIn)) {
543 HashWriter hasher{};
544 hasher << response;
545 const uint256 hash = hasher.GetHash();
546
547 // Now let's sign!
548 if (!key.SignSchnorr(hash, sig)) {
549 sig.fill(0);
550 }
551 }
552
553 // serialization support
554 SERIALIZE_METHODS(TCPResponse, obj) {
555 READWRITE(obj.response, obj.sig);
556 }
557 };
558} // namespace
559
563 TCPResponse(std::move(response), sessionKey)));
564}
565
567 std::vector<VoteItemUpdate> &updates,
568 bool &disconnect, std::string &error) {
569 disconnect = false;
570 updates.clear();
571
572 // Save the time at which we can query again.
574 peerManager->updateNextRequestTimeForResponse(nodeid, response));
575
576 std::vector<CInv> invs;
577
578 {
579 // Check that the query exists. There is a possibility that it has been
580 // deleted if the query timed out, so we don't disconnect for poor
581 // networking over time.
582 // Disconnecting has to be handled at callsite to avoid DoS.
583 auto w = queries.getWriteView();
584 auto it = w->find(std::make_tuple(nodeid, response.getRound()));
585 if (it == w.end()) {
586 error = "unexpected-ava-response";
587 return false;
588 }
589
590 invs = std::move(it->invs);
591 w->erase(it);
592 }
593
594 // Verify that the request and the vote are consistent.
595 const std::vector<Vote> &votes = response.GetVotes();
596 size_t size = invs.size();
597 if (votes.size() != size) {
598 disconnect = true;
599 error = "invalid-ava-response-size";
601 return false;
602 }
603
604 for (size_t i = 0; i < size; i++) {
605 if (invs[i].hash != votes[i].GetHash()) {
606 disconnect = true;
607 error = "invalid-ava-response-content";
609 return false;
610 }
611 }
612
613 std::map<AnyVoteItem, Vote, VoteMapComparator> responseItems;
614
615 // At this stage we are certain that invs[i] matches votes[i], so we can use
616 // the inv type to retrieve what is being voted on.
617 for (size_t i = 0; i < size; i++) {
618 auto item = getVoteItemFromInv(invs[i]);
619
620 if (isNull(item)) {
621 // This should not happen, but just in case...
622 continue;
623 }
624
625 if (!isWorthPolling(item)) {
626 // There is no point polling this item.
627 continue;
628 }
629
630 responseItems.insert(std::make_pair(std::move(item), votes[i]));
631 }
632
633 auto voteRecordsWriteView = voteRecords.getWriteView();
634
635 // Register votes.
636 for (const auto &p : responseItems) {
637 auto item = p.first;
638 const Vote &v = p.second;
639
640 auto it = voteRecordsWriteView->find(item);
641 if (it == voteRecordsWriteView.end()) {
642 // We are not voting on that item anymore.
643 continue;
644 }
645
646 auto &vr = it->second;
647 if (!vr.registerVote(nodeid, v.GetError())) {
648 if (vr.isStale(staleVoteThreshold, staleVoteFactor)) {
649 updates.emplace_back(std::move(item), VoteStatus::Stale);
650
651 // Just drop stale votes. If we see this item again, we'll
652 // do a new vote.
653 voteRecordsWriteView->erase(it);
654 }
655 // This vote did not provide any extra information, move on.
656 continue;
657 }
658
659 if (!vr.hasFinalized()) {
660 // This item has not been finalized, so we have nothing more to
661 // do.
662 updates.emplace_back(std::move(item), vr.isAccepted()
665 continue;
666 }
667
668 // We just finalized a vote. If it is valid, then let the caller
669 // know. Either way, remove the item from the map.
670 updates.emplace_back(std::move(item), vr.isAccepted()
673 voteRecordsWriteView->erase(it);
674 }
675
676 // FIXME This doesn't belong here as it has nothing to do with vote
677 // registration.
678 for (const auto &update : updates) {
679 if (update.getStatus() != VoteStatus::Finalized &&
680 update.getStatus() != VoteStatus::Invalid) {
681 continue;
682 }
683
684 const auto &item = update.getVoteItem();
685
686 if (!std::holds_alternative<const CBlockIndex *>(item)) {
687 continue;
688 }
689
690 if (update.getStatus() == VoteStatus::Invalid) {
691 // Track invalidated blocks. Other invalidated types are not
692 // tracked because they may be rejected for transient reasons
693 // (ex: immature proofs or orphaned txs) With blocks this is not
694 // the case. A rejected block will not be mined on. To prevent
695 // reorgs, invalidated blocks should never be polled again.
697 invalidatedBlocks.insert(GetVoteItemId(item));
698 continue;
699 }
700
701 // At this point the block index can only be finalized
702 const CBlockIndex *pindex = std::get<const CBlockIndex *>(item);
704 if (finalizationTip &&
705 finalizationTip->GetAncestor(pindex->nHeight) == pindex) {
706 continue;
707 }
708
709 finalizationTip = pindex;
710 }
711
712 return true;
713}
714
716 return sessionKey.GetPubKey();
717}
718
721
722 Delegation delegation;
723 if (peerData) {
724 if (!canShareLocalProof()) {
725 if (!delayedAvahelloNodeIds.emplace(pfrom->GetId()).second) {
726 // Nothing to do
727 return false;
728 }
729 } else {
730 delegation = peerData->delegation;
731 }
732 }
733
734 HashWriter hasher{};
735 hasher << delegation.getId();
736 hasher << pfrom->GetLocalNonce();
737 hasher << pfrom->nRemoteHostNonce;
738 hasher << pfrom->GetLocalExtraEntropy();
739 hasher << pfrom->nRemoteExtraEntropy;
740
741 // Now let's sign!
743 if (!sessionKey.SignSchnorr(hasher.GetHash(), sig)) {
744 return false;
745 }
746
749 Hello(delegation, sig, m_maxElementPoll)));
750
751 return delegation.getLimitedProofId() != uint256::ZERO;
752}
753
756 return sendHelloInternal(pfrom));
757}
758
761
762 auto it = delayedAvahelloNodeIds.begin();
763 while (it != delayedAvahelloNodeIds.end()) {
764 if (connman->ForNode(*it, [&](CNode *pnode) EXCLUSIVE_LOCKS_REQUIRED(
766 return sendHelloInternal(pnode);
767 })) {
768 // Our proof has been announced to this node
769 it = delayedAvahelloNodeIds.erase(it);
770 } else {
771 ++it;
772 }
773 }
774}
775
777 return peerData ? peerData->proof : ProofRef();
778}
779
782
784 if (!peerData) {
785 return state;
786 }
787
788 if (peerData->proof) {
790
791 const ProofId &proofid = peerData->proof->getId();
792
793 if (peerManager->isInConflictingPool(proofid)) {
795 "conflicting-utxos");
796 return state;
797 }
798
799 if (peerManager->isInvalid(proofid)) {
800 // If proof is invalid but verifies valid, it's been rejected by
801 // avalanche
803 "avalanche-invalidated");
804 return state;
805 }
806 }
807
808 return WITH_LOCK(peerData->cs_proofState, return peerData->proofState);
809}
810
813 scheduler, [this]() { this->runEventLoop(); }, AVALANCHE_TIME_STEP);
814}
815
817 return eventLoop.stopEventLoop();
818}
819
822
824 // Before IBD is complete there is no way to make sure a proof is valid
825 // or not, e.g. it can be spent in a block we don't know yet. In order
826 // to increase confidence that our proof set is similar to other nodes
827 // on the network, the messages received during IBD are not accounted.
828 return;
829 }
830
832 if (peerManager->latchAvaproofsSent(nodeid)) {
834 }
835}
836
837/*
838 * Returns a bool indicating whether we have a usable Avalanche quorum enabling
839 * us to take decisions based on polls.
840 */
843
844 {
846 if (peerManager->getNodeCount() < 8) {
847 // There is no point polling if we know the vote cannot converge
848 return false;
849 }
850 }
851
852 /*
853 * The following parameters can naturally go temporarly below the threshold
854 * under normal circumstances, like during a proof replacement with a lower
855 * stake amount, or the discovery of a new proofs for which we don't have a
856 * node yet.
857 * In order to prevent our node from starting and stopping the polls
858 * spuriously on such event, the quorum establishement is latched. The only
859 * parameters that should not latched is the minimum node count, as this
860 * would cause the poll to be inconclusive anyway and should not happen
861 * under normal circumstances.
862 */
864 return true;
865 }
866
867 // Don't do Avalanche while node is IBD'ing
869 return false;
870 }
871
873 return false;
874 }
875
876 auto localProof = getLocalProof();
877
878 // Get the registered proof score and registered score we have nodes for
879 uint32_t totalPeersScore;
880 uint32_t connectedPeersScore;
881 {
883 totalPeersScore = peerManager->getTotalPeersScore();
884 connectedPeersScore = peerManager->getConnectedPeersScore();
885
886 // Consider that we are always connected to our proof, even if we are
887 // the single node using that proof.
888 if (localProof &&
889 peerManager->forPeer(localProof->getId(), [](const Peer &peer) {
890 return peer.node_count == 0;
891 })) {
892 connectedPeersScore += localProof->getScore();
893 }
894 }
895
896 // Ensure enough is being staked overall
897 if (totalPeersScore < minQuorumScore) {
898 return false;
899 }
900
901 // Ensure we have connected score for enough of the overall score
902 uint32_t minConnectedScore =
903 std::round(double(totalPeersScore) * minQuorumConnectedScoreRatio);
904 if (connectedPeersScore < minConnectedScore) {
905 return false;
906 }
907
908 quorumIsEstablished = true;
909
910 // Attempt to compute the staking rewards winner now so we don't have to
911 // wait for a block if we already have all the prerequisites.
912 const CBlockIndex *pprev = WITH_LOCK(cs_main, return chainman.ActiveTip());
913 bool computedRewards = false;
914 if (pprev && IsStakingRewardsActivated(chainman.GetConsensus(), pprev)) {
915 computedRewards = computeStakingReward(pprev);
916 }
917 if (pprev && isStakingPreconsensusActivated(pprev) && !computedRewards) {
918 // It's possible to have quorum shortly after startup if peers were
919 // loaded from disk, but staking rewards may not be ready yet. In this
920 // case, we can still promote and poll for contenders.
922 }
923
924 return true;
925}
926
928 // The flag is latched
930 return true;
931 }
932
933 // Don't share our proof if we don't have any inbound connection.
934 // This is a best effort measure to prevent advertising a proof if we have
935 // limited network connectivity.
937
939}
940
942 if (!pindex) {
943 return false;
944 }
945
946 // If the quorum is not established there is no point picking a winner that
947 // will be rejected.
948 if (!isQuorumEstablished()) {
949 return false;
950 }
951
952 {
954 if (stakingRewards.count(pindex->GetBlockHash()) > 0) {
955 return true;
956 }
957 }
958
959 StakingReward _stakingRewards;
960 _stakingRewards.blockheight = pindex->nHeight;
961
962 bool rewardsInserted = false;
963 if (WITH_LOCK(cs_peerManager, return peerManager->selectStakingRewardWinner(
964 pindex, _stakingRewards.winners))) {
965 {
967 rewardsInserted =
968 stakingRewards
969 .emplace(pindex->GetBlockHash(), std::move(_stakingRewards))
970 .second;
971 }
972
973 if (isStakingPreconsensusActivated(pindex)) {
975 }
976 }
977
978 return rewardsInserted;
979}
980
983 return stakingRewards.erase(prevBlockHash) > 0;
984}
985
986void Processor::cleanupStakingRewards(const int minHeight) {
987 // Avoid cs_main => cs_peerManager reverse order locking
991
992 {
994 // std::erase_if is only defined since C++20
995 for (auto it = stakingRewards.begin(); it != stakingRewards.end();) {
996 if (it->second.blockheight < minHeight) {
997 it = stakingRewards.erase(it);
998 } else {
999 ++it;
1000 }
1001 }
1002 }
1003
1005 return peerManager->cleanupStakeContenders(minHeight));
1006}
1007
1009 const BlockHash &prevBlockHash,
1010 std::vector<std::pair<ProofId, CScript>> &winners) const {
1012 auto it = stakingRewards.find(prevBlockHash);
1013 if (it == stakingRewards.end()) {
1014 return false;
1015 }
1016
1017 winners = it->second.winners;
1018 return true;
1019}
1020
1022 std::vector<CScript> &payouts) const {
1023 std::vector<std::pair<ProofId, CScript>> winners;
1024 if (!getStakingRewardWinners(prevBlockHash, winners)) {
1025 return false;
1026 }
1027
1028 payouts.clear();
1029 payouts.reserve(winners.size());
1030 for (auto &winner : winners) {
1031 payouts.push_back(std::move(winner.second));
1032 }
1033
1034 return true;
1035}
1036
1038 const std::vector<CScript> &payouts) {
1039 assert(pprev);
1040
1041 StakingReward stakingReward;
1042 stakingReward.blockheight = pprev->nHeight;
1043
1044 stakingReward.winners.reserve(payouts.size());
1045 for (const CScript &payout : payouts) {
1046 stakingReward.winners.push_back({ProofId(), payout});
1047 }
1048
1049 if (isStakingPreconsensusActivated(pprev)) {
1051 peerManager->setStakeContenderWinners(pprev, payouts);
1052 }
1053
1055 return stakingRewards.insert_or_assign(pprev->GetBlockHash(), stakingReward)
1056 .second;
1057}
1058
1060 const CBlockIndex *pprev,
1061 const std::vector<std::pair<ProofId, CScript>> &winners) {
1062 assert(pprev);
1063
1064 StakingReward stakingReward;
1065 stakingReward.blockheight = pprev->nHeight;
1066 stakingReward.winners = winners;
1067
1069 return stakingRewards.insert_or_assign(pprev->GetBlockHash(), stakingReward)
1070 .second;
1071}
1072
1073void Processor::FinalizeNode(const ::Config &config, const CNode &node) {
1075
1076 const NodeId nodeid = node.GetId();
1077 WITH_LOCK(cs_peerManager, peerManager->removeNode(nodeid));
1078 WITH_LOCK(cs_delayedAvahelloNodeIds, delayedAvahelloNodeIds.erase(nodeid));
1079}
1080
1082 const StakeContenderId &contenderId) const {
1085
1086 BlockHash prevblockhash;
1087 int status =
1088 WITH_LOCK(cs_peerManager, return peerManager->getStakeContenderStatus(
1089 contenderId, prevblockhash));
1090
1091 if (status != -1) {
1092 std::vector<std::pair<ProofId, CScript>> winners;
1093 getStakingRewardWinners(prevblockhash, winners);
1094 if (winners.size() == 0) {
1095 // If we have not selected a local staking rewards winner yet,
1096 // indicate this contender is pending to avoid convergence issues.
1097 return -2;
1098 }
1099 }
1100
1101 return status;
1102}
1103
1106 peerManager->acceptStakeContender(contenderId);
1107}
1108
1111
1112 BlockHash prevblockhash;
1113 std::vector<std::pair<ProofId, CScript>> winners;
1114 {
1116 peerManager->finalizeStakeContender(contenderId, prevblockhash,
1117 winners);
1118 }
1119
1120 // Set staking rewards to include newly finalized contender
1121 if (winners.size() > 0) {
1122 const CBlockIndex *block = WITH_LOCK(
1123 cs_main,
1124 return chainman.m_blockman.LookupBlockIndex(prevblockhash));
1125 if (block) {
1126 setStakingRewardWinners(block, winners);
1127 }
1128 }
1129}
1130
1135
1136 // Snapshot contender metadata, then mark it rejected in the cache.
1137 std::optional<StakeContenderCacheInfo> info;
1138 {
1140 info = peerManager->getStakeContenderInfo(contenderId);
1141 peerManager->rejectStakeContender(contenderId);
1142 }
1143 if (!info) {
1144 return;
1145 }
1146
1147 const BlockHash &prevblockhash = info->prevblockhash;
1148 const ProofId &rejectedProofid = info->proofid;
1149
1150 // Check if the rejected proof is a current staking winner, and whether it
1151 // is the primary (first) winner.
1152 bool rejectedIsPrimaryWinner = false;
1153 {
1155 auto it = stakingRewards.find(prevblockhash);
1156 if (it == stakingRewards.end()) {
1157 return;
1158 }
1159 if (std::none_of(it->second.winners.begin(), it->second.winners.end(),
1160 [&](const auto &winner) {
1161 return winner.first == rejectedProofid;
1162 })) {
1163 return;
1164 }
1165 rejectedIsPrimaryWinner =
1166 it->second.winners[0].first == rejectedProofid;
1167 }
1168
1169 // The rejected contender is not the primary winner: leave the list
1170 // unchanged.
1171 if (!rejectedIsPrimaryWinner) {
1172 return;
1173 }
1174
1175 const CBlockIndex *pprev = WITH_LOCK(
1176 cs_main, return chainman.m_blockman.LookupBlockIndex(prevblockhash));
1177 if (!pprev) {
1178 return;
1179 }
1180
1181 std::vector<std::pair<ProofId, CScript>> newWinners;
1182 {
1184 if (peerManager->hasAcceptedStakeWinner(prevblockhash)) {
1185 // Other contenders were already avalanche-finalized into the
1186 // winner set: refresh from the cache (accepted first).
1187 if (!peerManager->getStakeContenderWinners(prevblockhash,
1188 newWinners)) {
1189 return;
1190 }
1191 } else {
1192 // Widen payouts for this prevblock only. Avoid computeStakingReward
1193 // so we do not re-apply local-winner status on rejected contenders.
1194 peerManager->addRejectedContender(rejectedProofid, prevblockhash);
1195 if (!peerManager->selectStakingRewardWinner(pprev, newWinners)) {
1196 return;
1197 }
1198 }
1199 }
1200
1201 setStakingRewardWinners(pprev, newWinners);
1202}
1203
1205 assert(pprev);
1206
1207 if (!isQuorumEstablished()) {
1208 // Avoid growing the contender cache before it's possible to clean it up
1209 // (by finalizing blocks).
1210 return;
1211 }
1212
1213 {
1215 peerManager->promoteStakeContendersToBlock(pprev);
1216 }
1217
1218 // If staking rewards have not been computed yet, we will try again when
1219 // they have been.
1220 std::vector<StakeContenderId> pollableContenders;
1221 if (setContenderStatusForLocalWinners(pprev, pollableContenders)) {
1222 for (const StakeContenderId &contender : pollableContenders) {
1223 addToReconcile(contender);
1224 }
1225 }
1226}
1227
1229 const CBlockIndex *pindex,
1230 std::vector<StakeContenderId> &pollableContenders) {
1231 const BlockHash prevblockhash = pindex->GetBlockHash();
1232 std::vector<std::pair<ProofId, CScript>> winners;
1233 getStakingRewardWinners(prevblockhash, winners);
1234
1236 return peerManager->setContenderStatusForLocalWinners(
1237 pindex, winners, AVALANCHE_CONTENDER_MAX_POLLABLE, pollableContenders);
1238}
1239
1241 const bool registerLocalProof = canShareLocalProof();
1242 auto registerProofs = [&]() {
1244
1245 auto registeredProofs = peerManager->updatedBlockTip();
1246
1247 ProofRegistrationState localProofState;
1248 if (peerData && peerData->proof && registerLocalProof) {
1249 if (peerManager->registerProof(peerData->proof, localProofState)) {
1250 registeredProofs.insert(peerData->proof);
1251 }
1252
1253 if (localProofState.GetResult() ==
1255 // If our proof already exists, that's fine but we don't want to
1256 // erase the state with a duplicated proof status, so let's
1257 // retrieve the proper state. It also means we are able to
1258 // update the status should the proof move from one pool to the
1259 // other.
1260 const ProofId &localProofId = peerData->proof->getId();
1261 if (peerManager->isImmature(localProofId)) {
1263 "immature-proof");
1264 }
1265 if (peerManager->isInConflictingPool(localProofId)) {
1266 localProofState.Invalid(
1268 "conflicting-utxos");
1269 }
1270 if (peerManager->isBoundToPeer(localProofId)) {
1271 localProofState = ProofRegistrationState();
1272 }
1273 }
1274
1275 WITH_LOCK(peerData->cs_proofState,
1276 peerData->proofState = std::move(localProofState));
1277 }
1278
1279 return registeredProofs;
1280 };
1281
1282 auto registeredProofs = registerProofs();
1283 for (const auto &proof : registeredProofs) {
1284 reconcileOrFinalize(proof);
1285 }
1286
1287 const CBlockIndex *activeTip =
1289 if (activeTip && isStakingPreconsensusActivated(activeTip)) {
1291 }
1292}
1293
1296 WITH_LOCK(cs_main, return chainman.ActiveTip()))) {
1297 addToReconcile(tx);
1298 }
1299}
1300
1302 // Don't poll if quorum hasn't been established yet
1303 if (!isQuorumEstablished()) {
1304 return;
1305 }
1306
1307 // First things first, check if we have requests that timed out and clear
1308 // them.
1310
1312
1313 NodeId nodeid{NO_NODE};
1314 do {
1315 std::vector<CInv> invs;
1316
1317 {
1318 // Build a unique pointer to the read view of vote records.
1319 // This is so we can release the lock immediately after we have
1320 // gathered the invs to poll and avoid a lock inversion with the
1321 // peer manager lock.
1322 auto voteRecordsReadView =
1323 std::make_unique<decltype(voteRecords.getReadView())>(
1325
1327
1328 // Make sure there is at least one suitable node to query before
1329 // gathering invs.
1330 nodeid = peerManager->selectNode();
1331 if (nodeid == NO_NODE) {
1332 return;
1333 }
1334
1335 size_t max_elements = AVALANCHE_MAX_ELEMENT_POLL_LEGACY;
1336 peerManager->forNode(nodeid, [&max_elements](const Node &node) {
1337 max_elements = node.maxElements;
1338 return true;
1339 });
1340
1341 invs = getInvsForNextPoll(*voteRecordsReadView, max_elements);
1342 if (invs.empty()) {
1343 return;
1344 }
1345
1346 // Release the read lock on vote records
1347 voteRecordsReadView.reset();
1348
1355 bool hasSent = connman->ForNode(
1356 nodeid, [this, invs](CNode *pnode) EXCLUSIVE_LOCKS_REQUIRED(
1357 cs_peerManager) mutable {
1358 uint64_t current_round = round++;
1359
1360 {
1361 // Compute the time at which this requests times out.
1362 auto timeout = Now<SteadyMilliseconds>() +
1364 // Register the query.
1365 queries.getWriteView()->insert(
1366 {pnode->GetId(), current_round, timeout, invs});
1367 // Set the timeout.
1368 peerManager->updateNextRequestTimeForPoll(
1369 pnode->GetId(), timeout, current_round);
1370 }
1371
1372 pnode->invsPolled(invs.size());
1373
1374 // Send the query to the node.
1376 pnode,
1378 Poll(current_round, std::move(invs))));
1379 return true;
1380 });
1381
1382 // Success!
1383 if (hasSent) {
1384 return;
1385 }
1386
1387 // This node is obsolete, delete it.
1388 peerManager->removeNode(nodeid);
1389 }
1390
1391 // Roll back inflight counters for the polls that were never dispatched.
1392 // Done after releasing cs_peerManager to avoid lock inversion with
1393 // voteRecords.
1395 } while (nodeid != NO_NODE);
1396}
1397
1399 auto w = voteRecords.getWriteView();
1400 for (auto it = w->begin(); it != w->end();) {
1401 if (!isWorthPolling(it->first)) {
1402 it = w->erase(it);
1403 } else {
1404 ++it;
1405 }
1406 }
1407}
1408
1410 auto now = Now<SteadyMilliseconds>();
1411 std::map<CInv, uint8_t> timedout_items{};
1412
1413 {
1414 // Clear expired requests.
1415 auto w = queries.getWriteView();
1416 auto it = w->get<query_timeout>().begin();
1417 while (it != w->get<query_timeout>().end() && it->timeout < now) {
1418 for (const auto &i : it->invs) {
1419 timedout_items[i]++;
1420 }
1421
1422 w->get<query_timeout>().erase(it++);
1423 }
1424 }
1425
1426 clearInflightRequests(timedout_items);
1427}
1428
1429void Processor::clearInflightRequests(const std::vector<CInv> &invs) {
1430 std::map<CInv, uint8_t> counts;
1431 for (const CInv &inv : invs) {
1432 counts[inv]++;
1433 }
1434 clearInflightRequests(counts);
1435}
1436
1438 const std::map<CInv, uint8_t> &inflightRequests) {
1439 if (inflightRequests.empty()) {
1440 return;
1441 }
1442
1443 auto voteRecordsReadView = voteRecords.getReadView();
1444 for (const auto &inflightRequest : inflightRequests) {
1445 auto item = getVoteItemFromInv(inflightRequest.first);
1446
1447 if (isNull(item)) {
1448 continue;
1449 }
1450
1451 auto it = voteRecordsReadView->find(item);
1452 if (it == voteRecordsReadView.end()) {
1453 continue;
1454 }
1455
1456 it->second.clearInflightRequest(inflightRequest.second);
1457 }
1458}
1459
1461 RWCollection<VoteMap>::ReadView &voteRecordsReadView, size_t max_elements,
1462 bool forPoll) const {
1463 std::vector<CInv> invs;
1464
1465 auto buildInvFromVoteItem = variant::overloaded{
1466 [](const ProofRef &proof) {
1467 return CInv(MSG_AVA_PROOF, proof->getId());
1468 },
1469 [](const CBlockIndex *pindex) {
1470 return CInv(MSG_BLOCK, pindex->GetBlockHash());
1471 },
1472 [](const StakeContenderId &contenderId) {
1473 return CInv(MSG_AVA_STAKE_CONTENDER, contenderId);
1474 },
1475 [](const CTransactionRef &tx) { return CInv(MSG_TX, tx->GetHash()); },
1476 };
1477
1478 for (const auto &[item, voteRecord] : voteRecordsReadView) {
1479 if (invs.size() >= max_elements) {
1480 // Make sure we do not produce more invs than specified by the
1481 // protocol.
1482 return invs;
1483 }
1484
1485 const bool shouldPoll =
1486 forPoll ? voteRecord.registerPoll() : voteRecord.shouldPoll();
1487
1488 if (!shouldPoll) {
1489 continue;
1490 }
1491
1492 invs.emplace_back(std::visit(buildInvFromVoteItem, item));
1493 }
1494
1495 return invs;
1496}
1497
1499 if (inv.IsMsgBlk()) {
1501 BlockHash(inv.hash)));
1502 }
1503
1504 if (inv.IsMsgProof()) {
1506 return peerManager->getProof(ProofId(inv.hash)));
1507 }
1508
1509 if (inv.IsMsgStakeContender()) {
1510 return StakeContenderId(inv.hash);
1511 }
1512
1513 if (mempool && inv.IsMsgTx()) {
1514 LOCK(mempool->cs);
1515 if (CTransactionRef tx = mempool->get(TxId(inv.hash))) {
1516 return tx;
1517 }
1519 [&inv](const TxConflicting &conflicting) {
1520 return conflicting.GetTx(TxId(inv.hash));
1521 })) {
1522 return tx;
1523 }
1524 }
1525
1526 return {nullptr};
1527}
1528
1531
1532 LOCK(cs_main);
1533
1534 if (pindex->nStatus.isInvalid()) {
1535 // No point polling invalid blocks.
1536 return false;
1537 }
1538
1540 return processor.finalizationTip &&
1541 processor.finalizationTip->GetAncestor(
1542 pindex->nHeight) == pindex)) {
1543 // There is no point polling blocks that are ancestor of a block that
1544 // has been accepted by the network.
1545 return false;
1546 }
1547
1549 return processor.invalidatedBlocks.contains(
1550 pindex->GetBlockHash()))) {
1551 // Blocks invalidated by Avalanche should not be polled twice.
1552 return false;
1553 }
1554
1555 return true;
1556}
1557
1559 // Avoid lock order issues cs_main -> cs_peerManager
1561 AssertLockNotHeld(processor.cs_peerManager);
1562
1563 const ProofId &proofid = proof->getId();
1564
1565 LOCK(processor.cs_peerManager);
1566
1567 // No point polling immature or discarded proofs
1568 return processor.peerManager->isBoundToPeer(proofid) ||
1569 processor.peerManager->isInConflictingPool(proofid);
1570}
1571
1573 const StakeContenderId &contenderId) const {
1574 AssertLockNotHeld(processor.cs_peerManager);
1575 AssertLockNotHeld(processor.cs_stakingRewards);
1576
1577 // Only worth polling for contenders that we know about
1578 return processor.getStakeContenderStatus(contenderId) != -1;
1579}
1580
1582 if (!processor.mempool) {
1583 return false;
1584 }
1585
1586 AssertLockNotHeld(processor.mempool->cs);
1587 return WITH_LOCK(processor.mempool->cs,
1588 return processor.mempool->isWorthPolling(tx));
1589}
1590
1592 return !isRecentlyFinalized(GetVoteItemId(item)) &&
1593 std::visit(IsWorthPolling(*this), item);
1594}
1595
1597 const CBlockIndex *pindex) const {
1599
1600 return WITH_LOCK(cs_main,
1601 return processor.chainman.ActiveChain().Contains(pindex));
1602}
1603
1605 AssertLockNotHeld(processor.cs_peerManager);
1606
1607 return WITH_LOCK(
1608 processor.cs_peerManager,
1609 return processor.peerManager->isBoundToPeer(proof->getId()));
1610}
1611
1613 const StakeContenderId &contenderId) const {
1614 AssertLockNotHeld(processor.cs_peerManager);
1615 AssertLockNotHeld(processor.cs_stakingRewards);
1616
1617 return processor.getStakeContenderStatus(contenderId) == 0;
1618}
1619
1621 const CTransactionRef &tx) const {
1622 if (!processor.mempool) {
1623 return false;
1624 }
1625
1626 AssertLockNotHeld(processor.mempool->cs);
1627
1628 return WITH_LOCK(processor.mempool->cs,
1629 return processor.mempool->exists(tx->GetId()));
1630}
1631
1633 // Require at least one avalanche-finalized block so we rely on a finalized
1634 // utxo set.
1635 return m_preConsensus && hasFinalizedTip();
1636}
1637
1639 return m_stakingPreConsensus;
1640}
1641
1642} // namespace avalanche
bool MoneyRange(const Amount nValue)
Definition: amount.h:177
ArgsManager gArgs
Definition: args.cpp:39
uint32_t PeerId
Definition: node.h:14
static constexpr bool DEFAULT_PERSIST_AVAPEERS
Default for -persistavapeers.
Definition: avalanche.h:56
static constexpr double AVALANCHE_DEFAULT_MIN_QUORUM_CONNECTED_STAKE_RATIO
Default minimum percentage of stake-weighted peers we must have a node for to constitute a usable quo...
Definition: avalanche.h:46
static constexpr bool DEFAULT_AVALANCHE_STAKING_PRECONSENSUS
Default for -avalanchestakingpreconsensus.
Definition: avalanche.h:62
static constexpr double AVALANCHE_DEFAULT_MIN_AVAPROOFS_NODE_COUNT
Default minimum number of nodes that sent us an avaproofs message before we can consider our quorum s...
Definition: avalanche.h:53
static constexpr bool DEFAULT_AVALANCHE_PRECONSENSUS
Default for -avalanchepreconsensus.
Definition: avalanche.h:59
static constexpr Amount AVALANCHE_DEFAULT_MIN_QUORUM_STAKE
Default minimum cumulative stake of all known peers that constitutes a usable quorum.
Definition: avalanche.h:39
fs::path GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: args.h:239
bool IsArgSet(const std::string &strArg) const
Return true if the given argument has been manually set.
Definition: args.cpp:371
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Return integer argument or default value.
Definition: args.cpp:494
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: args.cpp:462
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition: args.cpp:524
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: blockindex.h:25
BlockHash GetBlockHash() const
Definition: blockindex.h:129
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: blockindex.h:38
Definition: net.h:838
bool ForNode(NodeId id, std::function< bool(CNode *pnode)> func)
Definition: net.cpp:3204
size_t GetNodeCount(ConnectionDirection) const
Definition: net.cpp:2877
void PushMessage(CNode *pnode, CSerializedNetMsg &&msg)
Definition: net.cpp:3158
Inv(ventory) message data.
Definition: protocol.h:589
bool IsMsgBlk() const
Definition: protocol.h:620
bool IsMsgTx() const
Definition: protocol.h:608
bool IsMsgStakeContender() const
Definition: protocol.h:616
uint256 hash
Definition: protocol.h:592
bool IsMsgProof() const
Definition: protocol.h:612
An encapsulated secp256k1 private key.
Definition: key.h:28
bool IsValid() const
Check whether this private key is valid.
Definition: key.h:97
void MakeNewKey(bool fCompressed)
Generate a new private key using a cryptographic PRNG.
Definition: key.cpp:182
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:209
bool SignSchnorr(const uint256 &hash, SchnorrSig &sig, uint32_t test_case=0) const
Create a Schnorr signature.
Definition: key.cpp:287
Information about a peer.
Definition: net.h:395
NodeId GetId() const
Definition: net.h:687
uint64_t GetLocalNonce() const
Definition: net.h:689
uint64_t nRemoteHostNonce
Definition: net.h:444
uint64_t nRemoteExtraEntropy
Definition: net.h:446
uint64_t GetLocalExtraEntropy() const
Definition: net.h:690
void invsPolled(uint32_t count)
The node was polled for count invs.
Definition: net.cpp:3056
An encapsulated public key.
Definition: pubkey.h:31
Simple class for background tasks that should be run periodically or once "after a while".
Definition: scheduler.h:42
void scheduleEvery(Predicate p, std::chrono::milliseconds delta) EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex)
Repeat p until it return false.
Definition: scheduler.cpp:115
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:222
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
Definition: txmempool.h:316
CTransactionRef get(const TxId &txid) const
Definition: txmempool.cpp:681
auto withConflicting(Callable &&func) const EXCLUSIVE_LOCKS_REQUIRED(!cs_conflicting)
Definition: txmempool.h:594
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:1176
bool IsInitialBlockDownload() const
Check whether we are doing an initial block download (synchronizing from disk or network)
CBlockIndex * ActiveTip() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Definition: validation.h:1453
const Consensus::Params & GetConsensus() const
Definition: validation.h:1295
node::BlockManager m_blockman
A single BlockManager instance is shared across each constructed chainstate to avoid duplicating bloc...
Definition: validation.h:1336
A writer stream (for serialization) that computes a 256-bit hash.
Definition: hash.h:99
static RCUPtr make(Args &&...args)
Construct a new object that is owned by the pointer.
Definition: rcu.h:112
ReadView getReadView() const
Definition: rwcollection.h:76
WriteView getWriteView()
Definition: rwcollection.h:82
iterator end()
Definition: rwcollection.h:42
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
ProofId getProofId() const
Definition: delegation.cpp:56
static bool FromHex(Delegation &dg, const std::string &dgHex, bilingual_str &errorOut)
Definition: delegation.cpp:16
bool verify(DelegationState &state, CPubKey &auth) const
Definition: delegation.cpp:73
const DelegationId & getId() const
Definition: delegation.h:59
const CPubKey & getDelegatedPubkey() const
Definition: delegation.cpp:60
const LimitedProofId & getLimitedProofId() const
Definition: delegation.h:60
void transactionAddedToMempool(const CTransactionRef &tx, uint64_t mempool_sequence) override
Definition: processor.cpp:141
void sendResponse(CNode *pfrom, Response response) const
Definition: processor.cpp:560
const uint32_t staleVoteThreshold
Voting parameters.
Definition: processor.h:239
std::atomic< bool > quorumIsEstablished
Definition: processor.h:233
AnyVoteItem getVoteItemFromInv(const CInv &inv) const EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager)
Definition: processor.cpp:1498
Mutex cs_finalizedItems
Rolling bloom filter to track recently finalized inventory items of any type.
Definition: processor.h:485
bool sendHelloInternal(CNode *pfrom) EXCLUSIVE_LOCKS_REQUIRED(cs_delayedAvahelloNodeIds)
Definition: processor.cpp:719
int getConfidence(const AnyVoteItem &item) const
Definition: processor.cpp:494
bool addToReconcile(const AnyVoteItem &item) EXCLUSIVE_LOCKS_REQUIRED(!cs_finalizedItems)
Definition: processor.cpp:443
Processor(Config avaconfig, interfaces::Chain &chain, CConnman *connmanIn, ChainstateManager &chainman, CTxMemPool *mempoolIn, CScheduler &scheduler, std::unique_ptr< PeerData > peerDataIn, CKey sessionKeyIn, uint32_t minQuorumTotalScoreIn, double minQuorumConnectedScoreRatioIn, int64_t minAvaproofsNodeCountIn, uint32_t staleVoteThresholdIn, uint32_t staleVoteFactorIn, Amount stakeUtxoDustThresholdIn, bool preConsensus, bool stakingPreConsensus, size_t maxElementPoll)
Definition: processor.cpp:147
bool isStakingPreconsensusActivated(const CBlockIndex *pprev) const
Definition: processor.cpp:1638
RWCollection< QuerySet > queries
Definition: processor.h:218
bool hasFinalizedTip() const EXCLUSIVE_LOCKS_REQUIRED(!cs_finalizationTip)
Whether there is a finalized tip.
Definition: processor.h:348
bool isPreconsensusActivated(const CBlockIndex *pprev) const EXCLUSIVE_LOCKS_REQUIRED(!cs_finalizationTip)
Definition: processor.cpp:1632
ProofRegistrationState getLocalProofRegistrationState() const EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager)
Definition: processor.cpp:780
bool setContenderStatusForLocalWinners(const CBlockIndex *pindex, std::vector< StakeContenderId > &pollableContenders) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
Helper to set the vote status for local winners in the contender cache.
Definition: processor.cpp:1228
bool sendHello(CNode *pfrom) EXCLUSIVE_LOCKS_REQUIRED(!cs_delayedAvahelloNodeIds)
Send a avahello message.
Definition: processor.cpp:754
bool isRecentlyFinalized(const uint256 &itemId) const EXCLUSIVE_LOCKS_REQUIRED(!cs_finalizedItems)
Definition: processor.cpp:518
void setRecentlyFinalized(const uint256 &itemId) EXCLUSIVE_LOCKS_REQUIRED(!cs_finalizedItems)
Definition: processor.cpp:522
bool startEventLoop(CScheduler &scheduler)
Definition: processor.cpp:811
bool isQuorumEstablished() LOCKS_EXCLUDED(cs_main) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
Definition: processor.cpp:841
std::atomic< uint64_t > round
Keep track of peers and queries sent.
Definition: processor.h:182
static std::unique_ptr< Processor > MakeProcessor(const ArgsManager &argsman, interfaces::Chain &chain, CConnman *connman, ChainstateManager &chainman, CTxMemPool *mempoolIn, CScheduler &scheduler, bilingual_str &error)
Definition: processor.cpp:226
EventLoop eventLoop
Event loop machinery.
Definition: processor.h:226
CTxMemPool * mempool
Definition: processor.h:172
int64_t minAvaproofsNodeCount
Definition: processor.h:235
const bool m_preConsensus
Definition: processor.h:277
bool isPolled(const AnyVoteItem &item) const
Definition: processor.cpp:508
Mutex cs_delayedAvahelloNodeIds
Definition: processor.h:249
bool setStakingRewardWinners(const CBlockIndex *pprev, const std::vector< CScript > &payouts) EXCLUSIVE_LOCKS_REQUIRED(!cs_stakingRewards
Definition: processor.cpp:1037
void runEventLoop() EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
Definition: processor.cpp:1301
Mutex cs_invalidatedBlocks
We don't need many blocks but a low false positive rate.
Definition: processor.h:472
void updatedBlockTip() EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
Definition: processor.cpp:1240
RWCollection< VoteMap > voteRecords
Items to run avalanche on.
Definition: processor.h:177
std::unique_ptr< interfaces::Handler > chainNotificationsHandler
Definition: processor.h:244
uint32_t minQuorumScore
Quorum management.
Definition: processor.h:231
void FinalizeNode(const ::Config &config, const CNode &node) override LOCKS_EXCLUDED(cs_main) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
Handle removal of a node.
Definition: processor.cpp:1073
bool getStakingRewardWinners(const BlockHash &prevBlockHash, std::vector< std::pair< ProofId, CScript > > &winners) const EXCLUSIVE_LOCKS_REQUIRED(!cs_stakingRewards)
Definition: processor.cpp:1008
std::atomic< bool > m_canShareLocalProof
Definition: processor.h:234
void cleanupStakingRewards(const int minHeight) EXCLUSIVE_LOCKS_REQUIRED(!cs_stakingRewards
Definition: processor.cpp:986
bool isAccepted(const AnyVoteItem &item) const
Definition: processor.cpp:480
ProofRef getLocalProof() const
Definition: processor.cpp:776
void acceptStakeContender(const StakeContenderId &contenderId) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager)
Definition: processor.cpp:1104
bool reconcileOrFinalize(const ProofRef &proof) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
Wrapper around the addToReconcile for proofs that adds back the finalization flag to the peer if it i...
Definition: processor.cpp:461
int getStakeContenderStatus(const StakeContenderId &contenderId) const EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
Track votes on stake contenders.
Definition: processor.cpp:1081
const uint32_t staleVoteFactor
Definition: processor.h:240
void promoteAndPollStakeContenders(const CBlockIndex *pprev) EXCLUSIVE_LOCKS_REQUIRED(!cs_stakingRewards
Promote stake contender cache entries to a given block and then poll.
Definition: processor.cpp:1204
void sendDelayedAvahello() EXCLUSIVE_LOCKS_REQUIRED(!cs_delayedAvahelloNodeIds)
Definition: processor.cpp:759
void finalizeStakeContender(const StakeContenderId &contenderId) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
Definition: processor.cpp:1109
std::unique_ptr< PeerData > peerData
Definition: processor.h:222
const size_t m_maxElementPoll
Definition: processor.h:281
bool eraseStakingRewardWinner(const BlockHash &prevBlockHash) EXCLUSIVE_LOCKS_REQUIRED(!cs_stakingRewards)
Definition: processor.cpp:981
CConnman * connman
Definition: processor.h:170
std::vector< CInv > getInvsForNextPoll(RWCollection< VoteMap >::ReadView &voteRecordsReadView, size_t max_elements, bool forPoll=true) const
Definition: processor.cpp:1460
bool isWorthPolling(const AnyVoteItem &item) const EXCLUSIVE_LOCKS_REQUIRED(!cs_finalizedItems)
Definition: processor.cpp:1591
CPubKey getSessionPubKey() const
Definition: processor.cpp:715
void clearInflightRequests(const std::vector< CInv > &invs) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager)
Decrement VoteRecord::inflight for each inventory.
Definition: processor.cpp:1429
ChainstateManager & chainman
Definition: processor.h:171
std::atomic< int64_t > avaproofsNodeCounter
Definition: processor.h:236
std::atomic_bool m_stakingPreConsensus
Definition: processor.h:279
bool computeStakingReward(const CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
Definition: processor.cpp:941
bool registerVotes(NodeId nodeid, const Response &response, std::vector< VoteItemUpdate > &updates, bool &disconnect, std::string &error) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
Definition: processor.cpp:566
void clearTimedoutRequests() EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager)
Definition: processor.cpp:1409
void transactionAddedToMempool(const CTransactionRef &tx) EXCLUSIVE_LOCKS_REQUIRED(!cs_finalizedItems
Definition: processor.cpp:1294
Mutex cs_peerManager
Keep track of the peers and associated infos.
Definition: processor.h:187
bool getLocalAcceptance(const AnyVoteItem &item) const
Definition: processor.h:518
void clearInvsNotWorthPolling() EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
Definition: processor.cpp:1398
void rejectStakeContender(const StakeContenderId &contenderId) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
Definition: processor.cpp:1131
void avaproofsSent(NodeId nodeid) LOCKS_EXCLUDED(cs_main) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager)
Definition: processor.cpp:820
double minQuorumConnectedScoreRatio
Definition: processor.h:232
void clearFinalizedItems() EXCLUSIVE_LOCKS_REQUIRED(!cs_finalizedItems)
Definition: processor.cpp:526
static bool FromHex(Proof &proof, const std::string &hexProof, bilingual_str &errorOut)
Definition: proof.cpp:54
bool verify(const Amount &stakeUtxoDustThreshold, ProofValidationState &state) const
Definition: proof.cpp:128
static uint32_t amountToScore(Amount amount)
Definition: proof.cpp:98
uint32_t GetError() const
Definition: protocol.h:28
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:30
Chain notifications.
Definition: chain.h:257
Interface giving clients (wallet processes, maybe other analysis tools in the future) ability to acce...
Definition: chain.h:136
virtual std::unique_ptr< Handler > handleNotifications(std::shared_ptr< Notifications > notifications)=0
Register handler for notifications.
CBlockIndex * LookupBlockIndex(const BlockHash &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
256-bit opaque blob.
Definition: uint256.h:199
static const uint256 ZERO
Definition: uint256.h:210
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
std::array< uint8_t, CPubKey::SCHNORR_SIZE > SchnorrSig
a Schnorr signature
Definition: key.h:25
CKey DecodeSecret(const std::string &str)
Definition: key_io.cpp:77
#define LogDebug(category,...)
Definition: logging.h:446
bool ParseMoney(const std::string &money_string, Amount &nRet)
Parse an amount denoted in full coins.
Definition: moneystr.cpp:41
@ AVALANCHE
Definition: logging.h:91
CSerializedNetMsg Make(std::string msg_type, Args &&...args)
const char * AVAHELLO
Contains a delegation and a signature.
Definition: protocol.cpp:51
const char * AVARESPONSE
Contains an avalanche::Response.
Definition: protocol.cpp:53
const char * AVAPOLL
Contains an avalanche::Poll.
Definition: protocol.cpp:52
static constexpr Amount PROOF_DUST_THRESHOLD
Minimum amount per utxo.
Definition: proof.h:38
std::variant< const ProofRef, const CBlockIndex *, const StakeContenderId, const CTransactionRef > AnyVoteItem
Definition: processor.h:104
static bool VerifyDelegation(const Delegation &dg, const CPubKey &expectedPubKey, bilingual_str &error)
Definition: processor.cpp:94
static bool isNull(const AnyVoteItem &item)
Definition: processor.cpp:432
static bool VerifyProof(const Amount &stakeUtxoDustThreshold, const Proof &proof, bilingual_str &error)
Definition: processor.cpp:62
static uint256 GetVoteItemId(const AnyVoteItem &item)
Definition: processor.cpp:41
RCUPtr< const Proof > ProofRef
Definition: proof.h:183
static std::string PathToString(const path &path)
Convert path object to byte string.
Definition: fs.h:147
Definition: messages.h:12
Implement std::hash so RCUPtr can be used as a key for maps or sets.
Definition: rcu.h:259
static constexpr NodeId NO_NODE
Special NodeId that represent no node.
Definition: nodeid.h:15
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:315
static const std::string AVAPEERS_FILE_NAME
Definition: processor.cpp:38
Response response
Definition: processor.cpp:537
static constexpr std::chrono::milliseconds AVALANCHE_TIME_STEP
Run the avalanche event loop every 10ms.
Definition: processor.cpp:36
SchnorrSig sig
Definition: processor.cpp:538
static constexpr size_t DEFAULT_AVALANCHE_MAX_ELEMENT_POLL
Maximum item that can be polled at once.
Definition: processor.h:55
static constexpr size_t AVALANCHE_CONTENDER_MAX_POLLABLE
Maximum number of stake contenders to poll for, leaving room for polling blocks and proofs in the sam...
Definition: processor.h:69
static constexpr std::chrono::milliseconds AVALANCHE_DEFAULT_QUERY_TIMEOUT
How long before we consider that a query timed out.
Definition: processor.h:74
static constexpr size_t AVALANCHE_MAX_ELEMENT_POLL_LEGACY
Legacy maximum element poll.
Definition: processor.h:63
static constexpr int AVALANCHE_MAX_PROOF_STAKES
How many UTXOs can be used for a single proof.
Definition: proof.h:27
@ MSG_TX
Definition: protocol.h:573
@ MSG_AVA_STAKE_CONTENDER
Definition: protocol.h:581
@ MSG_AVA_PROOF
Definition: protocol.h:580
@ MSG_BLOCK
Definition: protocol.h:574
#define SERIALIZE_METHODS(cls, obj)
Implement the Serialize and Unserialize methods by delegating to a single templated static method tha...
Definition: serialize.h:276
#define READWRITE(...)
Definition: serialize.h:176
bool IsStakingRewardsActivated(const Consensus::Params &params, const CBlockIndex *pprev)
Definition: amount.h:23
A BlockHash is a unqiue identifier for a block.
Definition: blockhash.h:13
bool stopEventLoop() EXCLUSIVE_LOCKS_REQUIRED(!cs_running)
Definition: eventloop.cpp:45
bool startEventLoop(CScheduler &scheduler, std::function< void()> runEventLoop, std::chrono::milliseconds delta) EXCLUSIVE_LOCKS_REQUIRED(!cs_running)
Definition: eventloop.cpp:13
A TxId is the identifier of a transaction.
Definition: txid.h:14
const std::chrono::milliseconds queryTimeoutDuration
Definition: config.h:13
bool operator()(const CBlockIndex *pindex) const LOCKS_EXCLUDED(cs_main)
Definition: processor.cpp:1596
bool operator()(const CBlockIndex *pindex) const LOCKS_EXCLUDED(cs_main)
Definition: processor.cpp:1529
ProofRegistrationState proofState GUARDED_BY(cs_proofState)
std::vector< std::pair< ProofId, CScript > > winners
Definition: processor.h:260
StakeContenderIds are unique for each block to ensure that the peer polling for their acceptance has ...
Vote history.
Definition: voterecord.h:50
Bilingual messages:
Definition: translation.h:17
#define AssertLockNotHeld(cs)
Definition: sync.h:163
#define LOCK(cs)
Definition: sync.h:295
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:343
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:56
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1203
bilingual_str _(const char *psz)
Translation function.
Definition: translation.h:68
bool ParseFixedPoint(std::string_view val, int decimals, int64_t *amount_out)
Parse number as fixed point according to JSON number syntax.
AssertLockHeld(pool.cs)
assert(!tx.IsCoinBase())
static constexpr uint32_t AVALANCHE_VOTE_STALE_FACTOR
Scaling factor applied to confidence to determine staleness threshold.
Definition: voterecord.h:36
static constexpr uint32_t AVALANCHE_VOTE_STALE_MIN_THRESHOLD
Lowest configurable staleness threshold (finalization score + necessary votes to increase confidence ...
Definition: voterecord.h:29
static constexpr uint32_t AVALANCHE_VOTE_STALE_THRESHOLD
Number of votes before a record may be considered as stale.
Definition: voterecord.h:23