Bitcoin ABC 0.34.3
P2P Digital Currency
Loading...
Searching...
No Matches
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
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(
459 : VoteRecord(accepted)))
460 .second;
461}
462
464 if (!proof) {
465 return false;
466 }
467
468 if (isRecentlyFinalized(proof->getId())) {
469 PeerId peerid;
471 if (peerManager->forPeer(proof->getId(), [&](const Peer &peer) {
472 peerid = peer.peerid;
473 return true;
474 })) {
475 return peerManager->setFinalized(peerid);
476 }
477 }
478
479 return addToReconcile(proof);
480}
481
482bool Processor::isAccepted(const AnyVoteItem &item) const {
483 if (isNull(item)) {
484 return false;
485 }
486
487 auto r = voteRecords.getReadView();
488 auto it = r->find(item);
489 if (it == r.end()) {
490 return false;
491 }
492
493 return it->second.isAccepted();
494}
495
496int Processor::getConfidence(const AnyVoteItem &item) const {
497 if (isNull(item)) {
498 return -1;
499 }
500
501 auto r = voteRecords.getReadView();
502 auto it = r->find(item);
503 if (it == r.end()) {
504 return -1;
505 }
506
507 return it->second.getConfidence();
508}
509
510bool Processor::isPolled(const AnyVoteItem &item) const {
511 if (isNull(item)) {
512 return false;
513 }
514
515 auto r = voteRecords.getReadView();
516 auto it = r->find(item);
517 return it != r.end();
518}
519
520bool Processor::isRecentlyFinalized(const uint256 &itemId) const {
521 return WITH_LOCK(cs_finalizedItems, return finalizedItems.contains(itemId));
522}
523
525 WITH_LOCK(cs_finalizedItems, finalizedItems.insert(itemId));
526}
527
530 finalizedItems.reset();
531}
532
533namespace {
538 class TCPResponse {
539 Response response;
541
542 public:
543 TCPResponse(Response responseIn, const CKey &key)
544 : response(std::move(responseIn)) {
545 HashWriter hasher{};
546 hasher << response;
547 const uint256 hash = hasher.GetHash();
548
549 // Now let's sign!
550 if (!key.SignSchnorr(hash, sig)) {
551 sig.fill(0);
552 }
553 }
554
555 // serialization support
556 SERIALIZE_METHODS(TCPResponse, obj) {
557 READWRITE(obj.response, obj.sig);
558 }
559 };
560} // namespace
561
565 TCPResponse(std::move(response), sessionKey)));
566}
567
569 std::vector<VoteItemUpdate> &updates,
570 bool &disconnect, std::string &error) {
571 disconnect = false;
572 updates.clear();
573
574 // Save the time at which we can query again.
576 peerManager->updateNextRequestTimeForResponse(nodeid, response));
577
578 std::vector<CInv> invs;
579
580 {
581 // Check that the query exists. There is a possibility that it has been
582 // deleted if the query timed out, so we don't disconnect for poor
583 // networking over time.
584 // Disconnecting has to be handled at callsite to avoid DoS.
585 auto w = queries.getWriteView();
586 auto it = w->find(std::make_tuple(nodeid, response.getRound()));
587 if (it == w.end()) {
588 error = "unexpected-ava-response";
589 return false;
590 }
591
592 invs = std::move(it->invs);
593 w->erase(it);
594 }
595
596 // Verify that the request and the vote are consistent.
597 const std::vector<Vote> &votes = response.GetVotes();
598 size_t size = invs.size();
599 if (votes.size() != size) {
600 disconnect = true;
601 error = "invalid-ava-response-size";
603 return false;
604 }
605
606 for (size_t i = 0; i < size; i++) {
607 if (invs[i].hash != votes[i].GetHash()) {
608 disconnect = true;
609 error = "invalid-ava-response-content";
611 return false;
612 }
613 }
614
615 std::map<AnyVoteItem, Vote, VoteMapComparator> responseItems;
616
617 // At this stage we are certain that invs[i] matches votes[i], so we can use
618 // the inv type to retrieve what is being voted on.
619 for (size_t i = 0; i < size; i++) {
620 auto item = getVoteItemFromInv(invs[i]);
621
622 if (isNull(item)) {
623 // This should not happen, but just in case...
624 continue;
625 }
626
627 if (!isWorthPolling(item)) {
628 // There is no point polling this item.
629 continue;
630 }
631
632 responseItems.insert(std::make_pair(std::move(item), votes[i]));
633 }
634
635 auto voteRecordsWriteView = voteRecords.getWriteView();
636
637 // Register votes.
638 for (const auto &p : responseItems) {
639 auto item = p.first;
640 const Vote &v = p.second;
641
642 auto it = voteRecordsWriteView->find(item);
643 if (it == voteRecordsWriteView.end()) {
644 // We are not voting on that item anymore.
645 continue;
646 }
647
648 auto &vr = it->second;
649 if (!vr.registerVote(nodeid, v.GetError())) {
650 if (vr.isStale(staleVoteThreshold, staleVoteFactor)) {
651 updates.emplace_back(std::move(item), VoteStatus::Stale);
652
653 // Just drop stale votes. If we see this item again, we'll
654 // do a new vote.
655 voteRecordsWriteView->erase(it);
656 }
657 // This vote did not provide any extra information, move on.
658 continue;
659 }
660
661 if (!vr.hasFinalized()) {
662 // This item has not been finalized, so we have nothing more to
663 // do.
664 updates.emplace_back(std::move(item), vr.isAccepted()
667 continue;
668 }
669
670 // We just finalized a vote. If it is valid, then let the caller
671 // know. Either way, remove the item from the map.
672 updates.emplace_back(std::move(item), vr.isAccepted()
675 voteRecordsWriteView->erase(it);
676 }
677
678 // FIXME This doesn't belong here as it has nothing to do with vote
679 // registration.
680 for (const auto &update : updates) {
681 if (update.getStatus() != VoteStatus::Finalized &&
682 update.getStatus() != VoteStatus::Invalid) {
683 continue;
684 }
685
686 const auto &item = update.getVoteItem();
687
688 if (!std::holds_alternative<const CBlockIndex *>(item)) {
689 continue;
690 }
691
692 if (update.getStatus() == VoteStatus::Invalid) {
693 // Track invalidated blocks. Other invalidated types are not
694 // tracked because they may be rejected for transient reasons
695 // (ex: immature proofs or orphaned txs) With blocks this is not
696 // the case. A rejected block will not be mined on. To prevent
697 // reorgs, invalidated blocks should never be polled again.
699 invalidatedBlocks.insert(GetVoteItemId(item));
700 continue;
701 }
702
703 // At this point the block index can only be finalized
704 const CBlockIndex *pindex = std::get<const CBlockIndex *>(item);
706 if (finalizationTip &&
707 finalizationTip->GetAncestor(pindex->nHeight) == pindex) {
708 continue;
709 }
710
711 finalizationTip = pindex;
712 }
713
714 return true;
715}
716
720
723
724 Delegation delegation;
725 if (peerData) {
726 if (!canShareLocalProof()) {
727 if (!delayedAvahelloNodeIds.emplace(pfrom->GetId()).second) {
728 // Nothing to do
729 return false;
730 }
731 } else {
732 delegation = peerData->delegation;
733 }
734 }
735
736 HashWriter hasher{};
737 hasher << delegation.getId();
738 hasher << pfrom->GetLocalNonce();
739 hasher << pfrom->nRemoteHostNonce;
740 hasher << pfrom->GetLocalExtraEntropy();
741 hasher << pfrom->nRemoteExtraEntropy;
742
743 // Now let's sign!
745 if (!sessionKey.SignSchnorr(hasher.GetHash(), sig)) {
746 return false;
747 }
748
751 Hello(delegation, sig, m_maxElementPoll)));
752
753 return delegation.getLimitedProofId() != uint256::ZERO;
754}
755
758 return sendHelloInternal(pfrom));
759}
760
763
764 auto it = delayedAvahelloNodeIds.begin();
765 while (it != delayedAvahelloNodeIds.end()) {
766 if (connman->ForNode(*it, [&](CNode *pnode) EXCLUSIVE_LOCKS_REQUIRED(
768 return sendHelloInternal(pnode);
769 })) {
770 // Our proof has been announced to this node
771 it = delayedAvahelloNodeIds.erase(it);
772 } else {
773 ++it;
774 }
775 }
776}
777
779 return peerData ? peerData->proof : ProofRef();
780}
781
784
786 if (!peerData) {
787 return state;
788 }
789
790 if (peerData->proof) {
792
793 const ProofId &proofid = peerData->proof->getId();
794
795 if (peerManager->isInConflictingPool(proofid)) {
797 "conflicting-utxos");
798 return state;
799 }
800
801 if (peerManager->isInvalid(proofid)) {
802 // If proof is invalid but verifies valid, it's been rejected by
803 // avalanche
805 "avalanche-invalidated");
806 return state;
807 }
808 }
809
810 return WITH_LOCK(peerData->cs_proofState, return peerData->proofState);
811}
812
815 scheduler, [this]() { this->runEventLoop(); }, AVALANCHE_TIME_STEP);
816}
817
821
824
826 // Before IBD is complete there is no way to make sure a proof is valid
827 // or not, e.g. it can be spent in a block we don't know yet. In order
828 // to increase confidence that our proof set is similar to other nodes
829 // on the network, the messages received during IBD are not accounted.
830 return;
831 }
832
834 if (peerManager->latchAvaproofsSent(nodeid)) {
836 }
837}
838
839/*
840 * Returns a bool indicating whether we have a usable Avalanche quorum enabling
841 * us to take decisions based on polls.
842 */
845
846 {
848 if (peerManager->getNodeCount() < 8) {
849 // There is no point polling if we know the vote cannot converge
850 return false;
851 }
852 }
853
854 /*
855 * The following parameters can naturally go temporarly below the threshold
856 * under normal circumstances, like during a proof replacement with a lower
857 * stake amount, or the discovery of a new proofs for which we don't have a
858 * node yet.
859 * In order to prevent our node from starting and stopping the polls
860 * spuriously on such event, the quorum establishement is latched. The only
861 * parameters that should not latched is the minimum node count, as this
862 * would cause the poll to be inconclusive anyway and should not happen
863 * under normal circumstances.
864 */
866 return true;
867 }
868
869 // Don't do Avalanche while node is IBD'ing
871 return false;
872 }
873
875 return false;
876 }
877
878 auto localProof = getLocalProof();
879
880 // Get the registered proof score and registered score we have nodes for
881 uint32_t totalPeersScore;
882 uint32_t connectedPeersScore;
883 {
885 totalPeersScore = peerManager->getTotalPeersScore();
886 connectedPeersScore = peerManager->getConnectedPeersScore();
887
888 // Consider that we are always connected to our proof, even if we are
889 // the single node using that proof.
890 if (localProof &&
891 peerManager->forPeer(localProof->getId(), [](const Peer &peer) {
892 return peer.node_count == 0;
893 })) {
894 connectedPeersScore += localProof->getScore();
895 }
896 }
897
898 // Ensure enough is being staked overall
899 if (totalPeersScore < minQuorumScore) {
900 return false;
901 }
902
903 // Ensure we have connected score for enough of the overall score
904 uint32_t minConnectedScore =
905 std::round(double(totalPeersScore) * minQuorumConnectedScoreRatio);
906 if (connectedPeersScore < minConnectedScore) {
907 return false;
908 }
909
910 quorumIsEstablished = true;
911
912 // Attempt to compute the staking rewards winner now so we don't have to
913 // wait for a block if we already have all the prerequisites.
914 const CBlockIndex *pprev = WITH_LOCK(cs_main, return chainman.ActiveTip());
915 bool computedRewards = false;
916 if (pprev && IsStakingRewardsActivated(chainman.GetConsensus(), pprev)) {
917 computedRewards = computeStakingReward(pprev);
918 }
919 if (pprev && isStakingPreconsensusActivated(pprev) && !computedRewards) {
920 // It's possible to have quorum shortly after startup if peers were
921 // loaded from disk, but staking rewards may not be ready yet. In this
922 // case, we can still promote and poll for contenders.
924 }
925
926 return true;
927}
928
930 // The flag is latched
932 return true;
933 }
934
935 // Don't share our proof if we don't have any inbound connection.
936 // This is a best effort measure to prevent advertising a proof if we have
937 // limited network connectivity.
939
941}
942
944 if (!pindex) {
945 return false;
946 }
947
948 // If the quorum is not established there is no point picking a winner that
949 // will be rejected.
950 if (!isQuorumEstablished()) {
951 return false;
952 }
953
954 {
956 if (stakingRewards.count(pindex->GetBlockHash()) > 0) {
957 return true;
958 }
959 }
960
961 StakingReward _stakingRewards;
962 _stakingRewards.blockheight = pindex->nHeight;
963
964 bool rewardsInserted = false;
965 if (WITH_LOCK(cs_peerManager, return peerManager->selectStakingRewardWinner(
966 pindex, _stakingRewards.winners))) {
967 {
969 rewardsInserted =
970 stakingRewards
971 .emplace(pindex->GetBlockHash(), std::move(_stakingRewards))
972 .second;
973 }
974
975 if (isStakingPreconsensusActivated(pindex)) {
977 }
978 }
979
980 return rewardsInserted;
981}
982
985 return stakingRewards.erase(prevBlockHash) > 0;
986}
987
988void Processor::cleanupStakingRewards(const int minHeight) {
989 // Avoid cs_main => cs_peerManager reverse order locking
993
994 {
996 // std::erase_if is only defined since C++20
997 for (auto it = stakingRewards.begin(); it != stakingRewards.end();) {
998 if (it->second.blockheight < minHeight) {
999 it = stakingRewards.erase(it);
1000 } else {
1001 ++it;
1002 }
1003 }
1004 }
1005
1007 return peerManager->cleanupStakeContenders(minHeight));
1008}
1009
1011 const BlockHash &prevBlockHash,
1012 std::vector<std::pair<ProofId, CScript>> &winners) const {
1014 auto it = stakingRewards.find(prevBlockHash);
1015 if (it == stakingRewards.end()) {
1016 return false;
1017 }
1018
1019 winners = it->second.winners;
1020 return true;
1021}
1022
1024 std::vector<CScript> &payouts) const {
1025 std::vector<std::pair<ProofId, CScript>> winners;
1026 if (!getStakingRewardWinners(prevBlockHash, winners)) {
1027 return false;
1028 }
1029
1030 payouts.clear();
1031 payouts.reserve(winners.size());
1032 for (auto &winner : winners) {
1033 payouts.push_back(std::move(winner.second));
1034 }
1035
1036 return true;
1037}
1038
1040 const std::vector<CScript> &payouts) {
1041 assert(pprev);
1042
1043 StakingReward stakingReward;
1044 stakingReward.blockheight = pprev->nHeight;
1045
1046 stakingReward.winners.reserve(payouts.size());
1047 for (const CScript &payout : payouts) {
1048 stakingReward.winners.push_back({ProofId(), payout});
1049 }
1050
1051 if (isStakingPreconsensusActivated(pprev)) {
1053 peerManager->setStakeContenderWinners(pprev, payouts);
1054 }
1055
1057 return stakingRewards.insert_or_assign(pprev->GetBlockHash(), stakingReward)
1058 .second;
1059}
1060
1062 const CBlockIndex *pprev,
1063 const std::vector<std::pair<ProofId, CScript>> &winners) {
1064 assert(pprev);
1065
1066 StakingReward stakingReward;
1067 stakingReward.blockheight = pprev->nHeight;
1068 stakingReward.winners = winners;
1069
1071 return stakingRewards.insert_or_assign(pprev->GetBlockHash(), stakingReward)
1072 .second;
1073}
1074
1075void Processor::FinalizeNode(const ::Config &config, const CNode &node) {
1077
1078 const NodeId nodeid = node.GetId();
1079 WITH_LOCK(cs_peerManager, peerManager->removeNode(nodeid));
1080 WITH_LOCK(cs_delayedAvahelloNodeIds, delayedAvahelloNodeIds.erase(nodeid));
1081}
1082
1084 const StakeContenderId &contenderId) const {
1087
1088 BlockHash prevblockhash;
1089 int status =
1090 WITH_LOCK(cs_peerManager, return peerManager->getStakeContenderStatus(
1091 contenderId, prevblockhash));
1092
1093 if (status != -1) {
1094 std::vector<std::pair<ProofId, CScript>> winners;
1095 getStakingRewardWinners(prevblockhash, winners);
1096 if (winners.size() == 0) {
1097 // If we have not selected a local staking rewards winner yet,
1098 // indicate this contender is pending to avoid convergence issues.
1099 return -2;
1100 }
1101 }
1102
1103 return status;
1104}
1105
1108 peerManager->acceptStakeContender(contenderId);
1109}
1110
1113
1114 BlockHash prevblockhash;
1115 std::vector<std::pair<ProofId, CScript>> winners;
1116 {
1118 peerManager->finalizeStakeContender(contenderId, prevblockhash,
1119 winners);
1120 }
1121
1122 // Set staking rewards to include newly finalized contender
1123 if (winners.size() > 0) {
1124 const CBlockIndex *block = WITH_LOCK(
1125 cs_main,
1126 return chainman.m_blockman.LookupBlockIndex(prevblockhash));
1127 if (block) {
1128 setStakingRewardWinners(block, winners);
1129 }
1130 }
1131}
1132
1137
1138 // Snapshot contender metadata, then mark it rejected in the cache.
1139 std::optional<StakeContenderCacheInfo> info;
1140 {
1142 info = peerManager->getStakeContenderInfo(contenderId);
1143 peerManager->rejectStakeContender(contenderId);
1144 }
1145 if (!info) {
1146 return;
1147 }
1148
1149 const BlockHash &prevblockhash = info->prevblockhash;
1150 const ProofId &rejectedProofid = info->proofid;
1151
1152 // Check if the rejected proof is a current staking winner, and whether it
1153 // is the primary (first) winner.
1154 bool rejectedIsPrimaryWinner = false;
1155 {
1157 auto it = stakingRewards.find(prevblockhash);
1158 if (it == stakingRewards.end()) {
1159 return;
1160 }
1161 if (std::none_of(it->second.winners.begin(), it->second.winners.end(),
1162 [&](const auto &winner) {
1163 return winner.first == rejectedProofid;
1164 })) {
1165 return;
1166 }
1167 rejectedIsPrimaryWinner =
1168 it->second.winners[0].first == rejectedProofid;
1169 }
1170
1171 // The rejected contender is not the primary winner: leave the list
1172 // unchanged.
1173 if (!rejectedIsPrimaryWinner) {
1174 return;
1175 }
1176
1177 const CBlockIndex *pprev = WITH_LOCK(
1178 cs_main, return chainman.m_blockman.LookupBlockIndex(prevblockhash));
1179 if (!pprev) {
1180 return;
1181 }
1182
1183 std::vector<std::pair<ProofId, CScript>> newWinners;
1184 {
1186 if (peerManager->hasAcceptedStakeWinner(prevblockhash)) {
1187 // Other contenders were already avalanche-finalized into the
1188 // winner set: refresh from the cache (accepted first).
1189 if (!peerManager->getStakeContenderWinners(prevblockhash,
1190 newWinners)) {
1191 return;
1192 }
1193 } else {
1194 // Widen payouts for this prevblock only. Avoid computeStakingReward
1195 // so we do not re-apply local-winner status on rejected contenders.
1196 peerManager->addRejectedContender(rejectedProofid, prevblockhash);
1197 if (!peerManager->selectStakingRewardWinner(pprev, newWinners)) {
1198 return;
1199 }
1200 }
1201 }
1202
1203 setStakingRewardWinners(pprev, newWinners);
1204}
1205
1207 assert(pprev);
1208
1209 if (!isQuorumEstablished()) {
1210 // Avoid growing the contender cache before it's possible to clean it up
1211 // (by finalizing blocks).
1212 return;
1213 }
1214
1215 {
1217 peerManager->promoteStakeContendersToBlock(pprev);
1218 }
1219
1220 // If staking rewards have not been computed yet, we will try again when
1221 // they have been.
1222 std::vector<StakeContenderId> pollableContenders;
1223 if (setContenderStatusForLocalWinners(pprev, pollableContenders)) {
1224 for (const StakeContenderId &contender : pollableContenders) {
1225 addToReconcile(contender);
1226 }
1227 }
1228}
1229
1231 const CBlockIndex *pindex,
1232 std::vector<StakeContenderId> &pollableContenders) {
1233 const BlockHash prevblockhash = pindex->GetBlockHash();
1234 std::vector<std::pair<ProofId, CScript>> winners;
1235 getStakingRewardWinners(prevblockhash, winners);
1236
1238 return peerManager->setContenderStatusForLocalWinners(
1239 pindex, winners, AVALANCHE_CONTENDER_MAX_POLLABLE, pollableContenders);
1240}
1241
1243 const bool registerLocalProof = canShareLocalProof();
1244 auto registerProofs = [&]() {
1246
1247 auto registeredProofs = peerManager->updatedBlockTip();
1248
1249 ProofRegistrationState localProofState;
1250 if (peerData && peerData->proof && registerLocalProof) {
1251 if (peerManager->registerProof(peerData->proof, localProofState)) {
1252 registeredProofs.insert(peerData->proof);
1253 }
1254
1255 if (localProofState.GetResult() ==
1257 // If our proof already exists, that's fine but we don't want to
1258 // erase the state with a duplicated proof status, so let's
1259 // retrieve the proper state. It also means we are able to
1260 // update the status should the proof move from one pool to the
1261 // other.
1262 const ProofId &localProofId = peerData->proof->getId();
1263 if (peerManager->isImmature(localProofId)) {
1265 "immature-proof");
1266 }
1267 if (peerManager->isInConflictingPool(localProofId)) {
1268 localProofState.Invalid(
1270 "conflicting-utxos");
1271 }
1272 if (peerManager->isBoundToPeer(localProofId)) {
1273 localProofState = ProofRegistrationState();
1274 }
1275 }
1276
1277 WITH_LOCK(peerData->cs_proofState,
1278 peerData->proofState = std::move(localProofState));
1279 }
1280
1281 return registeredProofs;
1282 };
1283
1284 auto registeredProofs = registerProofs();
1285 for (const auto &proof : registeredProofs) {
1286 reconcileOrFinalize(proof);
1287 }
1288
1289 const CBlockIndex *activeTip =
1291 if (activeTip && isStakingPreconsensusActivated(activeTip)) {
1293 }
1294}
1295
1302
1304 // Don't poll if quorum hasn't been established yet
1305 if (!isQuorumEstablished()) {
1306 return;
1307 }
1308
1309 // First things first, check if we have requests that timed out and clear
1310 // them.
1312
1314
1315 NodeId nodeid{NO_NODE};
1316 do {
1317 std::vector<CInv> invs;
1318
1319 {
1320 // Build a unique pointer to the read view of vote records.
1321 // This is so we can release the lock immediately after we have
1322 // gathered the invs to poll and avoid a lock inversion with the
1323 // peer manager lock.
1324 auto voteRecordsReadView =
1325 std::make_unique<decltype(voteRecords.getReadView())>(
1327
1329
1330 // Make sure there is at least one suitable node to query before
1331 // gathering invs.
1332 nodeid = peerManager->selectNode();
1333 if (nodeid == NO_NODE) {
1334 return;
1335 }
1336
1337 size_t max_elements = AVALANCHE_MAX_ELEMENT_POLL_LEGACY;
1338 peerManager->forNode(nodeid, [&max_elements](const Node &node) {
1339 max_elements = node.maxElements;
1340 return true;
1341 });
1342
1343 invs = getInvsForNextPoll(*voteRecordsReadView, max_elements);
1344 if (invs.empty()) {
1345 return;
1346 }
1347
1348 // Release the read lock on vote records
1349 voteRecordsReadView.reset();
1350
1357 bool hasSent = connman->ForNode(
1358 nodeid, [this, invs](CNode *pnode) EXCLUSIVE_LOCKS_REQUIRED(
1359 cs_peerManager) mutable {
1360 uint64_t current_round = round++;
1361
1362 {
1363 // Compute the time at which this requests times out.
1364 auto timeout = Now<SteadyMilliseconds>() +
1366 // Register the query.
1367 queries.getWriteView()->insert(
1368 {pnode->GetId(), current_round, timeout, invs});
1369 // Set the timeout.
1370 peerManager->updateNextRequestTimeForPoll(
1371 pnode->GetId(), timeout, current_round);
1372 }
1373
1374 pnode->invsPolled(invs.size());
1375
1376 // Send the query to the node.
1378 pnode,
1380 Poll(current_round, std::move(invs))));
1381 return true;
1382 });
1383
1384 // Success!
1385 if (hasSent) {
1386 return;
1387 }
1388
1389 // This node is obsolete, delete it.
1390 peerManager->removeNode(nodeid);
1391 }
1392
1393 // Roll back inflight counters for the polls that were never dispatched.
1394 // Done after releasing cs_peerManager to avoid lock inversion with
1395 // voteRecords.
1397 } while (nodeid != NO_NODE);
1398}
1399
1401 auto w = voteRecords.getWriteView();
1402 for (auto it = w->begin(); it != w->end();) {
1403 if (!isWorthPolling(it->first)) {
1404 it = w->erase(it);
1405 } else {
1406 ++it;
1407 }
1408 }
1409}
1410
1412 auto now = Now<SteadyMilliseconds>();
1413 std::map<CInv, uint8_t> timedout_items{};
1414
1415 {
1416 // Clear expired requests.
1417 auto w = queries.getWriteView();
1418 auto it = w->get<query_timeout>().begin();
1419 while (it != w->get<query_timeout>().end() && it->timeout < now) {
1420 for (const auto &i : it->invs) {
1421 timedout_items[i]++;
1422 }
1423
1424 w->get<query_timeout>().erase(it++);
1425 }
1426 }
1427
1428 clearInflightRequests(timedout_items);
1429}
1430
1431void Processor::clearInflightRequests(const std::vector<CInv> &invs) {
1432 std::map<CInv, uint8_t> counts;
1433 for (const CInv &inv : invs) {
1434 counts[inv]++;
1435 }
1436 clearInflightRequests(counts);
1437}
1438
1440 const std::map<CInv, uint8_t> &inflightRequests) {
1441 if (inflightRequests.empty()) {
1442 return;
1443 }
1444
1445 auto voteRecordsReadView = voteRecords.getReadView();
1446 for (const auto &inflightRequest : inflightRequests) {
1447 auto item = getVoteItemFromInv(inflightRequest.first);
1448
1449 if (isNull(item)) {
1450 continue;
1451 }
1452
1453 auto it = voteRecordsReadView->find(item);
1454 if (it == voteRecordsReadView.end()) {
1455 continue;
1456 }
1457
1458 it->second.clearInflightRequest(inflightRequest.second);
1459 }
1460}
1461
1463 RWCollection<VoteMap>::ReadView &voteRecordsReadView, size_t max_elements,
1464 bool forPoll) const {
1465 std::vector<CInv> invs;
1466
1467 auto buildInvFromVoteItem = variant::overloaded{
1468 [](const ProofRef &proof) {
1469 return CInv(MSG_AVA_PROOF, proof->getId());
1470 },
1471 [](const CBlockIndex *pindex) {
1472 return CInv(MSG_BLOCK, pindex->GetBlockHash());
1473 },
1474 [](const StakeContenderId &contenderId) {
1475 return CInv(MSG_AVA_STAKE_CONTENDER, contenderId);
1476 },
1477 [](const CTransactionRef &tx) { return CInv(MSG_TX, tx->GetHash()); },
1478 };
1479
1480 for (const auto &[item, voteRecord] : voteRecordsReadView) {
1481 if (invs.size() >= max_elements) {
1482 // Make sure we do not produce more invs than specified by the
1483 // protocol.
1484 return invs;
1485 }
1486
1487 const bool shouldPoll =
1488 forPoll ? voteRecord.registerPoll() : voteRecord.shouldPoll();
1489
1490 if (!shouldPoll) {
1491 continue;
1492 }
1493
1494 invs.emplace_back(std::visit(buildInvFromVoteItem, item));
1495 }
1496
1497 return invs;
1498}
1499
1501 if (inv.IsMsgBlk()) {
1503 BlockHash(inv.hash)));
1504 }
1505
1506 if (inv.IsMsgProof()) {
1508 return peerManager->getProof(ProofId(inv.hash)));
1509 }
1510
1511 if (inv.IsMsgStakeContender()) {
1512 return StakeContenderId(inv.hash);
1513 }
1514
1515 if (mempool && inv.IsMsgTx()) {
1516 LOCK(mempool->cs);
1517 if (CTransactionRef tx = mempool->get(TxId(inv.hash))) {
1518 return tx;
1519 }
1521 [&inv](const TxConflicting &conflicting) {
1522 return conflicting.GetTx(TxId(inv.hash));
1523 })) {
1524 return tx;
1525 }
1526 }
1527
1528 return {nullptr};
1529}
1530
1533
1534 LOCK(cs_main);
1535
1536 if (pindex->nStatus.isInvalid()) {
1537 // No point polling invalid blocks.
1538 return false;
1539 }
1540
1542 return processor.finalizationTip &&
1543 processor.finalizationTip->GetAncestor(
1544 pindex->nHeight) == pindex)) {
1545 // There is no point polling blocks that are ancestor of a block that
1546 // has been accepted by the network.
1547 return false;
1548 }
1549
1551 return processor.invalidatedBlocks.contains(
1552 pindex->GetBlockHash()))) {
1553 // Blocks invalidated by Avalanche should not be polled twice.
1554 return false;
1555 }
1556
1557 return true;
1558}
1559
1561 // Avoid lock order issues cs_main -> cs_peerManager
1563 AssertLockNotHeld(processor.cs_peerManager);
1564
1565 const ProofId &proofid = proof->getId();
1566
1567 LOCK(processor.cs_peerManager);
1568
1569 // No point polling immature or discarded proofs
1570 return processor.peerManager->isBoundToPeer(proofid) ||
1571 processor.peerManager->isInConflictingPool(proofid);
1572}
1573
1575 const StakeContenderId &contenderId) const {
1576 AssertLockNotHeld(processor.cs_peerManager);
1577 AssertLockNotHeld(processor.cs_stakingRewards);
1578
1579 // Only worth polling for contenders that we know about
1580 return processor.getStakeContenderStatus(contenderId) != -1;
1581}
1582
1584 if (!processor.mempool) {
1585 return false;
1586 }
1587
1588 AssertLockNotHeld(processor.mempool->cs);
1589 return WITH_LOCK(processor.mempool->cs,
1590 return processor.mempool->isWorthPolling(tx));
1591}
1592
1594 return !isRecentlyFinalized(GetVoteItemId(item)) &&
1595 std::visit(IsWorthPolling(*this), item);
1596}
1597
1599 const CBlockIndex *pindex) const {
1601
1602 return WITH_LOCK(cs_main,
1603 return processor.chainman.ActiveChain().Contains(pindex));
1604}
1605
1607 AssertLockNotHeld(processor.cs_peerManager);
1608
1609 return WITH_LOCK(
1610 processor.cs_peerManager,
1611 return processor.peerManager->isBoundToPeer(proof->getId()));
1612}
1613
1615 const StakeContenderId &contenderId) const {
1616 AssertLockNotHeld(processor.cs_peerManager);
1617 AssertLockNotHeld(processor.cs_stakingRewards);
1618
1619 return processor.getStakeContenderStatus(contenderId) == 0;
1620}
1621
1623 const CTransactionRef &tx) const {
1624 if (!processor.mempool) {
1625 return false;
1626 }
1627
1628 AssertLockNotHeld(processor.mempool->cs);
1629
1630 return WITH_LOCK(processor.mempool->cs,
1631 return processor.mempool->exists(tx->GetId()));
1632}
1633
1635 // Require at least one avalanche-finalized block so we rely on a finalized
1636 // utxo set.
1637 return m_preConsensus && hasFinalizedTip();
1638}
1639
1643
1644} // 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
bool ForNode(NodeId id, std::function< bool(CNode *pnode)> func)
Definition net.cpp:3209
size_t GetNodeCount(ConnectionDirection) const
Definition net.cpp:2882
void PushMessage(CNode *pnode, CSerializedNetMsg &&msg)
Definition net.cpp:3163
Inv(ventory) message data.
Definition protocol.h:588
bool IsMsgBlk() const
Definition protocol.h:619
bool IsMsgTx() const
Definition protocol.h:607
bool IsMsgStakeContender() const
Definition protocol.h:615
uint256 hash
Definition protocol.h:591
bool IsMsgProof() const
Definition protocol.h:611
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:3061
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.
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
auto withConflicting(Callable &&func) const EXCLUSIVE_LOCKS_REQUIRED(!cs_conflicting)
Definition txmempool.h:601
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
bool IsInitialBlockDownload() const
Check whether we are doing an initial block download (synchronizing from disk or network)
CBlockIndex * ActiveTip() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
const Consensus::Params & GetConsensus() const
node::BlockManager m_blockman
A single BlockManager instance is shared across each constructed chainstate to avoid duplicating bloc...
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
WriteView getWriteView()
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
static bool FromHex(Delegation &dg, const std::string &dgHex, bilingual_str &errorOut)
bool verify(DelegationState &state, CPubKey &auth) const
const DelegationId & getId() const
Definition delegation.h:59
const CPubKey & getDelegatedPubkey() const
const LimitedProofId & getLimitedProofId() const
Definition delegation.h:60
void transactionAddedToMempool(const CTransactionRef &tx, uint64_t mempool_sequence) override
void sendResponse(CNode *pfrom, Response response) const
const uint32_t staleVoteThreshold
Voting parameters.
Definition processor.h:240
std::atomic< bool > quorumIsEstablished
Definition processor.h:234
AnyVoteItem getVoteItemFromInv(const CInv &inv) const EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager)
Mutex cs_finalizedItems
Rolling bloom filter to track recently finalized inventory items of any type.
Definition processor.h:488
bool sendHelloInternal(CNode *pfrom) EXCLUSIVE_LOCKS_REQUIRED(cs_delayedAvahelloNodeIds)
int getConfidence(const AnyVoteItem &item) const
bool addToReconcile(const AnyVoteItem &item) EXCLUSIVE_LOCKS_REQUIRED(!cs_finalizedItems)
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)
bool isStakingPreconsensusActivated(const CBlockIndex *pprev) const
RWCollection< QuerySet > queries
Definition processor.h:219
bool hasFinalizedTip() const EXCLUSIVE_LOCKS_REQUIRED(!cs_finalizationTip)
Whether there is a finalized tip.
Definition processor.h:351
bool isPreconsensusActivated(const CBlockIndex *pprev) const EXCLUSIVE_LOCKS_REQUIRED(!cs_finalizationTip)
ProofRegistrationState getLocalProofRegistrationState() const EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager)
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.
bool sendHello(CNode *pfrom) EXCLUSIVE_LOCKS_REQUIRED(!cs_delayedAvahelloNodeIds)
Send a avahello message.
bool isRecentlyFinalized(const uint256 &itemId) const EXCLUSIVE_LOCKS_REQUIRED(!cs_finalizedItems)
void setRecentlyFinalized(const uint256 &itemId) EXCLUSIVE_LOCKS_REQUIRED(!cs_finalizedItems)
bool startEventLoop(CScheduler &scheduler)
bool isQuorumEstablished() LOCKS_EXCLUDED(cs_main) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
std::atomic< uint64_t > round
Keep track of peers and queries sent.
Definition processor.h:183
static std::unique_ptr< Processor > MakeProcessor(const ArgsManager &argsman, interfaces::Chain &chain, CConnman *connman, ChainstateManager &chainman, CTxMemPool *mempoolIn, CScheduler &scheduler, bilingual_str &error)
EventLoop eventLoop
Event loop machinery.
Definition processor.h:227
CTxMemPool * mempool
Definition processor.h:173
int64_t minAvaproofsNodeCount
Definition processor.h:236
const bool m_preConsensus
Definition processor.h:278
bool isPolled(const AnyVoteItem &item) const
Mutex cs_delayedAvahelloNodeIds
Definition processor.h:250
bool setStakingRewardWinners(const CBlockIndex *pprev, const std::vector< CScript > &payouts) EXCLUSIVE_LOCKS_REQUIRED(!cs_stakingRewards
void runEventLoop() EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
Mutex cs_invalidatedBlocks
We don't need many blocks but a low false positive rate.
Definition processor.h:475
void updatedBlockTip() EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
RWCollection< VoteMap > voteRecords
Items to run avalanche on.
Definition processor.h:178
std::unique_ptr< interfaces::Handler > chainNotificationsHandler
Definition processor.h:245
uint32_t minQuorumScore
Quorum management.
Definition processor.h:232
void FinalizeNode(const ::Config &config, const CNode &node) override LOCKS_EXCLUDED(cs_main) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
Handle removal of a node.
bool getStakingRewardWinners(const BlockHash &prevBlockHash, std::vector< std::pair< ProofId, CScript > > &winners) const EXCLUSIVE_LOCKS_REQUIRED(!cs_stakingRewards)
std::atomic< bool > m_canShareLocalProof
Definition processor.h:235
void cleanupStakingRewards(const int minHeight) EXCLUSIVE_LOCKS_REQUIRED(!cs_stakingRewards
bool isAccepted(const AnyVoteItem &item) const
ProofRef getLocalProof() const
void acceptStakeContender(const StakeContenderId &contenderId) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager)
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...
int getStakeContenderStatus(const StakeContenderId &contenderId) const EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
Track votes on stake contenders.
const uint32_t staleVoteFactor
Definition processor.h:241
void promoteAndPollStakeContenders(const CBlockIndex *pprev) EXCLUSIVE_LOCKS_REQUIRED(!cs_stakingRewards
Promote stake contender cache entries to a given block and then poll.
void sendDelayedAvahello() EXCLUSIVE_LOCKS_REQUIRED(!cs_delayedAvahelloNodeIds)
void finalizeStakeContender(const StakeContenderId &contenderId) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
std::unique_ptr< PeerData > peerData
Definition processor.h:223
const size_t m_maxElementPoll
Definition processor.h:284
bool eraseStakingRewardWinner(const BlockHash &prevBlockHash) EXCLUSIVE_LOCKS_REQUIRED(!cs_stakingRewards)
std::vector< CInv > getInvsForNextPoll(RWCollection< VoteMap >::ReadView &voteRecordsReadView, size_t max_elements, bool forPoll=true) const
bool isWorthPolling(const AnyVoteItem &item) const EXCLUSIVE_LOCKS_REQUIRED(!cs_finalizedItems)
CPubKey getSessionPubKey() const
void clearInflightRequests(const std::vector< CInv > &invs) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager)
Decrement VoteRecord::inflight for each inventory.
ChainstateManager & chainman
Definition processor.h:172
std::atomic< int64_t > avaproofsNodeCounter
Definition processor.h:237
std::optional< uint32_t > m_voteRecordSeed
Definition processor.h:282
std::atomic_bool m_stakingPreConsensus
Definition processor.h:280
bool computeStakingReward(const CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
bool registerVotes(NodeId nodeid, const Response &response, std::vector< VoteItemUpdate > &updates, bool &disconnect, std::string &error) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
void clearTimedoutRequests() EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager)
void transactionAddedToMempool(const CTransactionRef &tx) EXCLUSIVE_LOCKS_REQUIRED(!cs_finalizedItems
Mutex cs_peerManager
Keep track of the peers and associated infos.
Definition processor.h:188
bool getLocalAcceptance(const AnyVoteItem &item) const
Definition processor.h:521
void clearInvsNotWorthPolling() EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
void rejectStakeContender(const StakeContenderId &contenderId) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
void avaproofsSent(NodeId nodeid) LOCKS_EXCLUDED(cs_main) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager)
double minQuorumConnectedScoreRatio
Definition processor.h:233
void clearFinalizedItems() EXCLUSIVE_LOCKS_REQUIRED(!cs_finalizedItems)
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:105
static bool VerifyDelegation(const Delegation &dg, const CPubKey &expectedPubKey, bilingual_str &error)
Definition processor.cpp:94
static bool isNull(const AnyVoteItem &item)
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
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
static const std::string AVAPEERS_FILE_NAME
Definition processor.cpp:38
Response response
static constexpr std::chrono::milliseconds AVALANCHE_TIME_STEP
Run the avalanche event loop every 10ms.
Definition processor.cpp:36
SchnorrSig sig
static constexpr size_t DEFAULT_AVALANCHE_MAX_ELEMENT_POLL
Maximum item that can be polled at once.
Definition processor.h:56
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:70
static constexpr std::chrono::milliseconds AVALANCHE_DEFAULT_QUERY_TIMEOUT
How long before we consider that a query timed out.
Definition processor.h:75
static constexpr size_t AVALANCHE_MAX_ELEMENT_POLL_LEGACY
Legacy maximum element poll.
Definition processor.h:64
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:572
@ MSG_AVA_STAKE_CONTENDER
Definition protocol.h:580
@ MSG_AVA_PROOF
Definition protocol.h:579
@ MSG_BLOCK
Definition protocol.h:573
#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)
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)
bool operator()(const CBlockIndex *pindex) const LOCKS_EXCLUDED(cs_main)
ProofRegistrationState proofState GUARDED_BY(cs_proofState)
std::vector< std::pair< ProofId, CScript > > winners
Definition processor.h:261
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 AssertLockHeld(cs)
Definition sync.h:146
#define EXCLUSIVE_LOCKS_REQUIRED(...)
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
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.
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